Merge from Chromium at DEPS revision r215573

This commit was generated by merge_to_master.py.

Change-Id: Ib95814f98e5765b459dd32425f9bf9138edf2bca
diff --git a/chrome/VERSION b/chrome/VERSION
index 35d2d5e0..718de15 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=30
 MINOR=0
-BUILD=1582
+BUILD=1588
 PATCH=0
diff --git a/chrome/android/OWNERS b/chrome/android/OWNERS
index 1f9608a..a223621 100644
--- a/chrome/android/OWNERS
+++ b/chrome/android/OWNERS
@@ -1,5 +1,6 @@
 aruslan@chromium.org
 bulach@chromium.org
 dtrainor@chromium.org
+nyquist@chromium.org
 tedchoc@chromium.org
 yfriedman@chromium.org
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ForeignSessionHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ForeignSessionHelper.java
new file mode 100644
index 0000000..4faf66b
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ForeignSessionHelper.java
@@ -0,0 +1,212 @@
+// Copyright 2013 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.
+
+package org.chromium.chrome.browser;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.chrome.browser.profiles.Profile;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * This class exposes to Java information about sessions, windows, and tabs on the user's synced
+ * devices.
+ */
+public class ForeignSessionHelper {
+    private int mNativeForeignSessionHelper;
+
+    /**
+     * Callback interface for getting notified when foreign session sync is updated.
+     */
+    public interface ForeignSessionCallback {
+        /**
+         * This method will be called every time foreign session sync is updated.
+         *
+         * It's a good place to call {@link ForeignSessionHelper#getForeignSessions()} to get the
+         * updated information.
+         */
+        @CalledByNative("ForeignSessionCallback")
+        public void onUpdated();
+    }
+
+    /**
+     * Represents synced foreign session.
+     */
+    public static class ForeignSession {
+        public final String tag;
+        public final String name;
+        public final String deviceType;
+        public final long modifiedTime;
+        public final List<ForeignSessionWindow> windows = new ArrayList<ForeignSessionWindow>();
+
+        private ForeignSession(String tag, String name, String deviceType, long modifiedTime) {
+            this.tag = tag;
+            this.name = name;
+            this.deviceType = deviceType;
+            this.modifiedTime = modifiedTime;
+        }
+    }
+
+    /**
+     * Represents synced foreign window. Note that desktop Chrome can have multiple windows in a
+     * session.
+     */
+    public static class ForeignSessionWindow {
+        public final long timestamp;
+        public final int sessionId;
+        public final List<ForeignSessionTab> tabs = new ArrayList<ForeignSessionTab>();
+
+        private ForeignSessionWindow(long timestamp, int sessionId) {
+            this.timestamp = timestamp;
+            this.sessionId = sessionId;
+        }
+    }
+
+    /**
+     * Represents synced foreign tab.
+     */
+    public static class ForeignSessionTab {
+        public final String url;
+        public final String title;
+        public final long timestamp;
+        public final int id;
+
+        private ForeignSessionTab(String url, String title, long timestamp, int id) {
+            this.url = url;
+            this.title = title;
+            this.timestamp = timestamp;
+            this.id = id;
+        }
+    }
+
+    @CalledByNative
+    private static ForeignSession pushSession(
+            List<ForeignSession> sessions, String tag, String name, String deviceType,
+            long modifiedTime) {
+        ForeignSession session = new ForeignSession(tag, name, deviceType, modifiedTime);
+        sessions.add(session);
+        return session;
+    }
+
+    @CalledByNative
+    private static ForeignSessionWindow pushWindow(
+            ForeignSession session, long timestamp, int sessionId) {
+        ForeignSessionWindow window = new ForeignSessionWindow(timestamp, sessionId);
+        session.windows.add(window);
+        return window;
+    }
+
+    @CalledByNative
+    private static void pushTab(
+            ForeignSessionWindow window, String url, String title, long timestamp, int sessionId) {
+        ForeignSessionTab tab = new ForeignSessionTab(url, title, timestamp, sessionId);
+        window.tabs.add(tab);
+    }
+
+    /**
+     * Initialize this class with the given profile.
+     * @param profile Profile that will be used for syncing.
+     */
+    public ForeignSessionHelper(Profile profile) {
+        mNativeForeignSessionHelper = nativeInit(profile);
+    }
+
+    /**
+     * Clean up the C++ side of this class. After the call, this class instance shouldn't be used.
+     */
+    public void destroy() {
+        assert mNativeForeignSessionHelper != 0;
+        nativeDestroy(mNativeForeignSessionHelper);
+    }
+
+    @Override
+    protected void finalize() {
+        // Just to make sure that we called destroy() before the java garbage collection picks up.
+        assert mNativeForeignSessionHelper == 0;
+    }
+
+    /**
+     * @return {@code True} iff Tab sync is enabled.
+     */
+    public boolean isTabSyncEnabled() {
+        return nativeIsTabSyncEnabled(mNativeForeignSessionHelper);
+    }
+
+    /**
+     * Sets callback instance that will be called on every foreign session sync update.
+     */
+    public void setOnForeignSessionCallback(ForeignSessionCallback callback) {
+        nativeSetOnForeignSessionCallback(mNativeForeignSessionHelper, callback);
+    }
+
+    /**
+     * @return The list of synced foreign sessions. {@code null} iff it fails to get them for some
+     *         reason.
+     */
+    public List<ForeignSession> getForeignSessions() {
+        List<ForeignSession> result = new ArrayList<ForeignSession>();
+        boolean received = nativeGetForeignSessions(mNativeForeignSessionHelper, result);
+        if (received) {
+            // Sort sessions from most recent to least recent.
+            Collections.sort(result, new Comparator<ForeignSession>() {
+                @Override
+                public int compare(ForeignSession lhs, ForeignSession rhs) {
+                    return lhs.modifiedTime < rhs.modifiedTime ? 1 :
+                        (lhs.modifiedTime == rhs.modifiedTime ? 0: -1);
+                }
+            });
+        } else {
+            result = null;
+        }
+
+        return result;
+    }
+
+    /**
+     * Opens the given foreign tab in a new tab.
+     * @param session Session that the target tab belongs to.
+     * @param tab     Target tab to open.
+     * @return        {@code True} iff the tab is successfully opened.
+     */
+    public boolean openForeignSessionTab(ForeignSession session, ForeignSessionTab tab) {
+        return nativeOpenForeignSessionTab(mNativeForeignSessionHelper, session.tag, tab.id);
+    }
+
+    /**
+     * Set the given session collapsed or uncollapsed in preferences.
+     * @param session     Session to set collapsed or uncollapsed.
+     * @param isCollapsed {@code True} iff we want the session to be collapsed.
+     */
+    public void setForeignSessionCollapsed(ForeignSession session, boolean isCollapsed) {
+        nativeSetForeignSessionCollapsed(mNativeForeignSessionHelper, session.tag, isCollapsed);
+    }
+
+    /**
+     * Remove Foreign session to display. Note that it will be reappear on the next sync.
+     *
+     * This is mainly for when user wants to delete very old session that won't be used or syned in
+     * the future.
+     * @param session Session to be deleted.
+     */
+    public void deleteForeignSession(ForeignSession session) {
+        nativeDeleteForeignSession(mNativeForeignSessionHelper, session.tag);
+    }
+
+    private static native int nativeInit(Profile profile);
+    private static native void nativeDestroy(int nativeForeignSessionHelper);
+    private static native boolean nativeIsTabSyncEnabled(int nativeForeignSessionHelper);
+    private static native void nativeSetOnForeignSessionCallback(
+            int nativeForeignSessionHelper, ForeignSessionCallback callback);
+    private static native boolean nativeGetForeignSessions(int nativeForeignSessionHelper,
+            List<ForeignSession> resultSessions);
+    private static native boolean nativeOpenForeignSessionTab(
+            int nativeForeignSessionHelper, String sessionTag, int tabId);
+    private static native void nativeSetForeignSessionCollapsed(
+            int nativeForeignSessionHelper, String sessionTag, boolean isCollapsed);
+    private static native void nativeDeleteForeignSession(
+            int nativeForeignSessionHelper, String sessionTag);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
index e585d0b..53bf7de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
@@ -116,6 +116,10 @@
             return mCountry;
         }
 
+        public String getCountryCode() {
+            return nativeToCountryCode(mCountry);
+        }
+
         @CalledByNative("AutofillProfile")
         public String getPhoneNumber() {
             return mPhoneNumber;
@@ -382,4 +386,5 @@
     private native String nativeSetCreditCard(int nativePersonalDataManagerAndroid,
             CreditCard card);
     private native void nativeRemoveByGUID(int nativePersonalDataManagerAndroid, String guid);
+    private static native String nativeToCountryCode(String countryName);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPrerender.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPrerender.java
new file mode 100644
index 0000000..a99e765
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPrerender.java
@@ -0,0 +1,74 @@
+// Copyright 2013 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.
+
+package org.chromium.chrome.browser.omnibox;
+
+import org.chromium.chrome.browser.profiles.Profile;
+
+/**
+ * Java bridge to handle conditional prerendering using autocomplete results * as the user types
+ * into the Omnibox.
+ *
+ * OmniboxPrerender takes keystrokes, autocomplete results and navigation actions then feeds
+ * them to the (native) AutocompleteActionPredictor. The predictor uses this data to update its
+ * database and returns predictions on what page, if any, to pre-render or pre-connect.
+ *
+ */
+public class OmniboxPrerender {
+    private int mNativeOmniboxPrerender = 0;
+
+    /**
+     * Constructor for creating a OmniboxPrerender instanace.
+     */
+    public OmniboxPrerender() {
+        mNativeOmniboxPrerender = nativeInit();
+    }
+
+    /**
+     * Clears the transitional matches. This should be called when the user stops typing into
+     * the omnibox (e.g. when navigating away, closing the keyboard or changing tabs)
+     *
+     * @param profile profile instance corresponding to the active profile.
+     */
+    public void clear(Profile profile) {
+        nativeClear(mNativeOmniboxPrerender, profile);
+    }
+
+    /**
+     * Initializes the underlying action predictor for a given profile instance. This should be
+     * called as soon as possible as the predictor must register for certain notifications to
+     * properly initialize before providing predictions and updated its learning database.
+     *
+     * @param profile profile instance corresponding to active profile.
+     */
+    public void initializeForProfile(Profile profile) {
+        nativeInitializeForProfile(mNativeOmniboxPrerender, profile);
+    }
+
+    /**
+     * Potentailly invokes a pre-render or pre-connect given the url typed into the omnibox and
+     * a corresponding autocomplete result. This should be invoked everytime the omnibox changes
+     * (e.g. As the user types characters this method should be invoked at least once per character)
+     *
+     * @param url url in the omnibox.
+     * @param currentUrl url the current tab is displaying.
+     * @param nativeAutocompleteResult native pointer to an autocomplete result.
+     * @param profile profile instance corresponding to the active profile.
+     * @param nativeWebContents native pointer to a web contents instance.
+     */
+    public void prerenderMaybe(String url, String currentUrl, int nativeAutocompleteResult,
+            Profile profile, int nativeWebContents) {
+        nativePrerenderMaybe(mNativeOmniboxPrerender, url, currentUrl, nativeAutocompleteResult,
+                profile, nativeWebContents);
+    }
+
+    private native int nativeInit();
+    private native void nativeClear(int nativeOmniboxPrerender, Profile profile);
+    private native void nativeInitializeForProfile(
+            int nativeOmniboxPrerender,
+            Profile profile);
+    private native void nativePrerenderMaybe(int nativeOmniboxPrerender, String url,
+            String currentUrl, int nativeAutocompleteResult, Profile profile,
+            int nativeWebContents);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
index 3aed589..f539e1b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
@@ -120,6 +120,14 @@
         return nativeGetDefaultSearchProvider(mNativeTemplateUrlServiceAndroid);
     }
 
+    /**
+     * @return {@link TemplateUrlService.TemplateUrl} for the default search engine.
+     */
+    public TemplateUrl getDefaultSearchEngineTemplateUrl() {
+        return nativeGetPrepopulatedTemplateUrlAt(
+                mNativeTemplateUrlServiceAndroid, getSearchEngine());
+    }
+
     public void setSearchEngine(int selectedIndex) {
         ThreadUtils.assertOnUiThread();
         nativeSetDefaultSearchProvider(mNativeTemplateUrlServiceAndroid, selectedIndex);
@@ -130,6 +138,14 @@
     }
 
     /**
+     * @return Whether or not the default search engine has search by image support.
+     */
+    public boolean isSearchByImageAvailable() {
+        ThreadUtils.assertOnUiThread();
+        return nativeIsSearchByImageAvailable(mNativeTemplateUrlServiceAndroid);
+    }
+
+    /**
      * @return Whether the default configured search engine is for a Google property.
      */
     public boolean isDefaultSearchEngineGoogle() {
@@ -164,5 +180,6 @@
             int selectedIndex);
     private native int nativeGetDefaultSearchProvider(int nativeTemplateUrlServiceAndroid);
     private native boolean nativeIsSearchProviderManaged(int nativeTemplateUrlServiceAndroid);
+    private native boolean nativeIsSearchByImageAvailable(int nativeTemplateUrlServiceAndroid);
     private native boolean nativeIsDefaultSearchEngineGoogle(int nativeTemplateUrlServiceAndroid);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelper.java
index 0b2cc15..7e0582d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelper.java
@@ -13,9 +13,10 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.sync.signin.AccountManagerHelper;
 
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
 import javax.annotation.Nullable;
 
 /**
@@ -25,12 +26,15 @@
  * AccountManagerHelper and forwards callbacks to native code.
  * <p/>
  */
-public class AndroidProfileOAuth2TokenServiceHelper {
+public final class AndroidProfileOAuth2TokenServiceHelper {
 
     private static final String TAG = "AndroidProfileOAuth2TokenServiceHelper";
 
     private static final String OAUTH2_SCOPE_PREFIX = "oauth2:";
 
+    private AndroidProfileOAuth2TokenServiceHelper() {
+    }
+
     private static Account getAccountOrNullFromUsername(Context context, String username) {
         if (username == null) {
             Log.e(TAG, "Username is null");
@@ -106,21 +110,25 @@
             Context context, @Nullable Activity activity, Account account, String scope,
             long timeout, TimeUnit unit) {
         assert !ThreadUtils.runningOnUiThread();
-        final BlockingQueue<String> queue = new LinkedBlockingQueue<String>();
+        final AtomicReference<String> result = new AtomicReference<String>();
+        final Semaphore semaphore = new Semaphore(0);
         getOAuth2AccessToken(
                 context, activity, account, scope,
                 new AccountManagerHelper.GetAuthTokenCallback() {
                     @Override
                     public void tokenAvailable(String token) {
-                        try {
-                            queue.put(token);
-                        } catch (InterruptedException e) {
-                            Log.d(TAG, "Got interrupted while trying to offer token", e);
-                        }
+                        result.set(token);
+                        semaphore.release();
                     }
                 });
         try {
-            return queue.poll(timeout, unit);
+            if (semaphore.tryAcquire(timeout, unit)) {
+                return result.get();
+            } else {
+                Log.d(TAG, "Failed to retrieve auth token within timeout (" +
+                        timeout + " + " + unit.name() + ")");
+                return null;
+            }
         } catch (InterruptedException e) {
             Log.w(TAG, "Got interrupted while waiting for auth token");
             return null;
diff --git a/chrome/android/javatests/DEPS b/chrome/android/javatests/DEPS
index 0d019e1..136005d 100644
--- a/chrome/android/javatests/DEPS
+++ b/chrome/android/javatests/DEPS
@@ -1,3 +1,7 @@
 include_rules = [
   "+content/public/android/java",
+  "+sync/android/java/src/org/chromium/sync/signin",
+  # We should only depend on the util package of something that lives in
+  # javatests.
+  "+sync/test/android/javatests/src/org/chromium/sync/test/util",
 ]
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
index 374be84..b7817c6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
@@ -55,7 +55,7 @@
         AutofillProfile storedProfile = mHelper.getProfile(profileOneGUID);
         assertEquals(profileOneGUID, storedProfile.getGUID());
         assertEquals("https://www.example.com", storedProfile.getOrigin());
-        assertEquals("Canada", storedProfile.getCountry());
+        assertEquals("CA", storedProfile.getCountryCode());
         assertEquals("San Francisco", storedProfile.getCity());
         assertNotNull(mHelper.getProfile(profileTwoGUID));
     }
@@ -117,4 +117,31 @@
         mHelper.deleteCreditCard(cardOneGUID);
         assertEquals(0, mHelper.getNumberOfCreditCards());
     }
+
+    @SmallTest
+    @Feature({"Autofill"})
+    public void testRespectCountryCodes() throws InterruptedException, ExecutionException {
+        // The constructor should accept country names and ISO 3166-1-alpha-2 country codes.
+        // getCountryCode() should return a country code.
+        AutofillProfile profile1 = new AutofillProfile(
+                "" /* guid */, "https://www.example.com" /* origin */,
+                "John Smith", "Acme Inc.", "1 Main", "Apt A", "Montreal", "Quebec",
+                "H3B 2Y5", "Canada", "514-670-1234", "john@acme.inc");
+        String profileGuid1 = mHelper.setProfile(profile1);
+
+        AutofillProfile profile2 = new AutofillProfile(
+                "" /* guid */, "https://www.example.com" /* origin */,
+                "Greg Smith", "Ucme Inc.", "123 Bush", "Apt 125", "Montreal", "Quebec",
+                "H3B 2Y5", "CA", "514-670-4321", "greg@ucme.inc");
+        String profileGuid2 = mHelper.setProfile(profile2);
+
+        assertEquals(2, mHelper.getNumberOfProfiles());
+
+        AutofillProfile storedProfile1 = mHelper.getProfile(profileGuid1);
+        assertEquals("CA", storedProfile1.getCountryCode());
+
+        AutofillProfile storedProfile2 = mHelper.getProfile(profileGuid2);
+        assertEquals("CA", storedProfile2.getCountryCode());
+    }
+
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelperTest.java
new file mode 100644
index 0000000..ef9c5c9
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelperTest.java
@@ -0,0 +1,68 @@
+// Copyright 2013 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.
+
+package org.chromium.chrome.browser.signin;
+
+import android.accounts.Account;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.base.test.util.AdvancedMockContext;
+import org.chromium.base.test.util.Feature;
+import org.chromium.sync.signin.AccountManagerHelper;
+import org.chromium.sync.test.util.AccountHolder;
+import org.chromium.sync.test.util.MockAccountManager;
+
+import java.util.concurrent.TimeUnit;
+
+public class AndroidProfileOAuth2TokenServiceHelperTest extends InstrumentationTestCase {
+
+    private AdvancedMockContext mContext;
+    private MockAccountManager mAccountManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Mock out the account manager on the device.
+        mContext = new AdvancedMockContext(getInstrumentation().getTargetContext());
+        mAccountManager = new MockAccountManager(mContext, getInstrumentation().getContext());
+        AccountManagerHelper.overrideAccountManagerHelperForTests(mContext, mAccountManager);
+    }
+
+    @SmallTest
+    @Feature({"Sync"})
+    public void testGetOAuth2AccessTokenWithTimeoutOnSuccess() {
+        String authToken = "someToken";
+        // Auth token should be successfully received.
+        runTestOfGetOAuth2AccessTokenWithTimeout(authToken);
+    }
+
+    @SmallTest
+    @Feature({"Sync"})
+    public void testGetOAuth2AccessTokenWithTimeoutOnError() {
+        String authToken = null;
+        // Should not crash when auth token is null.
+        runTestOfGetOAuth2AccessTokenWithTimeout(authToken);
+    }
+
+    private void runTestOfGetOAuth2AccessTokenWithTimeout(String expectedToken) {
+        String scope = "http://example.com/scope";
+        Account account = AccountManagerHelper.createAccountFromName("test@gmail.com");
+        String oauth2Scope = "oauth2:" + scope;
+
+        // Add an account with given auth token for the given scope, already accepted auth popup.
+        AccountHolder accountHolder =
+                AccountHolder.create()
+                        .account(account)
+                        .hasBeenAccepted(oauth2Scope, true)
+                        .authToken(oauth2Scope, expectedToken).build();
+        mAccountManager.addAccountHolderExplicitly(accountHolder);
+
+        String accessToken =
+                AndroidProfileOAuth2TokenServiceHelper.getOAuth2AccessTokenWithTimeout(
+                        mContext, null, account, scope, 5, TimeUnit.SECONDS);
+        assertEquals(expectedToken, accessToken);
+    }
+}
diff --git a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java
index 98df0c1..b53c80d 100644
--- a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java
+++ b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/ChromiumTestShellActivity.java
@@ -16,7 +16,7 @@
 import org.chromium.chrome.browser.DevToolsServer;
 import org.chromium.content.browser.ActivityContentVideoViewClient;
 import org.chromium.content.browser.AndroidBrowserProcess;
-import org.chromium.content.browser.ContentVideoView;
+import org.chromium.content.browser.BrowserStartupConfig;
 import org.chromium.content.browser.ContentVideoViewClient;
 import org.chromium.content.browser.ContentView;
 import org.chromium.content.browser.ContentViewClient;
@@ -51,19 +51,39 @@
     private DevToolsServer mDevToolsServer;
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
+    protected void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         if (!CommandLine.isInitialized()) CommandLine.initFromFile(COMMAND_LINE_FILE);
         waitForDebuggerIfNeeded();
 
         DeviceUtils.addDeviceSpecificUserAgentSwitch(this);
+
+        BrowserStartupConfig.setAsync(new BrowserStartupConfig.StartupCallback() {
+            @Override
+            public void run(int startupResult) {
+                if (startupResult > 0) {
+                    // TODO: Show error message.
+                    Log.e(TAG, "Chromium browser process initialization failed");
+                    finish();
+                } else {
+                    finishInitialization(savedInstanceState);
+                }
+            }
+        });
+
         try {
-            AndroidBrowserProcess.init(this, AndroidBrowserProcess.MAX_RENDERERS_LIMIT);
+            if (!AndroidBrowserProcess.init(this, AndroidBrowserProcess.MAX_RENDERERS_LIMIT)) {
+                // Process was already running, finish initialization now.
+                finishInitialization(savedInstanceState);
+            }
         } catch (ProcessInitException e) {
             Log.e(TAG, "Chromium browser process initialization failed", e);
             finish();
         }
+    }
+
+    private void finishInitialization(final Bundle savedInstanceState) {
         setContentView(R.layout.testshell_activity);
         mTabManager = (TabManager) findViewById(R.id.tab_manager);
         String startupUrl = getUrlFromIntent(getIntent());
@@ -161,6 +181,7 @@
 
     /**
      * Creates a {@link TestShellTab} with a URL specified by {@code url}.
+     *
      * @param url The URL the new {@link TestShellTab} should start with.
      */
     public void createTab(String url) {
diff --git a/chrome/android/testshell/testshell_tab.cc b/chrome/android/testshell/testshell_tab.cc
index ac47441..92a35c2 100644
--- a/chrome/android/testshell/testshell_tab.cc
+++ b/chrome/android/testshell/testshell_tab.cc
@@ -131,9 +131,9 @@
   return reinterpret_cast<jint>(tab);
 }
 
-int64 TestShellTab::GetSyncId() const {
+int TestShellTab::GetSyncId() const {
   NOTIMPLEMENTED();
   return 0;
 }
 
-void TestShellTab::SetSyncId(int64 sync_id) { NOTIMPLEMENTED(); }
+void TestShellTab::SetSyncId(int sync_id) { NOTIMPLEMENTED(); }
diff --git a/chrome/android/testshell/testshell_tab.h b/chrome/android/testshell/testshell_tab.h
index 8a374b6..0f79de4 100644
--- a/chrome/android/testshell/testshell_tab.h
+++ b/chrome/android/testshell/testshell_tab.h
@@ -68,8 +68,8 @@
 
   virtual void RunExternalProtocolDialog(const GURL& url) OVERRIDE;
 
-  virtual int64 GetSyncId() const OVERRIDE;
-  virtual void SetSyncId(int64 sync_id) OVERRIDE;
+  virtual int GetSyncId() const OVERRIDE;
+  virtual void SetSyncId(int sync_id) OVERRIDE;
 
   // Register the Tab's native methods through JNI.
   static bool RegisterTestShellTab(JNIEnv* env);
diff --git a/chrome/app/breakpad_linux.cc b/chrome/app/breakpad_linux.cc
index 8c71bcd..cf9d0c7 100644
--- a/chrome/app/breakpad_linux.cc
+++ b/chrome/app/breakpad_linux.cc
@@ -49,7 +49,6 @@
 
 #include "base/android/build_info.h"
 #include "base/android/path_utils.h"
-#include "chrome/common/descriptors_android.h"
 #endif
 #include "third_party/lss/linux_syscall_support.h"
 
@@ -1703,7 +1702,7 @@
     // generated as the renderer and browser run with different UIDs
     // (preventing the browser from inspecting the renderer process).
     int minidump_fd = base::GlobalDescriptors::GetInstance()->
-        MaybeGet(kAndroidMinidumpDescriptor);
+        MaybeGet(breakpad::GetBreakpadClient()->GetAndroidMinidumpDescriptor());
     if (minidump_fd == base::kInvalidPlatformFileValue) {
       NOTREACHED() << "Could not find minidump FD, crash reporting disabled.";
     } else {
diff --git a/chrome/app/breakpad_mac.mm b/chrome/app/breakpad_mac.mm
index aeebd7f..ae8cc01 100644
--- a/chrome/app/breakpad_mac.mm
+++ b/chrome/app/breakpad_mac.mm
@@ -26,8 +26,6 @@
 #include "chrome/common/child_process_logging.h"
 #include "content/public/common/content_switches.h"
 #include "components/breakpad/breakpad_client.h"
-#include "components/nacl/common/nacl_switches.h"
-#include "native_client/src/trusted/service_runtime/osx/crash_filter.h"
 #include "policy/policy_constants.h"
 
 namespace {
@@ -98,15 +96,6 @@
   return false;
 }
 
-#if !defined(DISABLE_NACL)
-bool NaClBreakpadCrashFilter(int exception_type,
-                             int exception_code,
-                             mach_port_t crashing_thread,
-                             void* context) {
-  return !NaClMachThreadIsInUntrusted(crashing_thread);
-}
-#endif
-
 // BreakpadGenerateAndSendReport() does not report the current
 // thread.  This class can be used to spin up a thread to run it.
 class DumpHelper : public base::PlatformThread::Delegate {
@@ -291,11 +280,7 @@
     process_type = base::SysUTF8ToNSString(process_type_switch);
   }
 
-#if !defined(DISABLE_NACL)
-  if (process_type_switch == switches::kNaClLoaderProcess) {
-    BreakpadSetFilterCallback(gBreakpadRef, NaClBreakpadCrashFilter, NULL);
-  }
-#endif
+  breakpad::GetBreakpadClient()->InstallAdditionalFilters(gBreakpadRef);
 
   // Store process type in crash dump.
   SetCrashKeyValue(@"ptype", process_type);
diff --git a/chrome/app/breakpad_win.cc b/chrome/app/breakpad_win.cc
index c618085..6e58e15 100644
--- a/chrome/app/breakpad_win.cc
+++ b/chrome/app/breakpad_win.cc
@@ -31,9 +31,9 @@
 #include "chrome/app/breakpad_field_trial_win.h"
 #include "chrome/app/hard_error_handler_win.h"
 #include "chrome/common/child_process_logging.h"
-#include "chrome/common/chrome_result_codes.h"
 #include "components/breakpad/breakpad_client.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/result_codes.h"
 #include "policy/policy_constants.h"
 #include "sandbox/win/src/nt_internals.h"
 #include "sandbox/win/src/sidestep/preamble_patcher.h"
@@ -781,8 +781,9 @@
     *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags));
   } __except(EXCEPTION_EXECUTE_HANDLER) {
     // Its not safe to continue executing, exit silently here.
-    ::TerminateProcess(::GetCurrentProcess(),
-                       chrome::RESULT_CODE_RESPAWN_FAILED);
+    ::TerminateProcess(
+        ::GetCurrentProcess(),
+        breakpad::GetBreakpadClient()->GetResultCodeRespawnFailed());
   }
 
   return true;
diff --git a/chrome/app/chrome_breakpad_client.cc b/chrome/app/chrome_breakpad_client.cc
index 4d0a58d..0bbac69 100644
--- a/chrome/app/chrome_breakpad_client.cc
+++ b/chrome/app/chrome_breakpad_client.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_result_codes.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/crash_keys.h"
 #include "chrome/common/env_vars.h"
@@ -38,6 +39,10 @@
 #include "chrome/installer/util/google_update_settings.h"
 #endif
 
+#if defined(OS_ANDROID)
+#include "chrome/common/descriptors_android.h"
+#endif
+
 namespace chrome {
 
 namespace {
@@ -177,6 +182,10 @@
     return true;
   return false;
 }
+
+int ChromeBreakpadClient::GetResultCodeRespawnFailed() {
+  return chrome::RESULT_CODE_RESPAWN_FAILED;
+}
 #endif
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
@@ -239,4 +248,10 @@
 }
 #endif
 
+#if defined(OS_ANDROID)
+int ChromeBreakpadClient::GetAndroidMinidumpDescriptor() {
+  return kAndroidMinidumpDescriptor;
+}
+#endif
+
 }  // namespace chrome
diff --git a/chrome/app/chrome_breakpad_client.h b/chrome/app/chrome_breakpad_client.h
index b5fa854..8b14f09 100644
--- a/chrome/app/chrome_breakpad_client.h
+++ b/chrome/app/chrome_breakpad_client.h
@@ -33,6 +33,7 @@
   virtual bool GetDeferredUploadsSupported(bool is_per_user_install) OVERRIDE;
   virtual bool GetIsPerUserInstall(const base::FilePath& exe_path) OVERRIDE;
   virtual bool GetShouldDumpLargerDumps(bool is_per_user_install) OVERRIDE;
+  virtual int GetResultCodeRespawnFailed() OVERRIDE;
 #endif
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
@@ -55,6 +56,14 @@
   virtual bool GetCollectStatsConsent() OVERRIDE;
 #endif
 
+#if defined(OS_ANDROID)
+  virtual int GetAndroidMinidumpDescriptor() OVERRIDE;
+#endif
+
+#if defined(OS_MACOSX)
+  virtual void InstallAdditionalFilters(BreakpadRef breakpad) OVERRIDE;
+#endif
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ChromeBreakpadClient);
 };
diff --git a/chrome/app/chrome_breakpad_client_mac.mm b/chrome/app/chrome_breakpad_client_mac.mm
new file mode 100644
index 0000000..0e842c1
--- /dev/null
+++ b/chrome/app/chrome_breakpad_client_mac.mm
@@ -0,0 +1,40 @@
+// Copyright 2013 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.
+
+#include "chrome/app/chrome_breakpad_client.h"
+
+#include "base/command_line.h"
+#include "chrome/common/chrome_switches.h"
+
+#if !defined(DISABLE_NACL)
+#import "breakpad/src/client/mac/Framework/Breakpad.h"
+#include "components/nacl/common/nacl_switches.h"
+#include "native_client/src/trusted/service_runtime/osx/crash_filter.h"
+#endif
+
+namespace chrome {
+
+namespace {
+
+#if !defined(DISABLE_NACL)
+bool NaClBreakpadCrashFilter(int exception_type,
+                             int exception_code,
+                             mach_port_t crashing_thread,
+                             void* context) {
+  return !NaClMachThreadIsInUntrusted(crashing_thread);
+}
+#endif
+
+}  // namespace
+
+void ChromeBreakpadClient::InstallAdditionalFilters(BreakpadRef breakpad) {
+#if !defined(DISABLE_NACL)
+  if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kProcessType) == switches::kNaClLoaderProcess) {
+    BreakpadSetFilterCallback(breakpad, NaClBreakpadCrashFilter, NULL);
+  }
+#endif
+}
+
+}  // namespace chrome
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 894956e..b354f61 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -262,6 +262,7 @@
 #define IDC_CONTENT_CONTEXT_COPYIMAGELOCATION 50111
 #define IDC_CONTENT_CONTEXT_COPYIMAGE 50112
 #define IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB 50113
+#define IDC_CONTENT_CONTEXT_SEARCHIMAGENEWTAB 50114
 // Audio/video items.
 #define IDC_CONTENT_CONTEXT_SAVEAVAS 50120
 #define IDC_CONTENT_CONTEXT_COPYAVLOCATION 50121
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index dd1c680..7086e8b 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -607,12 +607,12 @@
   <message name="IDS_USER_IS_LOCALLY_MANAGED_BY_NOTICE" desc="Text for notifications showing that this user is locally managed">
     This supervised user is managed by <ph name="USER_DISPLAY_NAME">$1<ex>John</ex></ph>. Usage and browsing history can be viewed by the manager.
   </message>
-  <message name="IDS_USER_IS_LOCALLY_MANAGED_REMOVE_WARNING" desc="Text shown as a warning when attempting to remove supervised user.">
+  <message name="IDS_LOGIN_POD_USER_REMOVE_WARNING" desc="Text shown as a warning when attempting to remove supervised user.">
     All files and local data associated with the supervised user will be permanently
     deleted once this supervised user is removed. Visited websites and settings
     for this supervised user may still be visible by the manager at <ph name="MANAGEMENT_URL">$1<ex>www.example.com</ex></ph>.
   </message>
-  <message name="IDS_USER_IS_LOCALLY_MANAGED_REMOVE_WARNING_BUTTON" desc="Text shown on a button that confirms removal of supervised user.">
+  <message name="IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON" desc="Text shown on a button that confirms removal of supervised user.">
     Remove this user
   </message>
   <message name="IDS_OFFLINE_LOGIN_HTML" desc="Text which suggests enter as an existing user when valid network isn't presented.">
@@ -3717,7 +3717,7 @@
   <message name="IDS_LOGIN_PUBLIC_ACCOUNT_SIGNOUT_REMINDER" desc="Text shown in the public account user pod, reminding the user to log out.">
     Your information will be removed when you sign out.
   </message>
-  <message name="IDS_LOGIN_PUBLIC_ACCOUNT_ENTER" desc="Label text for the sign-in button in the public account user pod.">
+  <message name="IDS_LOGIN_PUBLIC_ACCOUNT_ENTER" meaning="Label text for signing in." desc="Label text for the sign-in button in the public account user pod.">
     Enter
   </message>
   <message name="IDS_LOGIN_PUBLIC_ACCOUNT_ENTER_ACCESSIBLE_NAME" desc="Text to be spoken when focus is set to the sign-in button in the public account user pod.">
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 4416ca6..8dacb67 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -649,6 +649,16 @@
         Adding to Chromium...
       </message>
 
+      <message name="IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS"
+               desc="Message shown on the download shelf when the download is known to change browser settings.">
+        <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> may change your Chromium settings.
+      </message>
+
+      <message name="IDS_PROMPT_DOWNLOAD_CHANGES_SEARCH_SETTINGS"
+               desc="Message shown on the download shelf when the download is known to change search engine settings in the browser.">
+        <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> may change how your searches work in Chromium.
+      </message>
+
       <!-- Remove in-progress downloads confirmation dialog -->
       <message name="IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_EXPLANATION" desc="Explanation of the dialog asking for user confirmation to close the browser when one download is in progress.">
         A download is currently in progress. Do you want to exit Chromium and cancel the download?
@@ -1005,6 +1015,23 @@
           You can see all your notifications from Chromium apps, extensions, and websites here.
         </message>
       </if>
+      <message name="IDS_MESSAGE_CENTER_TOOLTIP" desc="Tooltip for notification tray icon without unread notifications">
+        Chromium - Notifications
+      </message>
+      <message name="IDS_MESSAGE_CENTER_TOOLTIP_UNREAD" desc="Tooltip for notification tray icon with unread notifications">
+        Chromium - Notifications (<ph name="QUANTITY">$1<ex>3</ex></ph> unread)
+      </message>
+
+      <!-- MediaStream capture status tray icon -->
+      <message name="IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_AND_VIDEO" desc="Tool tip for the capture status tray icon when microphone and camera are being used">
+        Chromium is using your camera and microphone.
+      </message>
+      <message name="IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_ONLY" desc="Tool tip for the capture status tray icon when microphone is being used">
+        Chromium is using your microphone.
+      </message>
+      <message name="IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_VIDEO_ONLY" desc="Tool tip for the capture status tray icon when camera is being used">
+        Chromium is using your camera.
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index da3cfd8..c74632b 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -359,7 +359,7 @@
         Media
       </message>
       <message name="IDS_WEBSITE_SETTINGS_TYPE_MIDI_SYSEX" desc="The label used for MIDI system exclusive message permission controls in the Website Settings popup.">
-        MIDI
+        MIDI full control
       </message>
       <message name="IDS_WEBSITE_SETTINGS_SHOW_SITE_DATA" desc="The label of the Show Cookies and Site Data link in the site data section of the Website Settings popup.">
         Show cookies and site data
@@ -787,6 +787,9 @@
         <message name="IDS_CONTENT_CONTEXT_SEARCHWEBFOR" desc="The name of the Search the Web for 'string' command in the content area context menu">
           &amp;Search <ph name="SEARCH_ENGINE">$1<ex>Google</ex></ph> for '<ph name="SEARCH_TERMS">$2<ex>flowers</ex></ph>'
         </message>
+        <message name="IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE" desc="The name of the Search For Image command in the content area context menu">
+          &amp;Search <ph name="SEARCH_ENGINE">$1<ex>Google</ex></ph> for this image
+        </message>
         <message name="IDS_CONTENT_CONTEXT_GOTOURL" desc="The name of the Go to 'url' command in the content area context menu">
           &amp;Go to <ph name="URL">$1<ex>http://www.google.com/</ex></ph>
         </message>
@@ -996,6 +999,9 @@
         <message name="IDS_CONTENT_CONTEXT_SEARCHWEBFOR" desc="In Title Case: The name of the Search the Web for 'string' command in the content area context menu">
           &amp;Search <ph name="SEARCH_ENGINE">$1<ex>Google</ex></ph> for '<ph name="SEARCH_TERMS">$2<ex>flowers</ex></ph>'
         </message>
+        <message name="IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE" desc="In Title Case: The name of the Search For Image command in the content area context menu">
+          &amp;Search <ph name="SEARCH_ENGINE">$1<ex>Google</ex></ph> for this image
+        </message>
         <message name="IDS_CONTENT_CONTEXT_GOTOURL" desc="In Title Case: The name of the Go to url for 'string' command in the content area context menu">
           &amp;Go to <ph name="URL">$1<ex>http://www.google.com/</ex></ph>
         </message>
@@ -2893,10 +2899,6 @@
          desc="Message shown to the user to validate the download when the download content is classified as uncommon by safebrowsing.">
         <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> is not commonly downloaded and could be dangerous.
       </message>
-      <message name="IDS_PROMPT_POTENTIALLY_UNWANTED_DOWNLOAD"
-         desc="Message shown on the download shelf when the download is known to modify the browser or computer settings.">
-        <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> might modify your browser or computer settings.
-      </message>
       <message name="IDS_CONFIRM_KEEP_DANGEROUS_DOWNLOAD_TITLE"
                desc="Title for the confirmation dialog asking whether the user really meant to keep a dangerous download">
         Confirm Download
@@ -2997,6 +2999,10 @@
                desc="Open downloads folder link">
         Open downloads folder
       </message>
+      <message name="IDS_DOWNLOAD_BY_EXTENSION"
+               desc="Indicates which extension downloaded this file.">
+        Downloaded by <ph name="EXTENSION">$1<ex>The Best Chrome Extension Ever</ex></ph>
+      </message>
 
       <!-- Download Context Menu Items -->
       <if expr="not pp_ifdef('use_titlecase')">
@@ -5516,9 +5522,6 @@
       <message name="IDS_EXTENSIONS_NONE_INSTALLED" desc="Text that lets the user know that no extensions are installed.">
         Boo... You have no extensions :-(
       </message>
-      <message name="IDS_APPS_DEVTOOL_TITLE" desc="Title for the apps devtool app.">
-        Apps Developer Tool
-      </message>
       <message name="IDS_EXTENSIONS_PERMISSIONS_HEADING" desc="Heading for permissions dialog in apps_devtools">
         It can:
       </message>
@@ -5532,22 +5535,28 @@
         Pack...
       </message>
       <message name="IDS_APPS_DEVTOOL_UPDATE_BUTTON" desc="Text for the 'Update app/extensions' button.">
-        Update now
+        Update
       </message>
       <message name="IDS_APPS_DEVTOOL_SEARCH" desc="Placeholder text that appears inside the search box until the user inputs data.">
         Search
       </message>
-      <message name="IDS_APPS_DEVTOOL_NO_APPS_INSTALLED" desc="Text that lets the user know that no apps are installed.">
-        Boo... You have no apps :-(
-      </message>
       <message name="IDS_APPS_DEVTOOL_APPS_INSTALLED" desc="Text for the word 'Apps'.">
         Apps
       </message>
       <message name="IDS_APPS_DEVTOOL_EXTENSIONS_INSTALLED" desc="Text for the word 'Extensions'.">
         Extensions
       </message>
-      <message name="IDS_APPS_DEVTOOL_NO_UNPACKED_INSTALLED" desc="Text that lets the user know that no unpacked apps or extensions are installed.">
-        No unpacked applications or extensions.
+      <message name="IDS_APPS_DEVTOOL_NO_PACKED_APPS" desc="Text that lets the user know that no packed apps are installed.">
+        No installed applications.
+      </message>
+      <message name="IDS_APPS_DEVTOOL_NO_UNPACKED_APPS" desc="Text that lets the user know that no unpacked apps are installed.">
+        No unpacked applications.
+      </message>
+      <message name="IDS_APPS_DEVTOOL_NO_PACKED_EXTENSIONS" desc="Text that lets the user know that no packed extensions are installed.">
+        No installed extensions.
+      </message>
+      <message name="IDS_APPS_DEVTOOL_NO_UNPACKED_EXTENSIONS" desc="Text that lets the user know that no unpacked extensions are installed.">
+        No unpacked extensions.
       </message>
       <message name="IDS_APPS_DEVTOOL_UNPACKED_INSTALLED" desc="Text for the word 'Unpacked' (unpacked app or extension).">
         Unpacked
@@ -5555,6 +5564,21 @@
       <message name="IDS_APPS_DEVTOOL_INSTALLED" desc="Text for the word 'Installed' (installed app or extension).">
         Installed
       </message>
+      <message name="IDS_APPS_DEVTOOL_UPDATING" desc="Text for the word 'Updating...' when the user clicks on 'Update'.">
+        Updating...
+      </message>
+      <message name="IDS_APPS_DEVTOOL_DELETE_CONFIRMATION_BUTTON" desc="Text of  the delete button in the apps/extension delete confirmation dialog.">
+        Delete
+      </message>
+      <message name="IDS_APPS_DEVTOOL_DELETE_CONFIRMATION_TITLE" desc="Text of the title of the apps/extension delete confirmation dialog.">
+        Delete
+      </message>
+      <message name="IDS_APPS_DEVTOOL_DELETE_CONFIRMATION_MESSAGE_APP" desc="Text of the message of the apps/extension delete confirmation dialog in case the user is deleting an app.">
+        Do you really want to delete this application?
+      </message>
+      <message name="IDS_APPS_DEVTOOL_DELETE_CONFIRMATION_MESSAGE_EXTENSION" desc="Text of the message of the apps/extension delete confirmation dialog in case the user is deleting an extension.">
+        Do you really want to delete this extension?
+      </message>
       <message name="IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY" desc="Text on next line after IDS_EXTENSIONS_NONE_INSTALLED that suggests the user look in the gallery for extensions to install.">
         Want to <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>browse the gallery<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph> instead?
       </message>
@@ -5676,6 +5700,12 @@
       <message name="IDS_EXTENSIONS_LOCKED_MANAGED_USER" desc="The error message (either shown in the extensions UI or logged) informing a supervised user that extensions cannot be changed.">
         Extensions cannot be modified by supervised users.
       </message>
+      <message name="IDS_EXTENSIONS_USE_APPS_DEV_TOOLS" desc="The message in the banner to invite people to use Apps Developer Tools.">
+        The new Apps Developer Tools makes it easier than ever to manage and debug your apps and extensions. It's also been added to the application list.
+      </message>
+      <message name="IDS_EXTENSIONS_OPEN_APPS_DEV_TOOLS" desc="The text of the button to open Apps Developer Tools in the banner to invite people to use Apps Developer Tools.">
+        Open
+      </message>
       <message name="IDS_GET_MORE_EXTENSIONS" desc="The text for getting more extensions. Displayed at bottom of extension management page when there is at least one extension installed.">
         Get more extensions
       </message>
@@ -6244,6 +6274,9 @@
 
       <if expr="is_android">
         <message name="IDS_POLICY_MANAGED_BOOKMARKS" desc="Mobile: name of the managed bookmarks folder">
+          <ph name="DOMAIN">$1<ex>google.com</ex></ph> bookmarks
+        </message>
+        <message name="IDS_POLICY_MANAGED_BOOKMARKS_DEFAULT_NAME" desc="Mobile: name of the managed bookmarks folder when the management domain can't be determined.">
           Managed bookmarks
         </message>
       </if>
@@ -6711,6 +6744,12 @@
       <message name="IDS_FLAGS_OVERSCROLL_HISTORY_NAVIGATION_DESCRIPTION" desc="Description for the flag to disable history navigation from horizontal overscroll.">
         Experimental history navigation in response to horizontal overscroll.
       </message>
+      <message name="IDS_FLAGS_OVERVIEW_MODE_NAME" desc="Title for the flag to enable window overview mode.">
+        Enable overview mode.
+      </message>
+      <message name="IDS_FLAGS_OVERVIEW_MODE_DESCRIPTION" desc="Description for the flag to enable window overview mode.">
+        Enable overview mode, activated by pushing the switch window button.
+      </message>
       <message name="IDS_FLAGS_SCROLL_END_EFFECT_NAME" desc="Title for the flag for scroll end effect from vertical overscroll.">
         Scroll end effect
       </message>
@@ -6811,11 +6850,11 @@
       <message name="IDS_FLAGS_ENABLE_SCTP_DATA_CHANNELS_DESCRIPTION" desc="Description of about:flags option to turn on SCTP data channels support">
         When enabled, DataChannels created by WebRTC will use the SCTP wire protocol.
       </message>
-      <message name="IDS_FLAGS_ENABLE_DEVICE_ENUMERATION_NAME" desc="Name of about:flags option to turn on WebRTC device enumeration support.">
-        Enable WebRTC device enumeration.
+      <message name="IDS_FLAGS_DISABLE_DEVICE_ENUMERATION_NAME" desc="Name of about:flags option to turn off WebRTC device enumeration support.">
+        Disable WebRTC device enumeration.
       </message>
-      <message name="IDS_FLAGS_ENABLE_DEVICE_ENUMERATION_DESCRIPTION" desc="Description of chrome:flags option to turn on WebRTC device enumeration support">
-        When enabled, MediaStreamTrack can list the available video and audio input devices, and getUserMedia can open these input devices by referencing an opaque id.
+      <message name="IDS_FLAGS_DISABLE_DEVICE_ENUMERATION_DESCRIPTION" desc="Description of chrome:flags option to turn off WebRTC device enumeration support">
+        Disable support for MediaStreamTrack.getSources().
       </message>
       <message name="IDS_FLAGS_DISABLE_WEBAUDIO_NAME" desc="Name of the 'Disable WebAudio' lab.">
         Disable WebAudio
@@ -6995,12 +7034,6 @@
       <message name="IDS_FLAGS_CHROMEOS_USE_NEW_NETWORK_CONNECTION_HANDLER_DESCRIPTION" desc="Description for the flag to enable using the new network connection handler.">
         Enable the new network connection handler.
       </message>
-      <message name="IDS_FLAGS_ASH_ENABLE_NEW_AUDIO_HANDLER_NAME" desc="Title for the flag to enable using the new audio handler.">
-        Enables new audio handler
-      </message>
-      <message name="IDS_FLAGS_ASH_ENABLE_NEW_AUDIO_HANDLER_DESCRIPTION" desc="Description for the flag to enable using the new audio handler.">
-        Enable the new audio handler which uses the new cras audio dbus apis.
-      </message>
       <message name="IDS_FLAGS_ASH_AUDIO_DEVICE_MENU_NAME" desc="Title for a flag for audio switching.">
         Audio input/output menu
       </message>
@@ -7465,6 +7498,14 @@
       <message name="IDS_FLAGS_ENABLE_DEVICE_MOTION_DESCRIPTION" desc="Description for the flag to enable device motion.">
         Enables device motion DOM events in JavaScript.
       </message>
+      <if expr="is_android">
+        <message name="IDS_FLAGS_ENABLE_CAST_NAME" desc="Name of the flag to enable Google Cast support.">
+          Enable Google Cast support
+        </message>
+        <message name="IDS_FLAGS_ENABLE_CAST_DESCRIPTION" desc="Description for the flag to enable playing videos remotely on Google Cast receivers.">
+          Enable playing videos remotely on Google Cast receivers.
+        </message>
+      </if>
 
       <!-- Crashes -->
       <message name="IDS_CRASHES_TITLE" desc="Title for the chrome://crashes page.">
@@ -8026,7 +8067,7 @@
         New tab
       </message>
       <message name="IDS_TOOLTIP_MIC_SEARCH" desc="The tooltip for search-by-voice button">
-        Voice search
+        Search by voice
       </message>
       <message name="IDS_TOOLTIP_ZOOM" desc="The tooltip for zoom icon">
         Zoom: <ph name="VALUE">$1<ex>100</ex></ph>%
@@ -8332,56 +8373,27 @@
         Skip Import
       </message>
 
-      <!-- Report Bug or Broken Website Dialog -->
-      <message name="IDS_FEEDBACK_TITLE" desc="Dialog title for bug report dialog">
-        Whoops. Let's fix that.
-      </message>
+      <!-- Feedback Dialog -->
       <message name="IDS_FEEDBACK_REPORT_PAGE_TITLE" desc="Label showing the title of the page that will be reported">
-        Feedback
+        Tell us what is happening.
       </message>
       <message name="IDS_FEEDBACK_REPORT_URL_LABEL" desc="Label showing the URL that will be reported">
-        Include this URL:
+        URL (optional):
       </message>
       <message name="IDS_FEEDBACK_USER_EMAIL_LABEL" desc="Label showing the e-mail address that will be reported">
-        Include this email:
+        Email (optional):
       </message>
-      <message name="IDS_FEEDBACK_DESCRIPTION_LABEL" desc="Label for the description field">
-        Tell us what is happening. (required)
-      </message>
-      <if expr="pp_ifdef('chromeos')">
-      <message name="IDS_FEEDBACK_SCREENSHOT_LABEL" desc="Label for the screenshot field if current screenshots are being shown">
-        Include the current screenshot:
-      </message>
-      </if>
-      <if expr="not pp_ifdef('chromeos')">
       <message name="IDS_FEEDBACK_SCREENSHOT_LABEL" desc="Label for the screenshot field if current screenshots are being shown">
         Include this screenshot:
       </message>
-      </if>
-      <message name="IDS_FEEDBACK_SAVED_SCREENSHOT_LABEL" desc="Label for the screenshot field if saved screenshots are being shown">
-        Include a saved screenshot:
-      </message>
-      <message name="IDS_FEEDBACK_CURRENT_SCREENSHOTS" desc="Radio button for including the last screen image on the bug report dialog box">
-        Send a current page screen shot
-      </message>
-      <message name="IDS_FEEDBACK_SAVED_SCREENSHOTS" desc="Radio button for including the last screen image on the bug report dialog box">
-        Send a saved screen shot
-      </message>
       <message name="IDS_FEEDBACK_INCLUDE_SYSTEM_INFORMATION_CHKBOX" desc="Checkbox for including system information on the bug report dialog box">
         Send system information
       </message>
-      <message name="IDS_FEEDBACK_CHOOSE_DIFFERENT_SCREENSHOT" desc="Text for changing the screenshot selection from current to saved">
-        (Choose a different screenshot)
-      </message>
-      <message name="IDS_FEEDBACK_CHOOSE_ORIGINAL_SCREENSHOT" desc="Text for changing the screenshot selection from saved to current">
-        (Go back to original screenshot)
-      </message>
       <message name="IDS_FEEDBACK_ATTACH_FILE_NOTE" desc="Text for describing the maximum size for an attached file">
-        The attached file is uploaded to Google servers for debugging.&lt;br&gt;
-        Note: You can attach one file per feedback report (3 MB limit)
+        The attached file is uploaded to Google servers for debugging.
       </message>
       <message name="IDS_FEEDBACK_ATTACH_FILE_LABEL" desc="Text for the label for the attached filename">
-        Include this file:
+        Attach file:
       </message>
       <message name="IDS_FEEDBACK_READING_FILE" desc="Text to display if reading a file when the user clicks the send report button">
         Reading file..
@@ -8399,18 +8411,9 @@
         that you provide to improve any Google product or service.
         <ph name="END_BOLD">&lt;/strong&gt;</ph>
       </message>
-      <message name="IDS_FEEDBACK_INCLUDE_NEW_SCREEN_IMAGE" desc="Radio button for including a new screen image on the bug report dialog box">
-        Send last active tab screen shot
-      </message>
-      <message name="IDS_FEEDBACK_INCLUDE_NO_SCREENSHOT" desc="Radio button for not including a screen image on the bug report dialog box">
-        Do not send a screen shot
-      </message>
       <message name="IDS_FEEDBACK_NO_DESCRIPTION" desc="Message shown when no text is entered before hitting send feedback">
         Please tell us what is happening before sending the feedback.
       </message>
-      <message name="IDS_FEEDBACK_NO_SAVED_SCREENSHOTS_HELP" desc="Message shown when no screenshots are available for selection">
-        You can select saved screenshots from here. Currently there are no screenshots available. You can press Ctrl + the "Window Switch" key together to take a screenshot. The last three screenshots that you have taken will appear here.
-      </message>
       <message name="IDS_FEEDBACK_SEND_REPORT" desc="Text for OK button of the send feedback dialog">
         Send feedback
       </message>
@@ -10058,6 +10061,9 @@
       <message name="IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT" desc="Text for the prompt shown when screen capturing is requrested by an app.">
         Do you want <ph name="APP_NAME">$1<ex>html5rocks.com</ex></ph> to share your screen?
       </message>
+      <message name="IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT" desc="Text for the prompt shown when screen and audio capturing is requrested by an app.">
+        Do you want <ph name="APP_NAME">$1<ex>html5rocks.com</ex></ph> to share your screen and audio output?
+      </message>
       <message name="IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TITLE" desc="Text shown on the button that stops screen capture.">
         Screen capture - <ph name="APP_NAME">$1<ex>Sharing App</ex></ph>
       </message>
@@ -12590,18 +12596,6 @@
       <message name="IDS_FLAGS_FILE_MANAGER_SHOW_CHECKBOXES_DESCRIPTION" desc="Description of the about:flag option to show the selecting checkboxes to the Files app.">
         Show selecting checkboxes in the Files.app.
       </message>
-      <message name="IDS_FLAGS_FILE_MANAGER_ENABLE_SHARING_NAME" desc="Name of the about:flag option to hide the selecting checkboxes to in the Files app.">
-        Sharing in Files.app.
-      </message>
-      <message name="IDS_FLAGS_FILE_MANAGER_ENABLE_SHARING_DESCRIPTION" desc="Description of the about:flag option to show the sharing option in Files app.">
-        Enables the sharing dialog in Files app under the context menu.
-      </message>
-      <message name="IDS_FLAGS_FILE_MANAGER_ENABLE_FOLDER_SHORTCUTS" desc="Name of the about:flag option to enable the folder-shortcuts feature in the Files app.">
-        Enable folder shortcuts in Files.app.
-      </message>
-      <message name="IDS_FLAGS_FILE_MANAGER_ENABLE_FOLDER_SHORTCUTS_DESCRIPTION" desc="Description of the about:flag option of folder shortcuts feature in Files app.">
-        Enables the folder shortcuts feature in Files.app, which allows you to create shortcuts to folders in the left panel.
-      </message>
       <message name="IDS_FLAGS_FILE_MANAGER_ENABLE_WEBSTORE_INTEGRATION" desc="Name of the about:flag option to enable the webstore integration feature in Files app.">
         Enable the integration of Webstore and Files.app.
       </message>
@@ -13151,6 +13145,9 @@
       <message name="IDS_FILE_BROWSER_OK_LABEL" desc="OK label.">
         OK
       </message>
+      <message name="IDS_FILE_BROWSER_UPLOAD_LABEL" desc="Upload label.">
+        Upload
+      </message>
       <message name="IDS_FILE_BROWSER_SEARCH_TEXT_LABEL" desc="Search text field label.">
         Search
       </message>
@@ -13185,6 +13182,9 @@
       <message name="IDS_FILE_BROWSER_SELECT_FOLDER_TITLE" desc="Select folder title.">
         Select a folder to open
       </message>
+      <message name="IDS_FILE_BROWSER_SELECT_UPLOAD_FOLDER_TITLE" desc="Select folder title for upload.">
+        Select a folder to upload
+      </message>
       <message name="IDS_FILE_BROWSER_SELECT_OPEN_FILE_TITLE" desc="Select open file title.">
         Select a file to open
       </message>
@@ -13529,6 +13529,9 @@
       <message name="IDS_FILE_BROWSER_THUMBNAIL_VIEW_TOOLTIP" desc="Tooltip for the Thumbnail View button.">
         Thumbnail view
       </message>
+      <message name="IDS_FILE_BROWSER_GEAR_BUTTON_TOOLTIP" desc="Tooltip for the gear button in Files.app.">
+        Settings
+      </message>
 
       <message name="IDS_FILE_BROWSER_TIME_TODAY" desc="File date and time in case the date is today.">
         Today <ph name="TODAY_DAYTIME">$1<ex>6:19 AM</ex></ph>
@@ -14640,6 +14643,33 @@
       This page has been blocked from tracking your location.
     </message>
 
+    <!-- Web MIDI messages -->
+    <message name="IDS_MIDI_SYSEX_TAB_LABEL" desc="Label for MIDI system exclusive message on Content Settings dialog">
+      MIDI full control
+    </message>
+    <message name="IDS_MIDI_SYSEX_ALLOW_RADIO" desc="A radio button in Content Settings dialog to allow a site to access MIDI devices with system exclusive messages.">
+      Allow all sites to use system exclusive messages to access MIDI devices
+    </message>
+    <message name="IDS_MIDI_SYSEX_BLOCK_RADIO" desc="A radio button in Content Settings dialog to deny a site to access MIDI devices with system exclusive messages.">
+      Do not allow any sites to use system exclusive messages to access MIDI devices
+    </message>
+    <message name="IDS_MIDI_SYSEX_ASK_RADIO" desc="A radio button in Content Settings dialog to allow a site to query the permision to access MIDI devices with system exclusive messages.">
+      Ask me when a site wants to use system exclusive messages to access MIDI devices (recommended)
+    </message>
+
+    <!-- Web MIDI messages -->
+    <message name="IDS_MIDI_SYSEX_INFOBAR_QUESTION" desc="Question asked on the info bar whenever URL wants to access MIDI devices with system exclusive messages.">
+      <ph name="URL">
+        $1<ex>www.google.com</ex>
+      </ph> wants to get full control of your MIDI devices.
+    </message>
+    <message name="IDS_MIDI_SYSEX_ALLOW_BUTTON" desc="A button in MIDI access infobar for allowing full access to MIDI devices for a given domain.">
+      Allow
+    </message>
+    <message name="IDS_MIDI_SYSEX_DENY_BUTTON" desc="A button in MIDI access infobar for denying full access to MIDI devices for a given domain.">
+      Deny
+    </message>
+
     <message name="IDS_REGISTER_PROTOCOL_HANDLER_TOOLTIP" desc="Location bar icon tooltip text when a page wants to use registerProtocolHandler.">
       This page wants to install a service handler.
     </message>
@@ -15090,17 +15120,6 @@
       Deny
     </message>
 
-    <!-- MediaStream capture status tray icon -->
-    <message name="IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_AND_VIDEO" desc="Tool tip for the capture status tray icon when microphone and camera are being used">
-      <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> is using your camera and microphone.
-    </message>
-    <message name="IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_ONLY" desc="Tool tip for the capture status tray icon when microphone is being used">
-      <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> is using your microphone.
-    </message>
-    <message name="IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_VIDEO_ONLY" desc="Tool tip for the capture status tray icon when camera is being used">
-      <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> is using your camera.
-    </message>
-
     <!-- Quota messages -->
     <if expr="is_android">
       <message name="IDS_REQUEST_QUOTA_INFOBAR_QUESTION" desc="Mobile: For Android device. Question asked on the info bar whenever webapp requests new (larger) quota to persistently store data on the device (e.g. for persistent-type filesystem).">
@@ -15249,6 +15268,9 @@
         <message name="IDS_LOGIN_BUTTON">
           Sign In
         </message>
+        <message name="IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON" desc="Login screen user pod menu item text.">
+          Remove This User
+        </message>
       </if>
       <if expr="not pp_ifdef('use_titlecase')">
         <message name="IDS_ADD_USER_BUTTON" desc="Text shown on an add user button on login/locker screen">
@@ -15260,6 +15282,9 @@
         <message name="IDS_LOGIN_BUTTON">
           Sign in
         </message>
+        <message name="IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON" desc="Login screen user pod menu item text.">
+          Remove this user
+        </message>
       </if>
       <message name="IDS_GO_INCOGNITO_BUTTON" desc="Text shown on Chrome OS login screen button that launches guest session. Should be short.">
         Browse as Guest
@@ -15279,8 +15304,8 @@
       <message name="IDS_LOGIN_POD_MENU_REMOVE_ITEM_ACCESSIBLE_NAME" desc="Text to be spoken when 'Remove user' item is selected on the pod menu.">
         Remove this user
       </message>
-      <message name="IDS_LOGIN_POD_REMOVE_USER" desc="Login screen user pod menu item text.">
-        Remove this user
+      <message name="IDS_LOGIN_POD_USER_REMOVE_WARNING" desc="Text shown as a warning when attempting to remove an user.">
+        Are you sure you want to delete this user and all the data associated with it from this computer? This cannot be undone!
       </message>
     </if>
 
@@ -15716,25 +15741,25 @@
 
     <!-- Reset Profile Settings strings -->
     <message name="IDS_RESET_PROFILE_SETTINGS_SECTION_TITLE" desc="The title of the section in chrome://settings that allows resetting some settings in a profile">
-      Reset
+      Reset browser settings
     </message>
     <message name="IDS_RESET_PROFILE_SETTINGS_DESCRIPTION" desc="A label describing the purpose of the 'reset profile settings' feature">
-      Things got out of control? Revert your Chrome profile to a clean, post install state.
+      Restore browser settings to their original defaults.
     </message>
     <message name="IDS_RESET_PROFILE_SETTINGS_EXPLANATION" desc="A label describing the consequences of the 'reset profile settings' feature">
-      Your browser settings will be restored to their original defaults. This will reset your homepage, new tab page, and search engine, disable your extensions and clear your cookies, content settings and site data.
+      Your browser settings will be restored to their original defaults. This will reset your homepage, new tab page and search engine, disable your extensions and unpin all tabs. It will also clear other temporary and cached data, such as cookies, content and site data.
     </message>
     <message name="IDS_RESET_PROFILE_SETTINGS_BUTTON" desc="The text on the button in chrome://settings that allows resetting some settings in a profile">
-      Reset profile settings...
+      Reset browser settings
     </message>
     <message name="IDS_RESET_PROFILE_SETTINGS_TITLE" desc="Dialog title">
-      Reset profile settings
+      Reset browser settings
     </message>
     <message name="IDS_RESET_PROFILE_SETTINGS_COMMIT_BUTTON" desc="Reset button of the Reset Profile Settings dialog">
       Reset
     </message>
     <message name="IDS_RESET_PROFILE_SETTINGS_FEEDBACK" desc="Feedback label in the Reset Profile Settings dialog">
-      Help protect Chrome users by sharing current settings with Google
+      Help make Google Chrome better by reporting the current settings
     </message>
 
     <!-- Identity internals strings -->
@@ -15791,6 +15816,17 @@
       Google+
     </message>
 
+    <!-- Desktop media picker UI for Desktop Capture API -->
+    <message name="IDS_DESKTOP_MEDIA_PICKER_TITLE" desc="Title for the window picker dialog shown when desktop capture is requrested by an app.">
+      Desktop sharing - <ph name="APP_NAME">$1<ex>Google Hangouts</ex></ph>
+    </message>
+    <message name="IDS_DESKTOP_MEDIA_PICKER_TEXT" desc="Text for the window picker dialog shown when desktop capture is requrested by an app.">
+      <ph name="APP_NAME">$1<ex>Google Hangouts</ex></ph> requested to share content of your desktop. Please select a window or the whole screen to share.
+    </message>
+    <message name="IDS_DESKTOP_MEDIA_PICKER_SCREEN_NAME" desc="Name for screens in the desktop media picker UI.">
+      Screen
+    </message>
+
     </messages>
 
     <structures fallback_to_english="true">
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index b9db382..663bc97 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -573,6 +573,16 @@
         Adding to Chrome...
       </message>
 
+      <message name="IDS_PROMPT_DOWNLOAD_CHANGES_SETTINGS"
+               desc="Message shown on the download shelf when the download is known to change browser settings.">
+        <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> may change your Chrome settings.
+      </message>
+
+      <message name="IDS_PROMPT_DOWNLOAD_CHANGES_SEARCH_SETTINGS"
+               desc="Message shown on the download shelf when the download is known to change search engine settings in the browser.">
+        <ph name="FILE_NAME">$1<ex>bla.exe</ex></ph> may change how your searches work in Chrome.
+      </message>
+
       <!-- Remove in-progress downloads confirmation dialog -->
       <message name="IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_EXPLANATION" desc="Explanation of the dialog asking for user confirmation to close the browser when one download is in progress.">
         A download is currently in progress. Do you want to exit Google Chrome and cancel the download?
@@ -930,6 +940,23 @@
           You can see all your notifications from Chrome apps, extensions, and websites here.
         </message>
       </if>
+      <message name="IDS_MESSAGE_CENTER_TOOLTIP" desc="Tooltip for notification tray icon without unread notifications">
+        Chrome - Notifications
+      </message>
+      <message name="IDS_MESSAGE_CENTER_TOOLTIP_UNREAD" desc="Tooltip for notification tray icon with unread notifications">
+        Chrome - Notifications (<ph name="QUANTITY">$1<ex>3</ex></ph> unread)
+      </message>
+
+      <!-- MediaStream capture status tray icon -->
+      <message name="IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_AND_VIDEO" desc="Tool tip for the capture status tray icon when microphone and camera are being used">
+        Google Chrome is using your camera and microphone.
+      </message>
+      <message name="IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_ONLY" desc="Tool tip for the capture status tray icon when microphone is being used">
+        Google Chrome is using your microphone.
+      </message>
+      <message name="IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_VIDEO_ONLY" desc="Tool tip for the capture status tray icon when camera is being used">
+        Google Chrome is using your camera.
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/app/hard_error_handler_win.cc b/chrome/app/hard_error_handler_win.cc
index 6f56666..92c168a 100644
--- a/chrome/app/hard_error_handler_win.cc
+++ b/chrome/app/hard_error_handler_win.cc
@@ -14,7 +14,7 @@
 
 #include "base/basictypes.h"
 #include "base/strings/string_util.h"
-#include "chrome/common/env_vars.h"
+#include "components/breakpad/breakpad_client.h"
 
 namespace {
 const DWORD kExceptionModuleNotFound = VcppException(ERROR_SEVERITY_ERROR,
@@ -37,7 +37,7 @@
 void RaiseHardErrorMsg(long nt_status, const std::string& p1,
                                        const std::string& p2) {
   // If headless just exit silently.
-  if (::GetEnvironmentVariableA(env_vars::kHeadless, NULL, 0))
+  if (breakpad::GetBreakpadClient()->IsRunningUnattended())
     return;
 
   HMODULE ntdll = ::GetModuleHandleA("NTDLL.DLL");
diff --git a/chrome/app/policy/policy_templates.json b/chrome/app/policy/policy_templates.json
index 3a32920..aeb4d5c 100644
--- a/chrome/app/policy/policy_templates.json
+++ b/chrome/app/policy/policy_templates.json
@@ -112,7 +112,7 @@
 #   persistent IDs for all fields (but not for groups!) are needed. These are
 #   specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs,
 #   because doing so would break the deployed wire format!
-#   For your editing convenience: highest ID currently used: 226
+#   For your editing convenience: highest ID currently used: 234
 #
 # Placeholders:
 #   The following placeholder strings are automatically substituted:
@@ -588,6 +588,22 @@
 
           If this setting is disabled or not set, then both local and remote users can interact with the host when it is being shared.''',
         },
+        {
+          'name': 'RemoteAccessHostAllowClientPairing',
+          'type': 'main',
+          'schema': { 'type': 'boolean' },
+          'supported_on': ['chrome.*:30-'],
+          'features': {
+            'dynamic_refresh': True,
+            'per_profile': False,
+          },
+          'example_value': False,
+          'id': 234,
+          'caption': '''Enable or disable PIN-less authentication''',
+          'desc': '''If this setting is enabled or not configured, then users can opt to pair clients and hosts at connection time, eliminating the need to enter a PIN every time.
+
+          If this setting is disabled, then this feature will not be available.''',
+        },
       ],
     },
     {
@@ -1848,6 +1864,96 @@
 
           This policy is only respected if the 'DefaultSearchProviderEnabled' policy is enabled.''',
         },
+        {
+          'name': 'DefaultSearchProviderImageURL',
+          'type': 'string',
+          'schema': { 'type': 'string' },
+          'supported_on': ['chrome.*:29-', 'chrome_os:0.29-', 'android:30-'],
+          'features': {
+            'dynamic_refresh': True,
+            'per_profile': True,
+          },
+          'example_value': 'http://search.my.company/searchbyimage/upload',
+          'id': 229,
+          'caption': '''Parameter providing search-by-image feature for the default search provider''',
+          'desc': '''Specifies the URL of the search engine used to provide image search. Search requests will be sent using the GET method. If the DefaultSearchProviderImageURLPostParams policy is set then image search requests will use the POST method instead.
+
+          This policy is optional. If not set, no image search will be used.
+
+          This policy is only respected if the 'DefaultSearchProviderEnabled' policy is enabled.''',
+        },
+        {
+          'name': 'DefaultSearchProviderSearchURLPostParams',
+          'type': 'string',
+          'schema': { 'type': 'string' },
+          'supported_on': ['chrome.*:29-', 'chrome_os:0.29-', 'android:30-'],
+          'features': {
+            'dynamic_refresh': True,
+            'per_profile': True,
+          },
+          'example_value': 'q={searchTerms},ie=utf-8,oe=utf-8',
+          'id': 230,
+          'caption': '''Parameters for search URL which uses POST''',
+          'desc': '''Specifies the parameters used when searching a URL with POST. It consists of comma-separated name/value pairs. If a value is a template parameter, like {searchTerms} in above example, it will be replaced with real search terms data.
+
+          This policy is optional. If not set, search request will be sent using the GET method.
+
+          This policy is only respected if the 'DefaultSearchProviderEnabled' policy is enabled.''',
+        },
+        {
+          'name': 'DefaultSearchProviderSuggestURLPostParams',
+          'type': 'string',
+          'schema': { 'type': 'string' },
+          'supported_on': ['chrome.*:29-', 'chrome_os:0.29-', 'android:30-'],
+          'features': {
+            'dynamic_refresh': True,
+            'per_profile': True,
+          },
+          'example_value': 'q={searchTerms},ie=utf-8,oe=utf-8',
+          'id': 231,
+          'caption': '''Parameters for suggest URL which uses POST''',
+          'desc': '''Specifies the parameters used when doing suggestion search with POST. It consists of comma-separated name/value pairs. If a value is a template parameter, like {searchTerms} in above example, it will be replaced with real search terms data.
+
+          This policy is optional. If not set, suggest search request will be sent using the GET method.
+
+          This policy is only respected if the 'DefaultSearchProviderEnabled' policy is enabled.''',
+        },
+        {
+          'name': 'DefaultSearchProviderInstantURLPostParams',
+          'type': 'string',
+          'schema': { 'type': 'string' },
+          'supported_on': ['chrome.*:29-', 'chrome_os:0.29-', 'android:30-'],
+          'features': {
+            'dynamic_refresh': True,
+            'per_profile': True,
+          },
+          'example_value': 'q={searchTerms},ie=utf-8,oe=utf-8',
+          'id': 232,
+          'caption': '''Parameters for instant URL which uses POST''',
+          'desc': '''Specifies the parameters used when doing instant search with POST. It consists of comma-separated name/value pairs. If a value is a template parameter, like {searchTerms} in above example, it will be replaced with real search terms data.
+
+          This policy is optional. If not set, instant search request will be sent using the GET method.
+
+          This policy is only respected if the 'DefaultSearchProviderEnabled' policy is enabled.''',
+        },
+        {
+          'name': 'DefaultSearchProviderImageURLPostParams',
+          'type': 'string',
+          'schema': { 'type': 'string' },
+          'supported_on': ['chrome.*:29-', 'chrome_os:0.29-', 'android:30-'],
+          'features': {
+            'dynamic_refresh': True,
+            'per_profile': True,
+          },
+          'example_value': 'content={imageThumbnail},url={imageURL},sbisrc={SearchSource}',
+          'id': 233,
+          'caption': '''Parameters for image URL which uses POST''',
+          'desc': '''Specifies the parameters used when doing image search with POST. It consists of comma-separated name/value pairs. If a value is a template parameter, like {imageThumbnail} in above example, it will be replaced with real image thumbnail data.
+
+          This policy is optional. If not set, image search request will be sent using the GET method.
+
+          This policy is only respected if the 'DefaultSearchProviderEnabled' policy is enabled.''',
+        },
       ],
     },
     {
@@ -2436,6 +2542,24 @@
       Leaving this policy not set will make <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> use the default value of 3 hours.''',
     },
     {
+      'name': 'MaxInvalidationFetchDelay',
+      'type': 'int',
+      'schema': { 'type': 'integer' },
+      'supported_on': ['chrome.*:30-', 'chrome_os:0.30-'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'example_value': 10000,
+      'id': 228,
+      'caption': '''Maximum fetch delay after a policy invalidation''',
+      'desc': '''Specifies the maximum delay in milliseconds between receiving a policy invalidation and fetching the new policy from the device management service.
+
+      Setting this policy overrides the default value of 5000 milliseconds. Valid values for this policy are in the range from 1000 (1 second) to 300000 (5 minutes). Any values not in this range will be clamped to the respective boundary.
+
+      Leaving this policy not set will make <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> use the default value of 5000 milliseconds.''',
+    },
+    {
       'name': 'ChromeFrameRendererSettings',
       'type': 'group',
       'caption': '''Default HTML renderer for <ph name="PRODUCT_FRAME_NAME">$3<ex>Google Chrome Frame</ex></ph>''',
diff --git a/chrome/app/theme/default_100_percent/common/notification_tray_attention.png b/chrome/app/theme/default_100_percent/common/notification_tray_attention.png
index 1603015..d6a5ebd 100644
--- a/chrome/app/theme/default_100_percent/common/notification_tray_attention.png
+++ b/chrome/app/theme/default_100_percent/common/notification_tray_attention.png
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/notification_tray_do_not_disturb_attention.png b/chrome/app/theme/default_100_percent/common/notification_tray_do_not_disturb_attention.png
new file mode 100644
index 0000000..c4b8667
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/common/notification_tray_do_not_disturb_attention.png
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/notification_tray_do_not_disturb_empty.png b/chrome/app/theme/default_100_percent/common/notification_tray_do_not_disturb_empty.png
new file mode 100644
index 0000000..218f98a
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/common/notification_tray_do_not_disturb_empty.png
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/notification_tray_empty.png b/chrome/app/theme/default_100_percent/common/notification_tray_empty.png
index b00a187..60f3637 100644
--- a/chrome/app/theme/default_100_percent/common/notification_tray_empty.png
+++ b/chrome/app/theme/default_100_percent/common/notification_tray_empty.png
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/wallet_logo.png b/chrome/app/theme/default_100_percent/common/wallet_logo.png
new file mode 100644
index 0000000..fde6e62
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/common/wallet_logo.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/notification_tray_attention.png b/chrome/app/theme/default_200_percent/common/notification_tray_attention.png
index f60193b..ac31141 100644
--- a/chrome/app/theme/default_200_percent/common/notification_tray_attention.png
+++ b/chrome/app/theme/default_200_percent/common/notification_tray_attention.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/notification_tray_do_not_disturb_attention.png b/chrome/app/theme/default_200_percent/common/notification_tray_do_not_disturb_attention.png
new file mode 100644
index 0000000..4a89b15
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/common/notification_tray_do_not_disturb_attention.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/notification_tray_do_not_disturb_empty.png b/chrome/app/theme/default_200_percent/common/notification_tray_do_not_disturb_empty.png
new file mode 100644
index 0000000..f74f5cc
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/common/notification_tray_do_not_disturb_empty.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/notification_tray_empty.png b/chrome/app/theme/default_200_percent/common/notification_tray_empty.png
index 346e004..1ad4009 100644
--- a/chrome/app/theme/default_200_percent/common/notification_tray_empty.png
+++ b/chrome/app/theme/default_200_percent/common/notification_tray_empty.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/wallet_logo.png b/chrome/app/theme/default_200_percent/common/wallet_logo.png
new file mode 100644
index 0000000..a0e6c4d
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/common/wallet_logo.png
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index a2ff249..1dbbf35 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -513,8 +513,10 @@
         <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_PERIPHERAL_BATTERY_LOW" file="cros/notification_peripheral_battery_low.png" />
       </if>
       <if expr="is_win or is_linux">
-        <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_TRAY_EMPTY" file="common/notification_tray_empty.png" />
+        <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_TRAY_DO_NOT_DISTURB_ATTENTION" file="common/notification_tray_do_not_disturb_attention.png" />
+        <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_TRAY_DO_NOT_DISTURB_EMPTY" file="common/notification_tray_do_not_disturb_empty.png" />
         <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_TRAY_ATTENTION" file="common/notification_tray_attention.png" />
+        <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_TRAY_EMPTY" file="common/notification_tray_empty.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_OMNIBOX_BORDER_BOTTOM" file="common/omnibox_border_bottom.png" />
       <structure type="chrome_scaled_image" name="IDR_OMNIBOX_BORDER_BOTTOM_LEFT" file="common/omnibox_border_bottom_left.png" />
@@ -963,6 +965,7 @@
         <structure type="chrome_scaled_image" name="IDR_USER_IMAGE_RECYCLE" file="discard_wide.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_WALLET_ICON" file="wallet.png" />
+      <structure type="chrome_scaled_image" name="IDR_WALLET_LOGO" file="common/wallet_logo.png" />
       <structure type="chrome_scaled_image" name="IDR_WALLET_STEP_CHECK" file="common/payment_checkmark.png" />
       <structure type="chrome_scaled_image" name="IDR_WARNING" file="alert_small.png" />
       <structure type="chrome_scaled_image" name="IDR_WEB_INTENT_PROGRESS_BACKGROUND" file="common/web_intent_progress_background.png" />
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index ca42870..a59a81a 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -82,6 +82,7 @@
   "+third_party/leveldatabase",
   "+third_party/libevent",  # For the remote V8 debugging server
   "+third_party/libjingle",
+  "+third_party/libyuv",
   "+third_party/protobuf/src/google/protobuf",
   "+third_party/re2",
   "+third_party/sqlite",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index be61ca9..ef2a1cc 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -418,26 +418,11 @@
     SINGLE_VALUE_TYPE(switches::kDisableGpuVsync)
   },
   {
-    "enable-webgl",
-    IDS_FLAGS_ENABLE_WEBGL_NAME,
-    IDS_FLAGS_ENABLE_WEBGL_DESCRIPTION,
-    kOsAndroid,
-#if defined(OS_ANDROID)
-    SINGLE_VALUE_TYPE(switches::kEnableExperimentalWebGL)
-#else
-    SINGLE_VALUE_TYPE("")
-#endif
-  },
-  {
     "disable-webgl",
     IDS_FLAGS_DISABLE_WEBGL_NAME,
     IDS_FLAGS_DISABLE_WEBGL_DESCRIPTION,
-    kOsDesktop,
-#if defined(OS_ANDROID)
-    SINGLE_VALUE_TYPE("")
-#else
+    kOsAll,
     SINGLE_VALUE_TYPE(switches::kDisableExperimentalWebGL)
-#endif
   },
   {
     "disable-webrtc",
@@ -459,11 +444,11 @@
     SINGLE_VALUE_TYPE(switches::kEnableSCTPDataChannels)
   },
   {
-    "enable-device-enumeration",
-    IDS_FLAGS_ENABLE_DEVICE_ENUMERATION_NAME,
-    IDS_FLAGS_ENABLE_DEVICE_ENUMERATION_DESCRIPTION,
+    "disable-device-enumeration",
+    IDS_FLAGS_DISABLE_DEVICE_ENUMERATION_NAME,
+    IDS_FLAGS_DISABLE_DEVICE_ENUMERATION_DESCRIPTION,
     kOsAll,
-    SINGLE_VALUE_TYPE(switches::kEnableDeviceEnumeration)
+    SINGLE_VALUE_TYPE(switches::kDisableDeviceEnumeration)
   },
 #endif
 #if defined(OS_ANDROID)
@@ -539,7 +524,7 @@
     IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_NAME,
     IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_DESCRIPTION,
     kOsDesktop,
-    SINGLE_VALUE_TYPE(switches::kEnableExperimentalExtensionApis)
+    SINGLE_VALUE_TYPE(extensions::switches::kEnableExperimentalExtensionApis)
   },
   {
     "extensions-on-chrome-urls",
@@ -1042,6 +1027,13 @@
     SINGLE_VALUE_TYPE(switches::kDisableMinimizeOnSecondLauncherItemClick)
   },
   {
+    "enable-overview-mode",
+    IDS_FLAGS_OVERVIEW_MODE_NAME,
+    IDS_FLAGS_OVERVIEW_MODE_DESCRIPTION,
+    kOsCrOS,
+    SINGLE_VALUE_TYPE(ash::switches::kAshEnableOverviewMode)
+  },
+  {
     "show-touch-hud",
     IDS_FLAGS_SHOW_TOUCH_HUD_NAME,
     IDS_FLAGS_SHOW_TOUCH_HUD_DESCRIPTION,
@@ -1100,20 +1092,6 @@
     SINGLE_VALUE_TYPE(chromeos::switches::kFileManagerShowCheckboxes)
   },
   {
-    "file-manager-enable-sharing",
-    IDS_FLAGS_FILE_MANAGER_ENABLE_SHARING_NAME,
-    IDS_FLAGS_FILE_MANAGER_ENABLE_SHARING_DESCRIPTION,
-    kOsCrOS,
-    SINGLE_VALUE_TYPE(chromeos::switches::kFileManagerEnableSharing)
-  },
-  {
-    "file-manager-enable-folder-shortcuts",
-    IDS_FLAGS_FILE_MANAGER_ENABLE_FOLDER_SHORTCUTS,
-    IDS_FLAGS_FILE_MANAGER_ENABLE_FOLDER_SHORTCUTS_DESCRIPTION,
-    kOsCrOS,
-    SINGLE_VALUE_TYPE(chromeos::switches::kFileManagerEnableFolderShortcuts)
-  },
-  {
     "file-manager-enable-webstore-integration",
     IDS_FLAGS_FILE_MANAGER_ENABLE_WEBSTORE_INTEGRATION,
     IDS_FLAGS_FILE_MANAGER_ENABLE_WEBSTORE_INTEGRATION_DESCRIPTION,
@@ -1215,14 +1193,6 @@
     SINGLE_VALUE_TYPE(chromeos::switches::kUseNewNetworkConnectionHandler),
   },
   {
-    "ash-enable-new-audio-handler2",
-    IDS_FLAGS_ASH_ENABLE_NEW_AUDIO_HANDLER_NAME,
-    IDS_FLAGS_ASH_ENABLE_NEW_AUDIO_HANDLER_DESCRIPTION,
-    kOsCrOS,
-    ENABLE_DISABLE_VALUE_TYPE(ash::switches::kAshEnableNewAudioHandler,
-                              ash::switches::kAshDisableNewAudioHandler)
-  },
-  {
     "ash-audio-device-menu",
     IDS_FLAGS_ASH_AUDIO_DEVICE_MENU_NAME,
     IDS_FLAGS_ASH_AUDIO_DEVICE_MENU_DESCRIPTION,
@@ -1300,7 +1270,7 @@
     "enable-interactive-autocomplete",
     IDS_FLAGS_ENABLE_INTERACTIVE_AUTOCOMPLETE_NAME,
     IDS_FLAGS_ENABLE_INTERACTIVE_AUTOCOMPLETE_DESCRIPTION,
-    kOsWin | kOsCrOS | kOsAndroid | kOsMac,
+    kOsWin | kOsCrOS | kOsMac,
     ENABLE_DISABLE_VALUE_TYPE(
         autofill::switches::kEnableInteractiveAutocomplete,
         autofill::switches::kDisableInteractiveAutocomplete)
@@ -1603,7 +1573,8 @@
     IDS_FLAGS_ENABLE_STICKY_KEYS_NAME,
     IDS_FLAGS_ENABLE_STICKY_KEYS_DESCRIPTION,
     kOsCrOS,
-    SINGLE_VALUE_TYPE(switches::kEnableStickyKeys),
+    ENABLE_DISABLE_VALUE_TYPE(switches::kEnableStickyKeys,
+                              switches::kDisableStickyKeys)
   },
 #endif
   {
@@ -1650,6 +1621,15 @@
     kOsAndroid,
     SINGLE_VALUE_TYPE(switches::kEnableDeviceMotion)
   },
+#if defined(OS_ANDROID)
+  {
+    "enable-cast",
+    IDS_FLAGS_ENABLE_CAST_NAME,
+    IDS_FLAGS_ENABLE_CAST_DESCRIPTION,
+    kOsAndroid,
+    SINGLE_VALUE_TYPE(switches::kEnableCast)
+  },
+#endif
 };
 
 const Experiment* experiments = kExperiments;
diff --git a/chrome/browser/accessibility/accessibility_extension_apitest.cc b/chrome/browser/accessibility/accessibility_extension_apitest.cc
index b66e2f3..279e6d9 100644
--- a/chrome/browser/accessibility/accessibility_extension_apitest.cc
+++ b/chrome/browser/accessibility/accessibility_extension_apitest.cc
@@ -2,17 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/infobars/simple_alert_infobar_delegate.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
-
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
+#include "chrome/test/base/test_switches.h"
+#include "extensions/common/switches.h"
 
 // Times out on win asan, http://crbug.com/166026
 #if defined(OS_WIN) && defined(ADDRESS_SANITIZER)
@@ -23,7 +21,7 @@
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_GetAlertsForTab) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -39,6 +37,6 @@
                                      InfoBarDelegate::kNoIconID,
                                      ASCIIToUTF16(kAlertMessage), false);
   CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
+      extensions::switches::kEnableExperimentalExtensionApis);
   ASSERT_TRUE(RunExtensionTest("accessibility/get_alerts_for_tab")) << message_;
 }
diff --git a/chrome/browser/android/OWNERS b/chrome/browser/android/OWNERS
index 29d0acf..427eba0 100644
--- a/chrome/browser/android/OWNERS
+++ b/chrome/browser/android/OWNERS
@@ -1,3 +1,4 @@
 bulach@chromium.org
+nyquist@chromium.org
 tedchoc@chromium.org
 yfriedman@chromium.org
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index cf5c8dc..4d0475c 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -12,8 +12,10 @@
 #include "chrome/browser/android/dev_tools_server.h"
 #include "chrome/browser/android/favicon_helper.h"
 #include "chrome/browser/android/field_trial_helper.h"
+#include "chrome/browser/android/foreign_session_helper.h"
 #include "chrome/browser/android/intent_helper.h"
 #include "chrome/browser/android/most_visited_sites.h"
+#include "chrome/browser/android/omnibox/omnibox_prerender.h"
 #include "chrome/browser/android/provider/chrome_browser_provider.h"
 #include "chrome/browser/android/signin/signin_manager_android.h"
 #include "chrome/browser/android/tab_android.h"
@@ -68,11 +70,14 @@
   { "DevToolsServer", RegisterDevToolsServer },
   { "FaviconHelper", FaviconHelper::RegisterFaviconHelper },
   { "FieldTrialHelper", RegisterFieldTrialHelper },
+  { "ForeignSessionHelper",
+    ForeignSessionHelper::RegisterForeignSessionHelper },
   { "IntentHelper", RegisterIntentHelper },
   { "JavascriptAppModalDialog",
     JavascriptAppModalDialogAndroid::RegisterJavascriptAppModalDialog },
   { "MostVisitedSites", RegisterMostVisitedSites },
   { "NavigationPopup", NavigationPopup::RegisterNavigationPopup },
+  { "OmniboxPrerender", RegisterOmniboxPrerender },
   { "PersonalDataManagerAndroid",
     autofill::PersonalDataManagerAndroid::Register },
   { "ProfileAndroid", ProfileAndroid::RegisterProfileAndroid },
diff --git a/chrome/browser/android/foreign_session_helper.cc b/chrome/browser/android/foreign_session_helper.cc
new file mode 100644
index 0000000..0c8f0d9
--- /dev/null
+++ b/chrome/browser/android/foreign_session_helper.cc
@@ -0,0 +1,270 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/android/foreign_session_helper.h"
+
+#include <jni.h>
+
+#include "base/android/jni_string.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/profiles/profile_android.h"
+#include "chrome/browser/sync/glue/session_model_associator.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/android/tab_model/tab_model.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
+#include "jni/ForeignSessionHelper_jni.h"
+
+using base::android::ScopedJavaGlobalRef;
+using base::android::ScopedJavaLocalRef;
+using base::android::AttachCurrentThread;
+using base::android::ConvertUTF16ToJavaString;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ConvertJavaStringToUTF8;
+using browser_sync::SessionModelAssociator;
+using browser_sync::SyncedSession;
+
+namespace {
+
+SessionModelAssociator* GetSessionModelAssociator(Profile* profile) {
+  ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
+      GetForProfile(profile);
+
+  // Only return the associator if it exists and it is done syncing sessions.
+  if (!service || !service->ShouldPushChanges())
+    return NULL;
+
+  return service->GetSessionModelAssociator();
+}
+
+void CopyTabsToJava(
+    JNIEnv* env,
+    const SessionWindow* window,
+    ScopedJavaLocalRef<jobject>& j_window) {
+  for (std::vector<SessionTab*>::const_iterator tab_it = window->tabs.begin();
+      tab_it != window->tabs.end(); ++tab_it) {
+    const SessionTab &tab = **tab_it;
+
+    if (tab.navigations.empty())
+      continue;
+
+    const ::sessions::SerializedNavigationEntry& current_navigation =
+        tab.navigations.at(tab.current_navigation_index);
+
+    GURL tab_url = current_navigation.virtual_url();
+    if (tab_url.SchemeIs(chrome::kChromeNativeScheme) ||
+        (tab_url.SchemeIs(chrome::kChromeUIScheme) &&
+            tab_url.host() == chrome::kChromeUINewTabHost))
+      continue;
+
+    Java_ForeignSessionHelper_pushTab(
+        env, j_window.obj(),
+        ConvertUTF8ToJavaString(env, tab_url.spec()).Release(),
+        ConvertUTF16ToJavaString(env, current_navigation.title()).Release(),
+        tab.timestamp.ToInternalValue(), tab.tab_id.id());
+  }
+}
+
+void CopyWindowsToJava(
+    JNIEnv* env,
+    const SyncedSession* session,
+    ScopedJavaLocalRef<jobject>& j_session) {
+  for (SyncedSession::SyncedWindowMap::const_iterator it =
+      session->windows.begin(); it != session->windows.end(); ++it) {
+    const SessionWindow* window = it->second;
+
+    ScopedJavaLocalRef<jobject> last_pushed_window;
+    last_pushed_window.Reset(
+        Java_ForeignSessionHelper_pushWindow(
+            env, j_session.obj(), window->timestamp.ToInternalValue(),
+            window->window_id.id()));
+
+    CopyTabsToJava(env, window, last_pushed_window);
+  }
+}
+
+}  // namespace
+
+static jint Init(JNIEnv* env, jclass clazz, jobject profile) {
+  ForeignSessionHelper* foreign_session_helper = new ForeignSessionHelper(
+      ProfileAndroid::FromProfileAndroid(profile));
+  return reinterpret_cast<jint>(foreign_session_helper);
+}
+
+ForeignSessionHelper::ForeignSessionHelper(Profile* profile)
+    : profile_(profile) {
+  ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
+      GetForProfile(profile);
+
+  registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE,
+                 content::Source<ProfileSyncService>(service));
+  registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
+                 content::Source<Profile>(profile));
+  registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED,
+                 content::Source<Profile>(profile));
+}
+
+ForeignSessionHelper::~ForeignSessionHelper() {
+}
+
+void ForeignSessionHelper::Destroy(JNIEnv* env, jobject obj) {
+  delete this;
+}
+
+jboolean ForeignSessionHelper::IsTabSyncEnabled(JNIEnv* env, jobject obj) {
+  ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
+      GetForProfile(profile_);
+  return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
+}
+
+void ForeignSessionHelper::SetOnForeignSessionCallback(JNIEnv* env,
+                                                       jobject obj,
+                                                       jobject callback) {
+  callback_.Reset(env, callback);
+}
+
+void ForeignSessionHelper::Observe(
+    int type, const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  if (callback_.is_null())
+    return;
+
+  JNIEnv* env = AttachCurrentThread();
+
+  switch (type) {
+    case chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED:
+      // Tab sync is disabled, so clean up data about collapsed sessions.
+      profile_->GetPrefs()->ClearPref(
+          prefs::kNtpCollapsedForeignSessions);
+      // Purposeful fall through.
+    case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE:
+    case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
+      Java_ForeignSessionCallback_onUpdated(env, callback_.obj());
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
+jboolean ForeignSessionHelper::GetForeignSessions(JNIEnv* env,
+                                                  jobject obj,
+                                                  jobject result) {
+  SessionModelAssociator* associator = GetSessionModelAssociator(profile_);
+  if (!associator)
+    return false;
+
+  std::vector<const browser_sync::SyncedSession*> sessions;
+  if (!associator->GetAllForeignSessions(&sessions))
+    return false;
+
+  // Use a pref to keep track of sessions that were collapsed by the user.
+  // To prevent the pref from accumulating stale sessions, clear it each time
+  // and only add back sessions that are still current.
+  DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
+                                   prefs::kNtpCollapsedForeignSessions);
+  DictionaryValue* pref_collapsed_sessions = pref_update.Get();
+  scoped_ptr<DictionaryValue> collapsed_sessions(
+      pref_collapsed_sessions->DeepCopy());
+  pref_collapsed_sessions->Clear();
+
+  ScopedJavaLocalRef<jobject> last_pushed_session;
+  ScopedJavaLocalRef<jobject> last_pushed_window;
+
+  // Note: we don't own the SyncedSessions themselves.
+  for (size_t i = 0; i < sessions.size(); ++i) {
+    const browser_sync::SyncedSession* session = sessions[i];
+
+    const bool is_collapsed = collapsed_sessions->HasKey(session->session_tag);
+
+    if (is_collapsed)
+      pref_collapsed_sessions->SetBoolean(session->session_tag, true);
+
+    last_pushed_session.Reset(
+        Java_ForeignSessionHelper_pushSession(
+            env,
+            result,
+            ConvertUTF8ToJavaString(env, session->session_tag).Release(),
+            ConvertUTF8ToJavaString(env, session->session_name).Release(),
+            ConvertUTF8ToJavaString(env,
+                                    session->DeviceTypeAsString()).Release(),
+            session->modified_time.ToInternalValue()));
+
+    CopyWindowsToJava(env, session, last_pushed_session);
+  }
+
+  return true;
+}
+
+jboolean ForeignSessionHelper::OpenForeignSessionTab(JNIEnv* env,
+                                                     jobject obj,
+                                                     jstring session_tag,
+                                                     jint tab_id) {
+  SessionModelAssociator* associator = GetSessionModelAssociator(profile_);
+  if (!associator) {
+    LOG(ERROR) << "Null SessionModelAssociator returned.";
+    return false;
+  }
+
+  const SessionTab* tab;
+
+  if (!associator->GetForeignTab(ConvertJavaStringToUTF8(env, session_tag),
+                                 tab_id, &tab)) {
+    LOG(ERROR) << "Failed to load foreign tab.";
+    return false;
+  }
+
+  if (tab->navigations.empty()) {
+    LOG(ERROR) << "Foreign tab no longer has valid navigations.";
+    return false;
+  }
+
+  TabModel* tab_model = TabModelList::GetTabModelWithProfile(profile_);
+  DCHECK(tab_model);
+  if (!tab_model)
+    return false;
+
+  std::vector<content::NavigationEntry*> entries =
+      sessions::SerializedNavigationEntry::ToNavigationEntries(
+          tab->navigations, profile_);
+  content::WebContents* new_web_contents = content::WebContents::Create(
+      content::WebContents::CreateParams(profile_));
+  int selected_index = tab->normalized_navigation_index();
+  new_web_contents->GetController().Restore(
+      selected_index,
+      content::NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
+      &entries);
+  tab_model->CreateTab(new_web_contents);
+
+  return true;
+}
+
+void ForeignSessionHelper::SetForeignSessionCollapsed(JNIEnv* env, jobject obj,
+                                                      jstring session_tag,
+                                                      jboolean is_collapsed) {
+  // Store session tags for collapsed sessions in a preference so that the
+  // collapsed state persists.
+  PrefService* prefs = profile_->GetPrefs();
+  DictionaryPrefUpdate update(prefs, prefs::kNtpCollapsedForeignSessions);
+  if (is_collapsed)
+    update.Get()->SetBoolean(ConvertJavaStringToUTF8(env, session_tag), true);
+  else
+    update.Get()->Remove(ConvertJavaStringToUTF8(env, session_tag), NULL);
+}
+
+void ForeignSessionHelper::DeleteForeignSession(JNIEnv* env, jobject obj,
+                                                jstring session_tag) {
+  SessionModelAssociator* associator = GetSessionModelAssociator(profile_);
+  if (associator)
+    associator->DeleteForeignSession(ConvertJavaStringToUTF8(env, session_tag));
+}
+
+// static
+bool ForeignSessionHelper::RegisterForeignSessionHelper(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
diff --git a/chrome/browser/android/foreign_session_helper.h b/chrome/browser/android/foreign_session_helper.h
new file mode 100644
index 0000000..22c1025
--- /dev/null
+++ b/chrome/browser/android/foreign_session_helper.h
@@ -0,0 +1,58 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_ANDROID_FOREIGN_SESSION_HELPER_H_
+#define CHROME_BROWSER_ANDROID_FOREIGN_SESSION_HELPER_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+using base::android::ScopedJavaLocalRef;
+
+struct SessionWindow;
+
+namespace browser_sync {
+class SessionModelAssociator;
+struct SyncedSession;
+}  // namespace browser_sync
+
+class ForeignSessionHelper : public content::NotificationObserver {
+ public:
+  explicit ForeignSessionHelper(Profile* profile);
+  void Destroy(JNIEnv* env, jobject obj);
+  jboolean IsTabSyncEnabled(JNIEnv* env, jobject obj);
+  void SetOnForeignSessionCallback(JNIEnv* env, jobject obj, jobject callback);
+  jboolean GetForeignSessions(JNIEnv* env, jobject obj, jobject result);
+  jboolean OpenForeignSessionTab(JNIEnv* env,
+                                 jobject obj,
+                                 jstring session_tag,
+                                 jint tab_id);
+  void SetForeignSessionCollapsed(JNIEnv* env,
+                                  jobject obj,
+                                  jstring session_tag,
+                                  jboolean is_collapsed);
+  void DeleteForeignSession(JNIEnv* env, jobject obj, jstring session_tag);
+
+  // NotificationObserver implemenation
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
+  static bool RegisterForeignSessionHelper(JNIEnv* env);
+
+ private:
+  virtual ~ForeignSessionHelper();
+
+  Profile* profile_;  // weak
+  base::android::ScopedJavaGlobalRef<jobject> callback_;
+  content::NotificationRegistrar registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(ForeignSessionHelper);
+};
+
+#endif  // CHROME_BROWSER_ANDROID_FOREIGN_SESSION_HELPER_H_
diff --git a/chrome/browser/android/omnibox/omnibox_prerender.cc b/chrome/browser/android/omnibox/omnibox_prerender.cc
new file mode 100644
index 0000000..216a719
--- /dev/null
+++ b/chrome/browser/android/omnibox/omnibox_prerender.cc
@@ -0,0 +1,143 @@
+// Copyright 2013 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.
+
+#include "omnibox_prerender.h"
+
+#include "base/android/jni_string.h"
+#include "base/logging.h"
+#include "chrome/browser/autocomplete/autocomplete_match.h"
+#include "chrome/browser/autocomplete/autocomplete_result.h"
+#include "chrome/browser/predictors/autocomplete_action_predictor.h"
+#include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_android.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_view.h"
+#include "jni/OmniboxPrerender_jni.h"
+#include "url/gurl.h"
+
+using predictors::AutocompleteActionPredictor;
+using predictors::AutocompleteActionPredictorFactory;
+
+OmniboxPrerender::OmniboxPrerender(JNIEnv* env, jobject obj)
+    : weak_java_omnibox_(env, obj) {
+}
+
+OmniboxPrerender::~OmniboxPrerender() {
+}
+
+static jint Init(JNIEnv* env, jobject obj) {
+  OmniboxPrerender* omnibox = new OmniboxPrerender(env, obj);
+  return reinterpret_cast<jint>(omnibox);
+}
+
+bool RegisterOmniboxPrerender(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+void OmniboxPrerender::Clear(JNIEnv* env,
+                             jobject obj,
+                             jobject j_profile_android) {
+  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile_android);
+  DCHECK(profile);
+  if (!profile)
+    return;
+  AutocompleteActionPredictor* action_predictor =
+      AutocompleteActionPredictorFactory::GetForProfile(profile);
+  action_predictor->ClearTransitionalMatches();
+}
+
+void OmniboxPrerender::InitializeForProfile(
+    JNIEnv* env,
+    jobject obj,
+    jobject j_profile_android) {
+  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile_android);
+  // Initialize the AutocompleteActionPredictor for this profile.
+  // It needs to register for notifications as part of its initialization.
+  AutocompleteActionPredictorFactory::GetForProfile(profile);
+}
+
+void OmniboxPrerender::PrerenderMaybe(JNIEnv* env,
+                                      jobject obj,
+                                      jstring j_url,
+                                      jstring j_current_url,
+                                      jint jsource_match,
+                                      jobject j_profile_android,
+                                      jint native_web_contents) {
+  AutocompleteResult* autocomplete_result =
+      reinterpret_cast<AutocompleteResult*>(jsource_match);
+  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile_android);
+  base::string16 url_string =
+      base::android::ConvertJavaStringToUTF16(env, j_url);
+  base::string16 current_url_string =
+      base::android::ConvertJavaStringToUTF16(env, j_current_url);
+  content::WebContents* web_contents =
+      reinterpret_cast<content::WebContents*>(native_web_contents);
+  // TODO(apiccion) Use a delegate for communicating with web_contents.
+  // This can happen in OmniboxTests since the results are generated
+  // in Java only.
+  if (!autocomplete_result)
+    return;
+  if (!profile)
+    return;
+
+  const AutocompleteResult::const_iterator default_match(
+      autocomplete_result->default_match());
+  if (default_match == autocomplete_result->end())
+    return;
+
+  AutocompleteActionPredictor* action_predictor =
+      AutocompleteActionPredictorFactory::GetForProfile(profile);
+  if (!action_predictor)
+    return;
+
+  AutocompleteActionPredictor::Action recommended_action =
+      AutocompleteActionPredictor::ACTION_NONE;
+  if (action_predictor) {
+    action_predictor->
+        RegisterTransitionalMatches(url_string, *autocomplete_result);
+    recommended_action =
+        action_predictor->RecommendAction(url_string, *default_match);
+  }
+
+  GURL current_url = GURL(current_url_string);
+  switch (recommended_action) {
+    case AutocompleteActionPredictor::ACTION_PRERENDER:
+      // Ask for prerendering if the destination URL is different than the
+      // current URL.
+      if (default_match->destination_url != current_url) {
+        DoPrerender(
+            *default_match,
+            profile,
+            web_contents);
+      }
+      break;
+    case AutocompleteActionPredictor::ACTION_PRECONNECT:
+      // TODO (apiccion) add preconnect logic
+      break;
+    case AutocompleteActionPredictor::ACTION_NONE:
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+}
+
+void OmniboxPrerender::DoPrerender(const AutocompleteMatch& match,
+                                   Profile* profile,
+                                   content::WebContents* web_contents) {
+  DCHECK(profile);
+  if (!profile)
+    return;
+  DCHECK(web_contents);
+  if (!web_contents)
+    return;
+  gfx::Rect container_bounds;
+  web_contents->GetView()->GetContainerBounds(&container_bounds);
+  predictors::AutocompleteActionPredictorFactory::GetForProfile(profile)->
+      StartPrerendering(
+          match.destination_url,
+          web_contents->GetController().GetSessionStorageNamespaceMap(),
+          container_bounds.size());
+}
diff --git a/chrome/browser/android/omnibox/omnibox_prerender.h b/chrome/browser/android/omnibox/omnibox_prerender.h
new file mode 100644
index 0000000..c164021
--- /dev/null
+++ b/chrome/browser/android/omnibox/omnibox_prerender.h
@@ -0,0 +1,70 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_ANDROID_OMNIBOX_OMNIBOX_PRERENDER_H_
+#define CHROME_BROWSER_ANDROID_OMNIBOX_OMNIBOX_PRERENDER_H_
+
+#include "base/android/jni_helper.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+
+class AutocompleteResult;
+class ProfielAndroid;
+class Profile;
+class TabAndroid;
+struct AutocompleteMatch;
+
+namespace content {
+class WebContents;
+}
+
+// This class is responsible for taking the user's omnibox input text,
+// AutocompleteResults and navigation actions and then feeding them to the
+// AutocompleteActionPredictor. The predictor uses it to update its
+// database and returns predictions on what page, if any, to pre-render
+// or pre-connect. This class then takes the corresponding action.
+class OmniboxPrerender {
+ public:
+  OmniboxPrerender(JNIEnv* env, jobject obj);
+  virtual ~OmniboxPrerender();
+
+  // Clears the transitional matches. This should be called when the user
+  // stops typing into the omnibox (e.g. when navigating away, closing the
+  // keyboard or changing tabs).
+  void Clear(JNIEnv* env, jobject obj, jobject j_profile_android);
+
+  // Initializes the underlying action predictor for a given profile instance.
+  // This should be called as soon as possible as the predictor must register
+  // for certain notifications to properly initialize before providing
+  // predictions and updated its learning database.
+  void InitializeForProfile(JNIEnv* env,
+                            jobject obj,
+                            jobject j_profile_android);
+
+  // Potentailly invokes a pre-render or pre-connect given the url typed into
+  // the omnibox and a corresponding autocomplete result. This should be
+  // invoked everytime the omnibox changes (e.g. As the user types characters
+  // this method should be invoked at least once per character).
+  void PrerenderMaybe(JNIEnv* env,
+                      jobject obj,
+                      jstring j_url,
+                      jstring j_current_url,
+                      jint jsource_match,
+                      jobject j_profile_android,
+                      jint native_web_contents);
+
+ private:
+
+  // Prerenders a given AutocompleteMatch's url.
+  void DoPrerender(const AutocompleteMatch& match,
+                   Profile* profile,
+                   content::WebContents* web_contents);
+  JavaObjectWeakGlobalRef weak_java_omnibox_;
+
+  DISALLOW_COPY_AND_ASSIGN(OmniboxPrerender);
+};
+
+bool RegisterOmniboxPrerender(JNIEnv* env);
+
+#endif  // CHROME_BROWSER_ANDROID_OMNIBOX_OMNIBOX_PRERENDER_H_
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index 9e39a67..3c6f0c1 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -82,8 +82,8 @@
   virtual void RunExternalProtocolDialog(const GURL& url);
 
   // Used by sync to get/set the sync id of tab.
-  virtual int64 GetSyncId() const = 0;
-  virtual void SetSyncId(int64 sync_id) = 0;
+  virtual int GetSyncId() const = 0;
+  virtual void SetSyncId(int sync_id) = 0;
 
   static bool RegisterTabAndroid(JNIEnv* env);
 
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index b695448..859a539 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -4,6 +4,7 @@
 
 #import "chrome/browser/app_controller_mac.h"
 
+#include "apps/app_shim/extension_app_shim_handler_mac.h"
 #include "apps/shell_window.h"
 #include "base/auto_reset.h"
 #include "base/bind.h"
@@ -42,6 +43,7 @@
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/browser/sync/sync_ui_util.h"
 #include "chrome/browser/ui/browser.h"
@@ -386,6 +388,27 @@
 }
 
 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app {
+  using extensions::ShellWindowRegistry;
+
+  // If there are no windows, quit immediately.
+  if (chrome::BrowserIterator().done() &&
+      !ShellWindowRegistry::IsShellWindowRegisteredInAnyProfile(0)) {
+    return NSTerminateNow;
+  }
+
+  // Check if this is a keyboard initiated quit on an app window. If so, quit
+  // the app. This could cause the app to trigger another terminate, but that
+  // will be caught by the no windows condition above.
+  if ([[app currentEvent] type] == NSKeyDown) {
+    apps::ShellWindow* shellWindow =
+        ShellWindowRegistry::GetShellWindowForNativeWindowAnyProfile(
+            [app keyWindow]);
+    if (shellWindow) {
+      apps::ExtensionAppShimHandler::QuitAppForWindow(shellWindow);
+      return NSTerminateCancel;
+    }
+  }
+
   // Check if the preference is turned on.
   const PrefService* prefs = g_browser_process->local_state();
   if (!prefs->GetBoolean(prefs::kConfirmToQuitEnabled)) {
@@ -742,7 +765,7 @@
   std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
   for (size_t i = 0; i < profiles.size(); ++i) {
     DownloadService* download_service =
-      DownloadServiceFactory::GetForProfile(profiles[i]);
+      DownloadServiceFactory::GetForBrowserContext(profiles[i]);
     DownloadManager* download_manager =
         (download_service->HasCreatedDownloadManager() ?
          BrowserContext::GetDownloadManager(profiles[i]) : NULL);
@@ -1000,10 +1023,11 @@
         chrome::OpenHelpWindow(lastProfile, chrome::HELP_SOURCE_MENU);
       break;
     case IDC_SHOW_SYNC_SETUP:
-      if (Browser* browser = ActivateBrowser(lastProfile))
-        chrome::ShowBrowserSignin(browser, SyncPromoUI::SOURCE_MENU);
-      else
-        chrome::OpenSyncSetupWindow(lastProfile, SyncPromoUI::SOURCE_MENU);
+      if (Browser* browser = ActivateBrowser(lastProfile)) {
+        chrome::ShowBrowserSignin(browser, signin::SOURCE_MENU);
+      } else {
+        chrome::OpenSyncSetupWindow(lastProfile, signin::SOURCE_MENU);
+      }
       break;
     case IDC_TASK_MANAGER:
       content::RecordAction(UserMetricsAction("TaskManager"));
diff --git a/chrome/browser/apps/shortcut_manager.cc b/chrome/browser/apps/shortcut_manager.cc
index 1365b5e..7ed2da5 100644
--- a/chrome/browser/apps/shortcut_manager.cc
+++ b/chrome/browser/apps/shortcut_manager.cc
@@ -27,6 +27,10 @@
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_source.h"
 
+#if defined(OS_MACOSX)
+#include "apps/app_shim/app_shim_mac.h"
+#endif
+
 using extensions::Extension;
 
 namespace {
@@ -95,8 +99,7 @@
     }
     case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
 #if defined(OS_MACOSX)
-      if (!CommandLine::ForCurrentProcess()->
-          HasSwitch(switches::kEnableAppShims))
+      if (!apps::IsAppShimsEnabled())
         break;
 #endif  // defined(OS_MACOSX)
 
@@ -151,8 +154,7 @@
   // Until it is enabled permanently, we need to check the flag, and set the
   // pref accordingly.
 #if defined(OS_MACOSX)
-  bool is_now_enabled =
-      CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppShims);
+  bool is_now_enabled = apps::IsAppShimsEnabled();
 #else
   bool is_now_enabled = true;
 #endif  // defined(OS_MACOSX)
diff --git a/chrome/browser/autocomplete/autocomplete_browsertest.cc b/chrome/browser/autocomplete/autocomplete_browsertest.cc
index c38c19d..3ce74d3 100644
--- a/chrome/browser/autocomplete/autocomplete_browsertest.cc
+++ b/chrome/browser/autocomplete/autocomplete_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/format_macros.h"
 #include "base/path_service.h"
 #include "base/strings/stringprintf.h"
@@ -27,15 +28,12 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 namespace {
 
 string16 AutocompleteResultAsString(const AutocompleteResult& result) {
@@ -71,7 +69,7 @@
 IN_PROC_BROWSER_TEST_F(AutocompleteBrowserTest, Basic) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -121,7 +119,7 @@
 IN_PROC_BROWSER_TEST_F(AutocompleteBrowserTest, MAYBE_Autocomplete) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -172,7 +170,7 @@
 IN_PROC_BROWSER_TEST_F(AutocompleteBrowserTest, TabAwayRevertSelect) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -199,7 +197,7 @@
 IN_PROC_BROWSER_TEST_F(AutocompleteBrowserTest, FocusSearch) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc
index 89d2925..d4245fd 100644
--- a/chrome/browser/autocomplete/search_provider.cc
+++ b/chrome/browser/autocomplete/search_provider.cc
@@ -251,11 +251,7 @@
       suggest_results_pending_(0),
       field_trial_triggered_(false),
       field_trial_triggered_in_session_(false),
-      omnibox_start_margin_(-1),
-      prevent_search_history_inlining_(
-          OmniboxFieldTrial::SearchHistoryPreventInlining()),
-      disable_search_history_(
-          OmniboxFieldTrial::SearchHistoryDisable()) {
+      omnibox_start_margin_(-1) {
 }
 
 // static
@@ -630,7 +626,8 @@
   keyword_history_results_.clear();
   default_history_results_.clear();
 
-  if (disable_search_history_)
+  if (OmniboxFieldTrial::SearchHistoryDisable(
+      input_.current_page_classification()))
     return;
 
   HistoryService* const history_service =
@@ -1203,6 +1200,9 @@
   AutocompleteClassifier* classifier =
       AutocompleteClassifierFactory::GetForProfile(profile_);
   SuggestResults scored_results;
+  const bool prevent_search_history_inlining =
+      OmniboxFieldTrial::SearchHistoryPreventInlining(
+          input_.current_page_classification());
   for (HistoryResults::const_iterator i(results.begin()); i != results.end();
        ++i) {
     // Don't autocomplete multi-word queries that have only been seen once
@@ -1230,8 +1230,9 @@
           !AutocompleteMatch::IsSearchType(match.type);
     }
 
-    int relevance = CalculateRelevanceForHistory(i->time, is_keyword,
-                                                 prevent_inline_autocomplete);
+    int relevance = CalculateRelevanceForHistory(
+        i->time, is_keyword, !prevent_inline_autocomplete,
+        prevent_search_history_inlining);
     scored_results.push_back(
         SuggestResult(i->term, is_keyword, relevance, false));
   }
@@ -1336,22 +1337,23 @@
 int SearchProvider::CalculateRelevanceForHistory(
     const base::Time& time,
     bool is_keyword,
-    bool prevent_inline_autocomplete) const {
+    bool use_aggressive_method,
+    bool prevent_search_history_inlining) const {
   // The relevance of past searches falls off over time. There are two distinct
   // equations used. If the first equation is used (searches to the primary
-  // provider that we want to inline autocomplete), the score is in the range
-  // 1300-1599 (unless |prevent_search_history_inlining_|, in which case
+  // provider that we want to score aggressively), the score is in the range
+  // 1300-1599 (unless |prevent_search_history_inlining|, in which case
   // it's in the range 1200-1299). If the second equation is used the
   // relevance of a search 15 minutes ago is discounted 50 points, while the
   // relevance of a search two weeks ago is discounted 450 points.
   double elapsed_time = std::max((base::Time::Now() - time).InSecondsF(), 0.0);
   bool is_primary_provider = is_keyword || !providers_.has_keyword_provider();
-  if (is_primary_provider && !prevent_inline_autocomplete) {
+  if (is_primary_provider && use_aggressive_method) {
     // Searches with the past two days get a different curve.
     const double autocomplete_time = 2 * 24 * 60 * 60;
     if (elapsed_time < autocomplete_time) {
       int max_score = is_keyword ? 1599 : 1399;
-      if (prevent_search_history_inlining_)
+      if (prevent_search_history_inlining)
         max_score = 1299;
       return max_score - static_cast<int>(99 *
           std::pow(elapsed_time / autocomplete_time, 2.5));
diff --git a/chrome/browser/autocomplete/search_provider.h b/chrome/browser/autocomplete/search_provider.h
index 3f9b080..74c5e7e 100644
--- a/chrome/browser/autocomplete/search_provider.h
+++ b/chrome/browser/autocomplete/search_provider.h
@@ -435,11 +435,14 @@
 
   // |time| is the time at which this query was last seen.  |is_keyword|
   // indicates whether the results correspond to the keyword provider or default
-  // provider. |prevent_inline_autocomplete| is true if we should not inline
-  // autocomplete this query.
+  // provider. |use_aggressive_method| says whether this function can use a
+  // method that gives high scores (1200+) rather than one that gives lower
+  // scores.  When using the aggressive method, scores may exceed 1300
+  // unless |prevent_search_history_inlining| is set.
   int CalculateRelevanceForHistory(const base::Time& time,
                                    bool is_keyword,
-                                   bool prevent_inline_autocomplete) const;
+                                   bool use_aggressive_method,
+                                   bool prevent_search_history_inlining) const;
 
   // Creates an AutocompleteMatch for "Search <engine> for |query_string|" with
   // the supplied relevance.  Adds this match to |map|; if such a match already
@@ -531,9 +534,6 @@
   // they will not be inlined.
   bool prevent_search_history_inlining_;
 
-  // If true, no search history query suggestions will be offered.
-  bool disable_search_history_;
-
   DISALLOW_COPY_AND_ASSIGN(SearchProvider);
 };
 
diff --git a/chrome/browser/autocomplete/zero_suggest_provider.cc b/chrome/browser/autocomplete/zero_suggest_provider.cc
index 181a3d9..bd850a6 100644
--- a/chrome/browser/autocomplete/zero_suggest_provider.cc
+++ b/chrome/browser/autocomplete/zero_suggest_provider.cc
@@ -194,10 +194,6 @@
   if (url.scheme() != chrome::kHttpScheme)
     return false;
 
-  // Don't enable ZeroSuggest until InstantExtended works with ZeroSuggest.
-  if (chrome::IsInstantExtendedAPIEnabled())
-    return false;
-
   // Don't run if there's no profile or in incognito mode.
   if (profile_ == NULL || profile_->IsOffTheRecord())
     return false;
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.cc b/chrome/browser/autofill/android/personal_data_manager_android.cc
index a853f92..9285982 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.cc
+++ b/chrome/browser/autofill/android/personal_data_manager_android.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "components/autofill/core/browser/autofill_country.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "jni/PersonalDataManager_jni.h"
@@ -213,7 +214,6 @@
   return ConvertUTF8ToJavaString(env, profile.guid());
 }
 
-
 jint PersonalDataManagerAndroid::GetCreditCardCount(JNIEnv* unused_env,
                                                     jobject unused_obj) {
   return personal_data_manager_->GetCreditCards().size();
@@ -282,6 +282,16 @@
   return RegisterNativesImpl(env);
 }
 
+// Returns an ISO 3166-1-alpha-2 country code for a |jcountry_name| using
+// the application locale, or an empty string.
+static jstring ToCountryCode(JNIEnv* env, jclass clazz, jstring jcountry_name) {
+  return ConvertUTF8ToJavaString(
+      env,
+      AutofillCountry::GetCountryCode(
+          base::android::ConvertJavaStringToUTF16(env, jcountry_name),
+          g_browser_process->GetApplicationLocale())).Release();
+}
+
 static jint Init(JNIEnv* env, jobject obj) {
   PersonalDataManagerAndroid* personal_data_manager_android =
       new PersonalDataManagerAndroid(env, obj);
diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc
index 1b37b32..9baa482 100644
--- a/chrome/browser/autofill/autofill_browsertest.cc
+++ b/chrome/browser/autofill/autofill_browsertest.cc
@@ -5,6 +5,7 @@
 #include <string>
 
 #include "base/basictypes.h"
+#include "base/command_line.h"
 #include "base/file_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
@@ -26,6 +27,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/autofill/content/browser/autofill_driver_impl.h"
 #include "components/autofill/core/browser/autofill_common_test.h"
@@ -51,11 +53,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/keycodes/keyboard_codes.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
-
 namespace autofill {
 
 static const char* kDataURIPrefix = "data:text/html;charset=utf-8,";
@@ -1183,7 +1180,7 @@
 IN_PROC_BROWSER_TEST_F(AutofillTest, InvalidCreditCardNumberIsNotAggregated) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -1203,8 +1200,8 @@
 IN_PROC_BROWSER_TEST_F(AutofillTest,
                        WhitespacesAndSeparatorCharsStrippedForValidCCNums) {
 #if defined(OS_WIN) && defined(USE_ASH)
-  // Disable this test in Metro+Ash for now (http://crbug.com/179830).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -1414,7 +1411,7 @@
 IN_PROC_BROWSER_TEST_F(AutofillTest, CCInfoNotStoredWhenAutocompleteOff) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index 75d7a80..4abcd57 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -75,6 +75,7 @@
  public:
   AutofillManagerTestDelegateImpl() {}
 
+  // autofill::AutofillManagerTestDelegate:
   virtual void DidPreviewFormData() OVERRIDE {
     LOG(INFO) << "DidPreviewFormData";
     loop_runner_->Quit();
@@ -125,15 +126,6 @@
       infobar_service_->RemoveInfoBar(infobar_service_->infobar_at(0));
   }
 
-  void Wait() {
-    if (!alerted_) {
-      has_run_message_loop_ = true;
-      content::RunMessageLoop();
-    }
-    PersonalDataManagerFactory::GetForProfile(browser_->profile())->
-        RemoveObserver(this);
-  }
-
   // PersonalDataManagerObserver:
   virtual void OnPersonalDataChanged() OVERRIDE {
     if (has_run_message_loop_) {
@@ -161,6 +153,15 @@
     confirm_infobar->Accept();
   }
 
+  void Wait() {
+    if (!alerted_) {
+      has_run_message_loop_ = true;
+      content::RunMessageLoop();
+    }
+    PersonalDataManagerFactory::GetForProfile(browser_->profile())->
+        RemoveObserver(this);
+  }
+
  private:
   bool alerted_;
   bool has_run_message_loop_;
@@ -180,6 +181,7 @@
   }
   virtual ~TestAutofillExternalDelegate() {}
 
+  // AutofillExternalDelegate:
   virtual void OnPopupShown(content::KeyboardListener* listener) OVERRIDE {
     AutofillExternalDelegate::OnPopupShown(listener);
     keyboard_listener_ = listener;
@@ -206,6 +208,7 @@
  protected:
   AutofillInteractiveTest() {}
 
+  // InProcessBrowserTest:
   virtual void SetUpOnMainThread() OVERRIDE {
     // Don't want Keychain coming up on Mac.
     test::DisableSystemServices(browser()->profile());
@@ -233,10 +236,24 @@
     autofill_manager->delegate()->HideAutofillPopup();
   }
 
-  PersonalDataManager* personal_data_manager() {
+  PersonalDataManager* GetPersonalDataManager() {
     return PersonalDataManagerFactory::GetForProfile(browser()->profile());
   }
 
+  content::RenderViewHost* GetRenderViewHost() {
+    return browser()->tab_strip_model()->GetActiveWebContents()->
+        GetRenderViewHost();
+  }
+
+  TestAutofillExternalDelegate* GetExternalDelegate() {
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    AutofillDriverImpl* autofill_driver =
+        AutofillDriverImpl::FromWebContents(web_contents);
+    return static_cast<TestAutofillExternalDelegate*>(
+        autofill_driver->autofill_external_delegate());
+  }
+
   void CreateTestProfile() {
     AutofillProfile profile;
     test::SetProfileInfo(
@@ -245,7 +262,7 @@
         "Basement", "Austin", "Texas", "78744", "US", "5125551234");
 
     WindowedPersonalDataManagerObserver observer(browser());
-    personal_data_manager()->AddProfile(profile);
+    GetPersonalDataManager()->AddProfile(profile);
 
     // AddProfile is asynchronous. Wait for it to finish before continuing the
     // tests.
@@ -263,22 +280,11 @@
     EXPECT_EQ(expected_value, value);
   }
 
-  RenderViewHost* render_view_host() {
-    return browser()->tab_strip_model()->GetActiveWebContents()->
-        GetRenderViewHost();
-  }
-
   void FocusFirstNameField() {
-    LOG(WARNING) << "Clicking on the tab.";
-    content::SimulateMouseClick(
-        browser()->tab_strip_model()->GetActiveWebContents(),
-        0,
-        WebKit::WebMouseEvent::ButtonLeft);
-
     LOG(WARNING) << "Focusing the first name field.";
     bool result = false;
     ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
-        render_view_host(),
+        GetRenderViewHost(),
         "if (document.readyState === 'complete')"
         "  document.getElementById('firstname').focus();"
         "else"
@@ -315,24 +321,15 @@
     content::NativeWebKeyboardEvent event;
     event.windowsKeyCode = key;
     test_delegate_.Reset();
-    external_delegate()->keyboard_listener()->HandleKeyPressEvent(event);
+    GetExternalDelegate()->keyboard_listener()->HandleKeyPressEvent(event);
     test_delegate_.Wait();
   }
 
-  TestAutofillExternalDelegate* external_delegate() {
-    content::WebContents* web_contents =
-        browser()->tab_strip_model()->GetActiveWebContents();
-    AutofillDriverImpl* autofill_driver =
-        AutofillDriverImpl::FromWebContents(web_contents);
-    return static_cast<TestAutofillExternalDelegate*>(
-        autofill_driver->autofill_external_delegate());
-  }
-
   AutofillManagerTestDelegateImpl test_delegate_;
 };
 
-// http://crbug.com/150084
-IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, DISABLED_AutofillSelectViaTab) {
+// Potentially flaky, see http://crbug.com/150084
+IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, AutofillSelectViaTab) {
   CreateTestProfile();
 
   // Load the test page.
diff --git a/chrome/browser/automation/chrome_frame_automation_provider_win.cc b/chrome/browser/automation/chrome_frame_automation_provider_win.cc
index e251a35..c0be19e 100644
--- a/chrome/browser/automation/chrome_frame_automation_provider_win.cc
+++ b/chrome/browser/automation/chrome_frame_automation_provider_win.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/automation/chrome_frame_automation_provider_win.h"
 
-#include "chrome/browser/browser_process.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/automation_messages.h"
@@ -14,15 +14,11 @@
 
 ChromeFrameAutomationProvider::ChromeFrameAutomationProvider(Profile* profile)
     : AutomationProvider(profile) {
-  DCHECK(g_browser_process);
-  if (g_browser_process)
-    g_browser_process->AddRefModule();
+  chrome::StartKeepAlive();
 }
 
 ChromeFrameAutomationProvider::~ChromeFrameAutomationProvider() {
-  DCHECK(g_browser_process);
-  if (g_browser_process)
-    g_browser_process->ReleaseModule();
+  chrome::EndKeepAlive();
 }
 
 bool ChromeFrameAutomationProvider::OnMessageReceived(
diff --git a/chrome/browser/automation/chrome_frame_automation_provider_win_unittest.cc b/chrome/browser/automation/chrome_frame_automation_provider_win_unittest.cc
index 3f9e9e4..8559a6b 100644
--- a/chrome/browser/automation/chrome_frame_automation_provider_win_unittest.cc
+++ b/chrome/browser/automation/chrome_frame_automation_provider_win_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/automation/chrome_frame_automation_provider_win.h"
+#include "chrome/browser/browser_shutdown.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "content/public/test/test_browser_thread.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -22,7 +23,13 @@
                void (const IPC::Message& message));  // NOLINT
 };
 
-typedef testing::Test AutomationProviderTest;
+class AutomationProviderTest : public testing::Test {
+ protected:
+  virtual void TearDown() OVERRIDE {
+    // Don't leak state into other tests.
+    browser_shutdown::SetTryingToQuit(false);
+  }
+};
 
 TEST_F(AutomationProviderTest, TestInvalidChromeFrameMessage) {
   base::MessageLoop message_loop;
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc
index 3dc3d20..baef984 100644
--- a/chrome/browser/automation/testing_automation_provider.cc
+++ b/chrome/browser/automation/testing_automation_provider.cc
@@ -1789,10 +1789,7 @@
   handler_map_["GetTimeInfo"] = &TestingAutomationProvider::GetTimeInfo;
   handler_map_["SetTimezone"] = &TestingAutomationProvider::SetTimezone;
 
-  handler_map_["GetUpdateInfo"] = &TestingAutomationProvider::GetUpdateInfo;
   handler_map_["UpdateCheck"] = &TestingAutomationProvider::UpdateCheck;
-  handler_map_["SetReleaseTrack"] =
-      &TestingAutomationProvider::SetReleaseTrack;
 
   handler_map_["GetVolumeInfo"] = &TestingAutomationProvider::GetVolumeInfo;
   handler_map_["SetVolume"] = &TestingAutomationProvider::SetVolume;
@@ -2491,7 +2488,7 @@
   ListValue* list_of_downloads = new ListValue;
 
   DownloadService* download_service(
-      DownloadServiceFactory::GetForProfile(browser->profile()));
+      DownloadServiceFactory::GetForBrowserContext(browser->profile()));
 
   if (download_service->HasCreatedDownloadManager()) {
     std::vector<DownloadItem*> downloads;
@@ -2523,7 +2520,7 @@
   }
 
   DownloadService* download_service =
-      DownloadServiceFactory::GetForProfile(browser->profile());
+      DownloadServiceFactory::GetForBrowserContext(browser->profile());
   if (!download_service->HasCreatedDownloadManager()) {
     // No download manager, so no downloads to wait for.
     AutomationJSONReply(this, reply_message).SendSuccess(NULL);
@@ -2547,7 +2544,7 @@
   std::string action;
 
   DownloadService* download_service =
-      DownloadServiceFactory::GetForProfile(browser->profile());
+      DownloadServiceFactory::GetForBrowserContext(browser->profile());
   if (!download_service->HasCreatedDownloadManager()) {
     AutomationJSONReply(this, reply_message).SendError("No download manager.");
     return;
diff --git a/chrome/browser/automation/testing_automation_provider.h b/chrome/browser/automation/testing_automation_provider.h
index a095c56..4e35f60 100644
--- a/chrome/browser/automation/testing_automation_provider.h
+++ b/chrome/browser/automation/testing_automation_provider.h
@@ -1368,14 +1368,8 @@
 
   void SetTimezone(base::DictionaryValue* args, IPC::Message* reply_message);
 
-  // Update.
-  void GetUpdateInfo(base::DictionaryValue* args, IPC::Message* reply_message);
-
   void UpdateCheck(base::DictionaryValue* args, IPC::Message* reply_message);
 
-  void SetReleaseTrack(base::DictionaryValue* args,
-                       IPC::Message* reply_message);
-
   // Volume.
   void GetVolumeInfo(base::DictionaryValue* args, IPC::Message* reply_message);
 
diff --git a/chrome/browser/automation/testing_automation_provider_chromeos.cc b/chrome/browser/automation/testing_automation_provider_chromeos.cc
index 6183395..598da8d 100644
--- a/chrome/browser/automation/testing_automation_provider_chromeos.cc
+++ b/chrome/browser/automation/testing_automation_provider_chromeos.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_util.h"
-#include "chrome/browser/chromeos/audio/audio_handler.h"
 #include "chrome/browser/chromeos/cros/network_library.h"
 #include "chrome/browser/chromeos/login/default_user_images.h"
 #include "chrome/browser/chromeos/login/enrollment/enrollment_screen.h"
@@ -43,6 +42,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
 #include "chromeos/dbus/update_engine_client.h"
@@ -103,33 +103,6 @@
   }
 }
 
-void GetReleaseTrackCallback(AutomationJSONReply* reply,
-                             const std::string& track) {
-  if (track.empty()) {
-    reply->SendError("Unable to get release track.");
-    delete reply;
-    return;
-  }
-
-  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
-  return_value->SetString("release_track", track);
-
-  const UpdateEngineClient::Status& status =
-      DBusThreadManager::Get()->GetUpdateEngineClient()->GetLastStatus();
-  UpdateEngineClient::UpdateStatusOperation update_status =
-      status.status;
-  return_value->SetString("status", UpdateStatusToString(update_status));
-  if (update_status == UpdateEngineClient::UPDATE_STATUS_DOWNLOADING)
-    return_value->SetDouble("download_progress", status.download_progress);
-  if (status.last_checked_time > 0)
-    return_value->SetInteger("last_checked_time", status.last_checked_time);
-  if (status.new_size > 0)
-    return_value->SetInteger("new_size", status.new_size);
-
-  reply->SendSuccess(return_value.get());
-  delete reply;
-}
-
 void UpdateCheckCallback(AutomationJSONReply* reply,
                          UpdateEngineClient::UpdateCheckResult result) {
   if (result == UpdateEngineClient::UPDATE_RESULT_SUCCESS)
@@ -1154,13 +1127,6 @@
   reply.SendSuccess(NULL);
 }
 
-void TestingAutomationProvider::GetUpdateInfo(DictionaryValue* args,
-                                              IPC::Message* reply_message) {
-  AutomationJSONReply* reply = new AutomationJSONReply(this, reply_message);
-  DBusThreadManager::Get()->GetUpdateEngineClient()
-      ->GetReleaseTrack(base::Bind(GetReleaseTrackCallback, reply));
-}
-
 void TestingAutomationProvider::UpdateCheck(
     DictionaryValue* args,
     IPC::Message* reply_message) {
@@ -1169,30 +1135,17 @@
       ->RequestUpdateCheck(base::Bind(UpdateCheckCallback, reply));
 }
 
-void TestingAutomationProvider::SetReleaseTrack(DictionaryValue* args,
-                                                IPC::Message* reply_message) {
-  AutomationJSONReply reply(this, reply_message);
-  std::string track;
-  if (!args->GetString("track", &track)) {
-    reply.SendError("Invalid or missing args.");
-    return;
-  }
-
-  DBusThreadManager::Get()->GetUpdateEngineClient()->SetReleaseTrack(track);
-  reply.SendSuccess(NULL);
-}
-
 void TestingAutomationProvider::GetVolumeInfo(DictionaryValue* args,
                                               IPC::Message* reply_message) {
   AutomationJSONReply reply(this, reply_message);
   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
-  chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
+  chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
   if (!audio_handler) {
-    reply.SendError("AudioHandler not initialized.");
+    reply.SendError("CrasAudioHandler not initialized.");
     return;
   }
-  return_value->SetDouble("volume", audio_handler->GetVolumePercent());
-  return_value->SetBoolean("is_mute", audio_handler->IsMuted());
+  return_value->SetDouble("volume", audio_handler->GetOutputVolumePercent());
+  return_value->SetBoolean("is_mute", audio_handler->IsOutputMuted());
   reply.SendSuccess(return_value.get());
 }
 
@@ -1204,12 +1157,12 @@
     reply.SendError("Invalid or missing args.");
     return;
   }
-  chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
+  chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
   if (!audio_handler) {
-    reply.SendError("AudioHandler not initialized.");
+    reply.SendError("CrasAudioHandler not initialized.");
     return;
   }
-  audio_handler->SetVolumePercent(volume_percent);
+  audio_handler->SetOutputVolumePercent(volume_percent);
   reply.SendSuccess(NULL);
 }
 
@@ -1221,12 +1174,12 @@
     reply.SendError("Invalid or missing args.");
     return;
   }
-  chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
+  chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
   if (!audio_handler) {
-    reply.SendError("AudioHandler not initialized.");
+    reply.SendError("CrasAudioHandler not initialized.");
     return;
   }
-  audio_handler->SetMuted(mute);
+  audio_handler->SetOutputMute(mute);
   reply.SendSuccess(NULL);
 }
 
diff --git a/chrome/browser/automation/url_request_automation_job.cc b/chrome/browser/automation/url_request_automation_job.cc
index b403a57..fba5f85 100644
--- a/chrome/browser/automation/url_request_automation_job.cc
+++ b/chrome/browser/automation/url_request_automation_job.cc
@@ -197,6 +197,7 @@
     }
   }
   DisconnectFromMessageFilter();
+  receive_headers_end_ = base::TimeTicks();
   net::URLRequestJob::Kill();
 }
 
@@ -259,6 +260,18 @@
   }
 }
 
+void URLRequestAutomationJob::GetLoadTimingInfo(
+    net::LoadTimingInfo* load_timing_info) const {
+  if (!receive_headers_end_.is_null()) {
+    load_timing_info->send_start = request_start_;
+    // The send ended some time ago, but that information is not available on
+    // this side of the automation channel. Consider the send to have ended at
+    // the same time we received the response headers.
+    load_timing_info->send_end = receive_headers_end_;
+    load_timing_info->receive_headers_end = receive_headers_end_;
+  }
+}
+
 int URLRequestAutomationJob::GetResponseCode() const {
   if (headers_.get())
     return headers_->response_code();
@@ -337,6 +350,8 @@
   set_expected_content_size(response.content_length);
   mime_type_ = response.mime_type;
 
+  receive_headers_end_ = base::TimeTicks::Now();
+
   redirect_url_ = response.redirect_url;
   redirect_status_ = response.redirect_status;
   DCHECK(redirect_status_ == 0 || redirect_status_ == 200 ||
@@ -499,6 +514,8 @@
   if (request_->get_upload())
     upload_data = CreateUploadData(request_->get_upload());
 
+  request_start_ = base::TimeTicks::Now();
+
   // Ask automation to start this request.
   AutomationURLRequest automation_request;
   automation_request.url = request_->url().spec();
diff --git a/chrome/browser/automation/url_request_automation_job.h b/chrome/browser/automation/url_request_automation_job.h
index 7a73144..9c7d716 100644
--- a/chrome/browser/automation/url_request_automation_job.h
+++ b/chrome/browser/automation/url_request_automation_job.h
@@ -49,6 +49,7 @@
   virtual bool GetMimeType(std::string* mime_type) const;
   virtual bool GetCharset(std::string* charset);
   virtual void GetResponseInfo(net::HttpResponseInfo* info);
+  virtual void GetLoadTimingInfo(net::LoadTimingInfo* load_timing_info) const;
   virtual int GetResponseCode() const;
   virtual bool IsRedirectResponse(GURL* location, int* http_status_code);
   virtual net::UploadProgress GetUploadProgress() const;
@@ -137,6 +138,12 @@
   // Size of the upload data appended to the request.
   uint64 upload_size_;
 
+  // When the request was sent out over automation.
+  base::TimeTicks request_start_;
+
+  // When the response headers arrived from automation.
+  base::TimeTicks receive_headers_end_;
+
   base::WeakPtrFactory<URLRequestAutomationJob> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(URLRequestAutomationJob);
diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc
index b925aba..579f031 100644
--- a/chrome/browser/background/background_mode_manager.cc
+++ b/chrome/browser/background/background_mode_manager.cc
@@ -665,17 +665,16 @@
   if (!status_tray_ || status_icon_)
     return;
 
-  status_icon_ =
-      status_tray_->CreateStatusIcon(StatusTray::BACKGROUND_MODE_ICON);
-  if (!status_icon_)
-    return;
-
-  // Set the image and add ourselves as a click observer on it.
   // TODO(rlp): Status tray icon should have submenus for each profile.
   gfx::ImageSkia* image_skia = ui::ResourceBundle::GetSharedInstance().
       GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
-  status_icon_->SetImage(*image_skia);
-  status_icon_->SetToolTip(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
+
+  status_icon_ = status_tray_->CreateStatusIcon(
+      StatusTray::BACKGROUND_MODE_ICON,
+      *image_skia,
+      l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
+  if (!status_icon_)
+    return;
   UpdateStatusTrayIconContextMenu();
 }
 
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 75897f2..82aa66b 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -115,11 +115,6 @@
 #include "chrome/browser/plugins/plugins_resource_service.h"
 #endif
 
-#if defined(OS_MACOSX)
-#include "apps/app_shim/app_shim_host_manager_mac.h"
-#include "chrome/browser/ui/app_list/app_list_service.h"
-#endif
-
 #if defined(ENABLE_WEBRTC)
 #include "chrome/browser/media/webrtc_log_uploader.h"
 #endif
@@ -279,10 +274,6 @@
   aura::Env::DeleteInstance();
 #endif
 
-#if defined(OS_MACOSX)
-  app_shim_host_manager_.reset();
-#endif
-
   platform_part()->StartTearDown();
 }
 
@@ -939,10 +930,7 @@
   storage_monitor_.reset(chrome::StorageMonitor::Create());
 #endif
 
-#if defined(OS_MACOSX)
-  app_shim_host_manager_.reset(new AppShimHostManager);
-  AppListService::InitAll(NULL);
-#endif
+  platform_part_->PreMainMessageLoopRun();
 }
 
 void BrowserProcessImpl::CreateIconManager() {
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 50a76d1..728c0d9 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -41,10 +41,6 @@
 class PolicyService;
 };
 
-#if defined(OS_MACOSX)
-class AppShimHostManager;
-#endif
-
 // Real implementation of BrowserProcess that creates and returns the services.
 class BrowserProcessImpl : public BrowserProcess,
                            public base::NonThreadSafe {
@@ -292,11 +288,6 @@
   scoped_refptr<PluginsResourceService> plugins_resource_service_;
 #endif
 
-#if defined(OS_MACOSX)
-  // Hosts the IPC channel factory that App Shims connect to on Mac.
-  scoped_ptr<AppShimHostManager> app_shim_host_manager_;
-#endif
-
   scoped_ptr<BrowserProcessPlatformPart> platform_part_;
 
   // TODO(eroman): Remove this when done debugging 113031. This tracks
diff --git a/chrome/browser/browser_process_platform_part_base.cc b/chrome/browser/browser_process_platform_part_base.cc
index 1da8917..0287321 100644
--- a/chrome/browser/browser_process_platform_part_base.cc
+++ b/chrome/browser/browser_process_platform_part_base.cc
@@ -29,3 +29,6 @@
   chrome::CloseAllBrowsers();
 #endif
 }
+
+void BrowserProcessPlatformPartBase::PreMainMessageLoopRun() {
+}
diff --git a/chrome/browser/browser_process_platform_part_base.h b/chrome/browser/browser_process_platform_part_base.h
index 1494abc..abf35d3 100644
--- a/chrome/browser/browser_process_platform_part_base.h
+++ b/chrome/browser/browser_process_platform_part_base.h
@@ -27,6 +27,9 @@
   // Called from AttemptExitInternal().
   virtual void AttemptExit();
 
+  // Called at the end of BrowserProcessImpl::PreMainMessageLoopRun().
+  virtual void PreMainMessageLoopRun();
+
  private:
   DISALLOW_COPY_AND_ASSIGN(BrowserProcessPlatformPartBase);
 };
diff --git a/chrome/browser/browser_process_platform_part_mac.h b/chrome/browser/browser_process_platform_part_mac.h
index 2077fef..20d2645 100644
--- a/chrome/browser/browser_process_platform_part_mac.h
+++ b/chrome/browser/browser_process_platform_part_mac.h
@@ -6,17 +6,31 @@
 #define CHROME_BROWSER_BROWSER_PROCESS_PLATFORM_PART_MAC_H_
 
 #include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
 #include "chrome/browser/browser_process_platform_part_base.h"
 
+class AppShimHostManager;
+
+namespace apps {
+class ExtensionAppShimHandler;
+}
+
 class BrowserProcessPlatformPart : public BrowserProcessPlatformPartBase {
  public:
   BrowserProcessPlatformPart();
   virtual ~BrowserProcessPlatformPart();
 
   // Overridden from BrowserProcessPlatformPartBase:
+  virtual void StartTearDown() OVERRIDE;
   virtual void AttemptExit() OVERRIDE;
+  virtual void PreMainMessageLoopRun() OVERRIDE;
+
+  AppShimHostManager* app_shim_host_manager();
 
  private:
+  // Hosts the IPC channel factory that App Shims connect to on Mac.
+  scoped_ptr<AppShimHostManager> app_shim_host_manager_;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserProcessPlatformPart);
 };
 
diff --git a/chrome/browser/browser_process_platform_part_mac.mm b/chrome/browser/browser_process_platform_part_mac.mm
index a279de6..c83cd5b 100644
--- a/chrome/browser/browser_process_platform_part_mac.mm
+++ b/chrome/browser/browser_process_platform_part_mac.mm
@@ -3,7 +3,10 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/browser_process_platform_part_mac.h"
+
+#include "apps/app_shim/app_shim_host_manager_mac.h"
 #include "chrome/browser/chrome_browser_application_mac.h"
+#include "chrome/browser/ui/app_list/app_list_service.h"
 
 BrowserProcessPlatformPart::BrowserProcessPlatformPart() {
 }
@@ -11,9 +14,22 @@
 BrowserProcessPlatformPart::~BrowserProcessPlatformPart() {
 }
 
+void BrowserProcessPlatformPart::StartTearDown() {
+  app_shim_host_manager_.reset();
+}
+
 void BrowserProcessPlatformPart::AttemptExit() {
   // On the Mac, the application continues to run once all windows are closed.
   // Terminate will result in a CloseAllBrowsers() call, and once (and if)
   // that is done, will cause the application to exit cleanly.
   chrome_browser_application_mac::Terminate();
 }
+
+void BrowserProcessPlatformPart::PreMainMessageLoopRun() {
+  app_shim_host_manager_.reset(new AppShimHostManager);
+  AppListService::InitAll(NULL);
+}
+
+AppShimHostManager* BrowserProcessPlatformPart::app_shim_host_manager() {
+  return app_shim_host_manager_.get();
+}
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index cc669ab..7fb7013 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -117,6 +117,7 @@
         <include name="IDR_EXTENSION_INFO_JS" file="resources\extensions\extension_info.js" flattenhtml="true" type="BINDATA" />
         <include name="IDR_EXTENSIONS_JS" file="resources\extensions\extensions.js" flattenhtml="true" type="BINDATA" />
       </if>
+      <include name="IDR_FEEDBACK_MANIFEST" file="resources\feedback\manifest.json" type="BINDATA" />
       <include name="IDR_FLAGS_HTML" file="resources\flags.html" flattenhtml="true" type="BINDATA" />
       <include name="IDR_FLAGS_JS" file="resources\flags.js" type="BINDATA" />
       <include name="IDR_HELP_JS" file="resources\help\help.js" flattenhtml="true" type="BINDATA" />
@@ -242,9 +243,6 @@
         <include name="IDR_SUGGESTIONS_PAGE_JS" file="resources\ntp4\suggestions_page.js" type="BINDATA" />
       </if>
       <include name="IDR_TRANSLATE_JS" file="resources\translate.js" type="BINDATA" />
-      <include name="IDR_FEEDBACK_HTML" file="resources\feedback.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
-      <include name="IDR_FEEDBACK_HTML_INVALID" file="resources\feedback_invalid.html" allowexternalscript="true" flattenhtml="true" type="BINDATA" />
-      <include name="IDR_FEEDBACK_JS" file="resources\feedback.js" flattenhtml="true" type="BINDATA" />
       <include name="IDR_UBER_HTML" file="resources\uber\uber.html" flattenhtml="true" type="BINDATA" />
       <include name="IDR_UBER_JS" file="resources\uber\uber.js" type="BINDATA" />
       <include name="IDR_UBER_FRAME_HTML" file="resources\uber\uber_frame.html" flattenhtml="true" type="BINDATA" />
@@ -284,6 +282,7 @@
         <include name="IDR_CHROMEVOX_MANIFEST" file="resources\chromeos\access_chromevox\manifest.json" type="BINDATA" />
         <!-- manifest file of Connectivity Diagnostics app -->
         <include name="IDR_CONNECTIVITY_DIAGNOSTICS_MANIFEST" file="resources\chromeos\connectivity_diagnostics\manifest.json" type="BINDATA" />
+        <include name="IDR_CONNECTIVITY_DIAGNOSTICS_LAUNCHER_MANIFEST" file="resources\chromeos\connectivity_diagnostics_launcher\manifest.json" type="BINDATA" />
         <!-- manifest file of built-in speech synthesis extension -->
         <include name="IDR_SPEECH_SYNTHESIS_MANIFEST" file="resources\chromeos\speech_synthesis\manifest.json" type="BINDATA" />
         <include name="IDR_DIAGNOSTICS_MAIN_CSS" file="resources\chromeos\diagnostics\main.css" type="BINDATA" />
diff --git a/chrome/browser/browsing_data/browsing_data_file_system_helper.cc b/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
index 7f7a4d8..884deea 100644
--- a/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
+++ b/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
@@ -15,7 +15,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_quota_util.h"
-#include "webkit/browser/fileapi/file_system_task_runners.h"
 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
 #include "webkit/common/fileapi/file_system_types.h"
 
@@ -57,7 +56,7 @@
 
   // Returns the file task runner for the |filesystem_context_|.
   base::SequencedTaskRunner* file_task_runner() {
-    return filesystem_context_->task_runners()->file_task_runner();
+    return filesystem_context_->default_file_task_runner();
   }
 
   // Keep a reference to the FileSystemContext object for the current profile
diff --git a/chrome/browser/browsing_data/browsing_data_file_system_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_file_system_helper_unittest.cc
index 4c9f586..6f86f0e 100644
--- a/chrome/browser/browsing_data/browsing_data_file_system_helper_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_file_system_helper_unittest.cc
@@ -18,7 +18,6 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_url.h"
-#include "webkit/browser/fileapi/file_system_usage_cache.h"
 #include "webkit/common/fileapi/file_system_types.h"
 
 using content::BrowserContext;
diff --git a/chrome/browser/browsing_data/browsing_data_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
index 4901f64..2186b44 100644
--- a/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
@@ -77,7 +77,7 @@
   EXPECT_FALSE(IsWebScheme(chrome::kJavaScriptScheme));
   EXPECT_FALSE(IsWebScheme(chrome::kMailToScheme));
   EXPECT_FALSE(IsWebScheme(chrome::kMetadataScheme));
-  EXPECT_FALSE(IsWebScheme(chrome::kSwappedOutScheme));
+  EXPECT_FALSE(IsWebScheme(content::kSwappedOutScheme));
   EXPECT_FALSE(IsWebScheme(content::kViewSourceScheme));
 }
 
@@ -102,7 +102,7 @@
   EXPECT_FALSE(IsExtensionScheme(chrome::kJavaScriptScheme));
   EXPECT_FALSE(IsExtensionScheme(chrome::kMailToScheme));
   EXPECT_FALSE(IsExtensionScheme(chrome::kMetadataScheme));
-  EXPECT_FALSE(IsExtensionScheme(chrome::kSwappedOutScheme));
+  EXPECT_FALSE(IsExtensionScheme(content::kSwappedOutScheme));
   EXPECT_FALSE(IsExtensionScheme(content::kViewSourceScheme));
 }
 
diff --git a/chrome/browser/browsing_data/browsing_data_remover.cc b/chrome/browser/browsing_data/browsing_data_remover.cc
index 15b8959..fb6403c 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover.cc
@@ -849,8 +849,10 @@
 void BrowsingDataRemover::ClearShaderCacheOnUIThread() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  BrowserContext::GetDefaultStoragePartition(profile_)->AsyncClearDataBetween(
-      content::StoragePartition::kShaderStorage, delete_begin_, delete_end_,
+  BrowserContext::GetDefaultStoragePartition(profile_)->ClearDataForRange(
+      content::StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE,
+      content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
+      delete_begin_, delete_end_,
       base::Bind(&BrowsingDataRemover::ClearedShaderCache,
                  base::Unretained(this)));
 }
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 8b71ef3..e7f5193 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -226,8 +226,7 @@
 // |local_state_task_runner| must be a shutdown-blocking task runner.
 PrefService* InitializeLocalState(
     base::SequencedTaskRunner* local_state_task_runner,
-    const CommandLine& parsed_command_line,
-    bool is_first_run) {
+    const CommandLine& parsed_command_line) {
   TRACE_EVENT0("startup", "ChromeBrowserMainParts::InitializeLocalState")
   base::FilePath local_state_path;
   PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path);
@@ -238,8 +237,8 @@
   PrefService* local_state = g_browser_process->local_state();
   DCHECK(local_state);
 
-  if (is_first_run) {
 #if defined(OS_WIN)
+  if (first_run::IsChromeFirstRun()) {
     // During first run we read the google_update registry key to find what
     // language the user selected when downloading the installer. This
     // becomes our default language in the prefs.
@@ -249,8 +248,8 @@
       local_state->SetString(prefs::kApplicationLocale,
                              WideToASCII(install_lang));
     }
-#endif  // defined(OS_WIN)
   }
+#endif  // defined(OS_WIN)
 
   // If the local state file for the current profile doesn't exist and the
   // parent profile command line flag is present, then we should inherit some
@@ -552,7 +551,6 @@
       profile_(NULL),
       run_message_loop_(true),
       notify_result_(ProcessSingleton::PROCESS_NONE),
-      do_first_run_tasks_(false),
       local_state_(NULL),
       restart_last_session_(false) {
   // If we're running tests (ui_task is non-null).
@@ -683,12 +681,14 @@
   return enabled;
 }
 
-void ChromeBrowserMainParts::RecordBrowserStartupTime(bool is_first_run) {
+void ChromeBrowserMainParts::RecordBrowserStartupTime() {
   // Don't record any metrics if UI was displayed before this point e.g.
   // warning dialogs.
   if (startup_metric_utils::WasNonBrowserUIDisplayed())
     return;
 
+  bool is_first_run = first_run::IsChromeFirstRun();
+
 // CurrentProcessInfo::CreationTime() is currently only implemented on Mac and
 // Windows.
 #if defined(OS_MACOSX) || defined(OS_WIN)
@@ -823,22 +823,13 @@
   // Force MediaCaptureDevicesDispatcher to be created on UI thread.
   MediaCaptureDevicesDispatcher::GetInstance();
 
-  // Whether this is First Run. |do_first_run_tasks_| should be prefered to this
-  // unless the desire is actually to know whether this is really First Run
-  // (i.e., even if --no-first-run is passed).
-  bool is_first_run = false;
   // Android's first run is done in Java instead of native.
 #if !defined(OS_ANDROID)
   process_singleton_.reset(new ChromeProcessSingleton(
       user_data_dir_, base::Bind(&ProcessSingletonNotificationCallback)));
 
-  bool force_first_run =
-      parsed_command_line().HasSwitch(switches::kForceFirstRun);
-  bool force_skip_first_run_tasks =
-      (!force_first_run &&
-       parsed_command_line().HasSwitch(switches::kNoFirstRun));
-
-  is_first_run = force_first_run || first_run::IsChromeFirstRun();
+  // Cache first run state early.
+  first_run::IsChromeFirstRun();
 #endif
 
   scoped_refptr<base::SequencedTaskRunner> local_state_task_runner =
@@ -876,7 +867,7 @@
   }
 
   local_state_ = InitializeLocalState(
-      local_state_task_runner.get(), parsed_command_line(), is_first_run);
+      local_state_task_runner.get(), parsed_command_line());
 
   // These members must be initialized before returning from this function.
   master_prefs_.reset(new first_run::MasterPrefs);
@@ -965,66 +956,50 @@
 #endif
 
   // Android does first run in Java instead of native.
-#if !defined(OS_ANDROID)
+  // Chrome OS has its own out-of-box-experience code.
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
   // On first run, we need to process the predictor preferences before the
   // browser's profile_manager object is created, but after ResourceBundle
   // is initialized.
-  if (is_first_run) {
+  if (first_run::IsChromeFirstRun()) {
     first_run::ProcessMasterPreferencesResult pmp_result =
         first_run::ProcessMasterPreferences(user_data_dir_,
                                             master_prefs_.get());
     if (pmp_result == first_run::EULA_EXIT_NOW)
       return chrome::RESULT_CODE_EULA_REFUSED;
 
-    // Do first run tasks unless:
-    //  - Explicitly forced not to by |force_skip_first_run_tasks| or
-    //    |pmp_result|.
-    //  - Running in App (or App Launcher) mode, where showing the importer
-    //    (first run) UI is undesired.
-    do_first_run_tasks_ = (
-        !force_skip_first_run_tasks &&
-        pmp_result != first_run::SKIP_FIRST_RUN_TASKS &&
-        !parsed_command_line().HasSwitch(switches::kApp) &&
+    if (!parsed_command_line().HasSwitch(switches::kApp) &&
         !parsed_command_line().HasSwitch(switches::kAppId) &&
-        !parsed_command_line().HasSwitch(switches::kShowAppList));
-
-    if (do_first_run_tasks_) {
+        !parsed_command_line().HasSwitch(switches::kShowAppList)) {
       AddFirstRunNewTabs(browser_creator_.get(), master_prefs_->new_tabs);
-
-      // TODO(macourteau): refactor preferences that are copied from
-      // master_preferences into local_state, as a "local_state" section in
-      // master preferences. If possible, a generic solution would be prefered
-      // over a copy one-by-one of specific preferences. Also see related TODO
-      // in first_run.h.
-
-      // Store the initial VariationsService seed in local state, if it exists
-      // in master prefs.
-      if (!master_prefs_->variations_seed.empty()) {
-        local_state_->SetString(prefs::kVariationsSeed,
-                                master_prefs_->variations_seed);
-        // Set the variation seed date to the current system time. If the user's
-        // clock is incorrect, this may cause some field trial expiry checks to
-        // not do the right thing until the next seed update from the server,
-        // when this value will be updated.
-        local_state_->SetInt64(prefs::kVariationsSeedDate,
-                               base::Time::Now().ToInternalValue());
-      }
-
-      if (!master_prefs_->suppress_default_browser_prompt_for_version.empty()) {
-        local_state_->SetString(
-            prefs::kBrowserSuppressDefaultBrowserPrompt,
-            master_prefs_->suppress_default_browser_prompt_for_version);
-      }
-
-      AppListService::Get()->HandleFirstRun();
     }
 
-    if (do_first_run_tasks_ ||
-        parsed_command_line().HasSwitch(switches::kNoFirstRun)) {
-      // Create the First Run sentinel whether first run tasks are executed
-      // or not.
-      first_run::CreateSentinel();
+    // TODO(macourteau): refactor preferences that are copied from
+    // master_preferences into local_state, as a "local_state" section in
+    // master preferences. If possible, a generic solution would be prefered
+    // over a copy one-by-one of specific preferences. Also see related TODO
+    // in first_run.h.
+
+    // Store the initial VariationsService seed in local state, if it exists
+    // in master prefs.
+    if (!master_prefs_->variations_seed.empty()) {
+      local_state_->SetString(prefs::kVariationsSeed,
+                              master_prefs_->variations_seed);
+      // Set the variation seed date to the current system time. If the user's
+      // clock is incorrect, this may cause some field trial expiry checks to
+      // not do the right thing until the next seed update from the server,
+      // when this value will be updated.
+      local_state_->SetInt64(prefs::kVariationsSeedDate,
+                             base::Time::Now().ToInternalValue());
     }
+
+    if (!master_prefs_->suppress_default_browser_prompt_for_version.empty()) {
+      local_state_->SetString(
+          prefs::kBrowserSuppressDefaultBrowserPrompt,
+          master_prefs_->suppress_default_browser_prompt_for_version);
+    }
+
+    AppListService::Get()->HandleFirstRun();
   }
 #endif
 
@@ -1263,6 +1238,8 @@
         NOTREACHED();
     }
   }
+
+  first_run::CreateSentinelIfNeeded();
 #endif  // !defined(OS_ANDROID)
 
   // Desktop construction occurs here, (required before profile creation).
@@ -1356,13 +1333,13 @@
   // Create an instance of GpuModeManager to watch gpu mode pref change.
   g_browser_process->gpu_mode_manager();
 
-#if !defined(OS_ANDROID)
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
   // Show the First Run UI if this is the first time Chrome has been run on
   // this computer, or we're being compelled to do so by a command line flag.
   // Note that this be done _after_ the PrefService is initialized and all
   // preferences are registered, since some of the code that the importer
   // touches reads preferences.
-  if (do_first_run_tasks_) {
+  if (first_run::IsChromeFirstRun()) {
     first_run::AutoImport(profile_,
                           master_prefs_->homepage_defined,
                           master_prefs_->do_import_items,
@@ -1378,8 +1355,8 @@
     } else {
       browser_creator_->set_is_default_browser_dialog_suppressed(true);
     }
-  }  // if (do_first_run_tasks_)
-#endif  // !defined(OS_ANDROID)
+  }
+#endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 
 #if defined(OS_WIN)
   // Sets things up so that if we crash from this point on, a dialog will
@@ -1412,12 +1389,12 @@
   // file thread to be run sometime later. If this is the first run we record
   // the installation event.
   PrefService* pref_service = profile_->GetPrefs();
-  int ping_delay = do_first_run_tasks_ ? master_prefs_->ping_delay :
+  int ping_delay = first_run::IsChromeFirstRun() ? master_prefs_->ping_delay :
       pref_service->GetInteger(first_run::GetPingDelayPrefName().c_str());
   // Negative ping delay means to send ping immediately after a first search is
   // recorded.
   RLZTracker::InitRlzFromProfileDelayed(
-      profile_, do_first_run_tasks_, ping_delay < 0,
+      profile_, first_run::IsChromeFirstRun(), ping_delay < 0,
       base::TimeDelta::FromMilliseconds(abs(ping_delay)));
 #endif  // defined(ENABLE_RLZ) && !defined(OS_CHROMEOS)
 
@@ -1640,7 +1617,7 @@
   // These should be invoked as close to the start of the browser's
   // UI thread message loop as possible to get a stable measurement
   // across versions.
-  RecordBrowserStartupTime(do_first_run_tasks_);
+  RecordBrowserStartupTime();
   startup_timer_->SignalStartupComplete(
       performance_monitor::StartupTimer::STARTUP_NORMAL);
 
diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h
index 0e092e4..10c1d8e 100644
--- a/chrome/browser/chrome_browser_main.h
+++ b/chrome/browser/chrome_browser_main.h
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "content/public/browser/browser_main_parts.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/common/main_function_params.h"
 
 class ActiveTabTracker;
 class BrowserProcessImpl;
@@ -41,10 +42,6 @@
 class TrackingSynchronizer;
 }
 
-namespace content {
-struct MainFunctionParams;
-}
-
 namespace performance_monitor {
 class StartupTimer;
 }
@@ -101,7 +98,6 @@
   }
 
   Profile* profile() { return profile_; }
-  bool do_first_run_tasks() const { return do_first_run_tasks_; }
 
   const PrefService* local_state() const { return local_state_; }
 
@@ -121,8 +117,7 @@
   bool IsMetricsReportingEnabled();
 
   // Record time from process startup to present time in an UMA histogram.
-  // |is_first_run| - is the current launch part of a first run.
-  void RecordBrowserStartupTime(bool is_first_run);
+  void RecordBrowserStartupTime();
 
   // Records a time value to an UMA histogram in the context of the
   // PreReadExperiment field-trial. This also reports to the appropriate
@@ -136,7 +131,7 @@
 
   // Members initialized on construction ---------------------------------------
 
-  const content::MainFunctionParams& parameters_;
+  const content::MainFunctionParams parameters_;
   const CommandLine& parsed_command_line_;
   int result_code_;
 
@@ -195,7 +190,6 @@
 
   // Members initialized in PreMainMessageLoopRun, needed in
   // PreMainMessageLoopRunThreadsCreated.
-  bool do_first_run_tasks_;
   PrefService* local_state_;
   base::FilePath user_data_dir_;
 
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index a2e3008..b9ef210 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -22,15 +22,9 @@
 #include "base/win/windows_version.h"
 #include "base/win/wrapped_window_proc.h"
 #include "chrome/browser/browser_util_win.h"
-#include "chrome/browser/first_run/first_run.h"
-#include "chrome/browser/metrics/metrics_service.h"
 #include "chrome/browser/profiles/profile_info_cache.h"
 #include "chrome/browser/profiles/profile_shortcut_manager.h"
 #include "chrome/browser/shell_integration.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_prepopulate_data.h"
-#include "chrome/browser/search_engines/template_url_service.h"
-#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/simple_message_box.h"
 #include "chrome/browser/ui/uninstall_browser_prompt.h"
 #include "chrome/common/chrome_constants.h"
@@ -208,27 +202,6 @@
   return rv;
 }
 
-void ChromeBrowserMainPartsWin::PostMainMessageLoopRun() {
-  // Log the search engine chosen on first run. Do this at shutdown, after any
-  // changes are made from the first run bubble link, etc.
-  if (do_first_run_tasks() && profile() && !profile()->IsOffTheRecord()) {
-    TemplateURLService* url_service =
-        TemplateURLServiceFactory::GetForProfile(profile());
-    const TemplateURL* default_search_engine =
-        url_service->GetDefaultSearchProvider();
-    // The default engine can be NULL if the administrator has disabled
-    // default search.
-    SearchEngineType search_engine_type =
-        TemplateURLPrepopulateData::GetEngineType(default_search_engine ?
-            default_search_engine->url() : std::string());
-    // Record the search engine chosen.
-    UMA_HISTOGRAM_ENUMERATION("Chrome.SearchSelectExempt", search_engine_type,
-                              SEARCH_ENGINE_MAX);
-  }
-
-  ChromeBrowserMainParts::PostMainMessageLoopRun();
-}
-
 void ChromeBrowserMainPartsWin::ShowMissingLocaleMessageBox() {
   ui::MessageBox(NULL, ASCIIToUTF16(chrome_browser::kMissingLocaleDataMessage),
                  ASCIIToUTF16(chrome_browser::kMissingLocaleDataTitle),
diff --git a/chrome/browser/chrome_browser_main_win.h b/chrome/browser/chrome_browser_main_win.h
index 496d234..f3e7c78 100644
--- a/chrome/browser/chrome_browser_main_win.h
+++ b/chrome/browser/chrome_browser_main_win.h
@@ -27,7 +27,6 @@
   virtual void ToolkitInitialized() OVERRIDE;
   virtual void PreMainMessageLoopStart() OVERRIDE;
   virtual int PreCreateThreads() OVERRIDE;
-  virtual void PostMainMessageLoopRun() OVERRIDE;
 
   // ChromeBrowserMainParts overrides.
   virtual void ShowMissingLocaleMessageBox() OVERRIDE;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 86e7584..dda99ec 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -76,7 +76,6 @@
 #include "chrome/browser/search_engines/search_provider_install_state_message_filter.h"
 #include "chrome/browser/speech/chrome_speech_recognition_manager_delegate.h"
 #include "chrome/browser/speech/tts_message_filter.h"
-#include "chrome/browser/spellchecker/spellcheck_message_filter.h"
 #include "chrome/browser/ssl/ssl_add_certificate.h"
 #include "chrome/browser/ssl/ssl_blocking_page.h"
 #include "chrome/browser/ssl/ssl_tab_helper.h"
@@ -139,6 +138,7 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/message_center/message_center_util.h"
 #include "webkit/browser/fileapi/external_mount_points.h"
+#include "webkit/browser/fileapi/syncable/sync_file_system_backend.h"
 #include "webkit/common/webpreferences.h"
 #include "webkit/plugins/plugin_switches.h"
 
@@ -228,6 +228,10 @@
 #include "chrome/browser/chrome_browser_main_extra_parts_x11.h"
 #endif
 
+#if defined(ENABLE_SPELLCHECK)
+#include "chrome/browser/spellchecker/spellcheck_message_filter.h"
+#endif
+
 using WebKit::WebWindowFeatures;
 using base::FileDescriptor;
 using content::AccessTokenStore;
@@ -855,7 +859,10 @@
   const Extension* extension =
       service->extensions()->GetExtensionOrAppByURL(url);
   if (!extension) {
-    NOTREACHED();
+    // It's ok to return here, since we could be running a browser plugin
+    // outside an extension, and don't need to attach a
+    // BrowserPluginGuestDelegate in that case;
+    // e.g. running with flag --enable-browser-plugin-for-all-view-types.
     return;
   }
 
@@ -887,7 +894,9 @@
 #endif
   host->GetChannel()->AddFilter(
       new SearchProviderInstallStateMessageFilter(id, profile));
+#if defined(ENABLE_SPELLCHECK)
   host->GetChannel()->AddFilter(new SpellCheckMessageFilter(id));
+#endif
 #if defined(OS_MACOSX)
   host->GetChannel()->AddFilter(new SpellCheckMessageFilterMac(id));
 #endif
@@ -922,42 +931,17 @@
   host->Send(new ChromeViewMsg_SetContentSettingRules(rules));
 }
 
-GURL ChromeContentBrowserClient::GetPossiblyPrivilegedURL(
-    content::BrowserContext* browser_context,
-    const GURL& url,
-    bool is_renderer_initiated,
-    content::SiteInstance* current_instance) {
-  Profile* profile = Profile::FromBrowserContext(browser_context);
-  if (!profile)
-    return url;
-
-  // Only return the privileged instant URL if we are entering from a browser-
-  // initiated navigation or if we are already in the instant process.
-  bool is_instant_process = false;
-  int process_id = current_instance->GetProcess()->GetID();
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(profile);
-  if (instant_service)
-    is_instant_process = instant_service->IsInstantProcess(process_id);
-
-  DCHECK_EQ(is_instant_process,
-            chrome::IsPrivilegedURLForInstant(current_instance->GetSiteURL()));
-  if (!is_renderer_initiated || is_instant_process) {
-    // If the input |url| should be assigned to the Instant renderer, make its
-    // privileged URL distinct from other URLs on the search provider's domain.
-    if (chrome::ShouldAssignURLToInstantRenderer(url, profile))
-      return chrome::GetPrivilegedURLForInstant(url, profile);
-  }
-
-  return url;
-}
-
 GURL ChromeContentBrowserClient::GetEffectiveURL(
     content::BrowserContext* browser_context, const GURL& url) {
   Profile* profile = Profile::FromBrowserContext(browser_context);
   if (!profile)
     return url;
 
+  // If the input |url| should be assigned to the Instant renderer, make its
+  // effective URL distinct from other URLs on the search provider's domain.
+  if (chrome::ShouldAssignURLToInstantRenderer(url, profile))
+    return chrome::GetEffectiveURLForInstant(url, profile);
+
 #if !defined(OS_CHROMEOS)
   // If the input |url| should be assigned to the Signin renderer, make its
   // effective URL distinct from other URLs on the signin service's domain.
@@ -1454,6 +1438,7 @@
       autofill::switches::kEnableInteractiveAutocomplete,
       extensions::switches::kAllowLegacyExtensionManifests,
       extensions::switches::kAllowScriptingGallery,
+      extensions::switches::kEnableExperimentalExtensionApis,
       extensions::switches::kExtensionsOnChromeURLs,
       switches::kAllowHTTPBackgroundPage,
       // TODO(victorhsieh): remove the following flag once we move PPAPI FileIO
@@ -1471,7 +1456,6 @@
       switches::kEnableAdviewSrcAttribute,
       switches::kEnableAppWindowControls,
       switches::kEnableBenchmarking,
-      switches::kEnableExperimentalExtensionApis,
       switches::kEnableIPCFuzzing,
       switches::kEnableNaCl,
       switches::kEnableNetBenchmarking,
@@ -1498,9 +1482,9 @@
                                    arraysize(kSwitchNames));
   } else if (process_type == switches::kUtilityProcess) {
     static const char* const kSwitchNames[] = {
+      extensions::switches::kEnableExperimentalExtensionApis,
       extensions::switches::kExtensionsOnChromeURLs,
       switches::kAllowHTTPBackgroundPage,
-      switches::kEnableExperimentalExtensionApis,
       switches::kWhitelistedExtensionID,
     };
 
@@ -2186,9 +2170,6 @@
       static_cast<float>(prefs->GetDouble(prefs::kWebKitFontScaleFactor));
   web_prefs->force_enable_zoom =
       prefs->GetBoolean(prefs::kWebKitForceEnableZoom);
-#if defined(GOOGLE_TV)
-  web_prefs->user_gesture_required_for_media_playback = false;
-#endif
 #endif
 
 #if defined(OS_ANDROID)
@@ -2435,12 +2416,6 @@
 #endif
 }
 
-base::FilePath ChromeContentBrowserClient::GetHyphenDictionaryDirectory() {
-  base::FilePath directory;
-  PathService::Get(chrome::DIR_APP_DICTIONARIES, &directory);
-  return directory.Append(FILE_PATH_LITERAL("Hyphen"));
-}
-
 ui::SelectFilePolicy* ChromeContentBrowserClient::CreateSelectFilePolicy(
     WebContents* web_contents) {
   return new ChromeSelectFilePolicy(web_contents);
@@ -2479,6 +2454,8 @@
   DCHECK(backend->CanHandleType(fileapi::kFileSystemTypeExternal));
   additional_backends->push_back(backend);
 #endif
+
+  additional_backends->push_back(new sync_file_system::SyncFileSystemBackend());
 }
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 8c6435e..f7dc4ee 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -73,11 +73,6 @@
       content::RenderProcessHost* host) OVERRIDE;
   virtual bool ShouldUseProcessPerSite(content::BrowserContext* browser_context,
                                        const GURL& effective_url) OVERRIDE;
-  virtual GURL GetPossiblyPrivilegedURL(
-      content::BrowserContext* browser_context,
-      const GURL& url,
-      bool is_renderer_initiated,
-      content::SiteInstance* current_instance) OVERRIDE;
   virtual GURL GetEffectiveURL(content::BrowserContext* browser_context,
                                const GURL& url) OVERRIDE;
   virtual void GetAdditionalWebUISchemes(
@@ -244,7 +239,6 @@
       const GURL& url,
       bool private_api,
       const content::SocketPermissionRequest& params) OVERRIDE;
-  virtual base::FilePath GetHyphenDictionaryDirectory() OVERRIDE;
   virtual ui::SelectFilePolicy* CreateSelectFilePolicy(
       content::WebContents* web_contents) OVERRIDE;
   virtual void GetAdditionalAllowedSchemesForFileSystem(
diff --git a/chrome/browser/chrome_main_browsertest.cc b/chrome/browser/chrome_main_browsertest.cc
index d8cbfd6..30bcf71 100644
--- a/chrome/browser/chrome_main_browsertest.cc
+++ b/chrome/browser/chrome_main_browsertest.cc
@@ -15,6 +15,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
@@ -22,10 +23,6 @@
 #include "content/public/browser/web_contents.h"
 #include "net/base/net_util.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 // These tests don't apply to the Mac version; see GetCommandLineForRelaunch
 // for details.
 #if !defined(OS_MACOSX)
@@ -43,7 +40,7 @@
 IN_PROC_BROWSER_TEST_F(ChromeMainTest, SecondLaunch) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -57,7 +54,7 @@
 IN_PROC_BROWSER_TEST_F(ChromeMainTest, ReuseBrowserInstanceWhenOpeningFile) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -112,7 +109,7 @@
 IN_PROC_BROWSER_TEST_F(ChromeMainTest, SecondLaunchFromIncognitoWithNormalUrl) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/chrome_notification_types.h b/chrome/browser/chrome_notification_types.h
index 381252e..326ba22 100644
--- a/chrome/browser/chrome_notification_types.h
+++ b/chrome/browser/chrome_notification_types.h
@@ -532,6 +532,12 @@
   // removed the extension from its internal state.
   NOTIFICATION_EXTENSION_UNLOADED,
 
+  // Sent when an Extension object is removed from ExtensionService. This
+  // can happen when an extension is uninstalled, upgraded, or blacklisted,
+  // including all cases when the Extension is deleted. The details are an
+  // Extension, and the source is a Profile.
+  NOTIFICATION_EXTENSION_REMOVED,
+
   // Sent after a new ExtensionHost is created. The details are
   // an ExtensionHost* and the source is a Profile*.
   NOTIFICATION_EXTENSION_HOST_CREATED,
@@ -670,7 +676,10 @@
   NOTIFICATION_EXTENSION_UPDATE_FOUND,
 
   // Component Updater -------------------------------------------------------
-
+  //
+  // The component updater notifications are deprecated and they will be
+  // removed soon.
+  //
   // Sent when the component updater starts doing update checks. If no
   // component has been registered for update this notification is not
   // generated. The source is the component updater itself and there are
@@ -1135,12 +1144,6 @@
   NOTIFICATION_ASH_SESSION_ENDED,
 #endif
 
-#if defined(OS_CHROMEOS)
-  // Sent when WebSocketProxy started accepting connections; details is integer
-  // port on which proxy is listening.
-  NOTIFICATION_WEB_SOCKET_PROXY_STARTED,
-#endif
-
   // Sent when a new web store promo has been loaded.
   NOTIFICATION_WEB_STORE_PROMO_LOADED,
 
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/kiosk_app_launcher.cc
index 174cffd..a2cac72 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_launcher.cc
@@ -26,6 +26,12 @@
 
 namespace chromeos {
 
+namespace {
+
+void IgnoreResult(bool mount_success, cryptohome::MountError mount_error) {}
+
+}  // namespace
+
 // static
 KioskAppLauncher* KioskAppLauncher::running_instance_ = NULL;
 
@@ -101,11 +107,9 @@
  public:
   ProfileLoader(KioskAppManager* kiosk_app_manager,
                 KioskAppLauncher* kiosk_app_launcher)
-      : kiosk_app_launcher_(kiosk_app_launcher) {
-    KioskAppManager::App app;
-    if (!kiosk_app_manager->GetApp(kiosk_app_launcher->app_id_, &app))
-      NOTREACHED() << "Logging into nonexistent kiosk-app account.";
-    user_id_ = app.user_id;
+      : kiosk_app_launcher_(kiosk_app_launcher),
+        user_id_(kiosk_app_launcher->user_id_) {
+    CHECK(!user_id_.empty());
   }
 
   virtual ~ProfileLoader() {
@@ -160,6 +164,9 @@
     : kiosk_app_manager_(kiosk_app_manager),
       app_id_(app_id),
       remove_attempted_(false) {
+  KioskAppManager::App app;
+  CHECK(kiosk_app_manager_->GetApp(app_id_, &app));
+  user_id_ = app.user_id;
 }
 
 KioskAppLauncher::~KioskAppLauncher() {}
@@ -200,12 +207,14 @@
 }
 
 void KioskAppLauncher::StartMount() {
-  const std::string token =
-      CryptohomeLibrary::Get()->EncryptWithSystemSalt(app_id_);
-
-  cryptohome::AsyncMethodCaller::GetInstance()->AsyncMount(
+  // Nuke old home that uses |app_id_| as cryptohome user id.
+  // TODO(xiyuan): Remove this after all clients migrated to new home.
+  cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
       app_id_,
-      token,
+      base::Bind(&IgnoreResult));
+
+  cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic(
+      user_id_,
       cryptohome::CREATE_IF_MISSING,
       base::Bind(&KioskAppLauncher::MountCallback,
                  base::Unretained(this)));
@@ -234,7 +243,7 @@
 
 void KioskAppLauncher::AttemptRemove() {
   cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
-      app_id_,
+      user_id_,
       base::Bind(&KioskAppLauncher::RemoveCallback,
                  base::Unretained(this)));
 }
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_launcher.h b/chrome/browser/chromeos/app_mode/kiosk_app_launcher.h
index 44f9946..5b9908f 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_launcher.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_launcher.h
@@ -57,6 +57,7 @@
 
   KioskAppManager* kiosk_app_manager_;
   const std::string app_id_;
+  std::string user_id_;
 
   scoped_ptr<CryptohomedChecker> crytohomed_checker;
   scoped_ptr<ProfileLoader> profile_loader_;
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
index 8d81e81..922e186 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_data.h"
-#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
 #include "chrome/browser/chromeos/policy/device_local_account.h"
@@ -146,7 +145,7 @@
 
 void KioskAppManager::OnOwnerFileChecked(
     const KioskAppManager::GetConsumerKioskModeStatusCallback& callback,
-    bool *owner_present) {
+    bool* owner_present) {
   ownership_established_ = *owner_present;
 
   if (callback.is_null())
@@ -383,7 +382,8 @@
        it != old_apps.end(); ++it) {
     it->second->ClearCache();
     cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
-        it->first, base::Bind(&OnRemoveAppCryptohomeComplete, it->first));
+        it->second->user_id(),
+        base::Bind(&OnRemoveAppCryptohomeComplete, it->first));
   }
   STLDeleteValues(&old_apps);
 
diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
index b0ee4e9..8308bf0 100644
--- a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
@@ -20,6 +20,8 @@
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/extensions/webstore_startup_installer.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/signin/profile_oauth2_token_service.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/token_service.h"
 #include "chrome/browser/signin/token_service_factory.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
@@ -28,9 +30,7 @@
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/manifest_handlers/kiosk_enabled_info.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "google_apis/gaia/gaia_constants.h"
 
@@ -150,23 +150,30 @@
 void StartupAppLauncher::InitializeTokenService() {
   chromeos::UpdateAppLaunchSplashScreenState(
       chromeos::APP_LAUNCH_STATE_LOADING_TOKEN_SERVICE);
-  TokenService* token_service =
-      TokenServiceFactory::GetForProfile(profile_);
-  if (token_service->HasOAuthLoginToken()) {
+  ProfileOAuth2TokenService* profile_token_service =
+      ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
+  if (profile_token_service->RefreshTokenIsAvailable()) {
     InitializeNetwork();
     return;
   }
 
-  registrar_.Add(this,
-                 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED,
-                 content::Source<TokenService>(token_service));
-  registrar_.Add(this,
-                 chrome::NOTIFICATION_TOKEN_AVAILABLE,
-                 content::Source<TokenService>(token_service));
+  // At the end of this method, the execution will be put on hold until
+  // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
+  // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
+  // whichever comes first, both handlers call RemoveObserver on PO2TS. Handling
+  // any of the two events is the only way to resume the execution and enable
+  // Cleanup method to be called, self-invoking a destructor. In destructor
+  // StartupAppLauncher is no longer an observer of PO2TS and there is no need
+  // to call RemoveObserver again.
+  profile_token_service->AddObserver(this);
 
+  TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
   token_service->Initialize(GaiaConstants::kChromeSource, profile_);
+
   // Pass oauth2 refresh token from the auth file.
   // TODO(zelidrag): We should probably remove this option after M27.
+  // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
+  // Unless the code is no longer needed.
   if (!auth_params_.refresh_token.empty()) {
     token_service->UpdateCredentialsWithOAuth2(
         GaiaAuthConsumer::ClientOAuthResult(
@@ -179,31 +186,17 @@
   }
 }
 
-void StartupAppLauncher::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  switch (type) {
-    case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: {
-      registrar_.RemoveAll();
-      InitializeNetwork();
-      break;
-    }
-    case chrome::NOTIFICATION_TOKEN_AVAILABLE: {
-      TokenService::TokenAvailableDetails* token_details =
-          content::Details<TokenService::TokenAvailableDetails>(
-              details).ptr();
-      if (token_details->service() ==
-              GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
-        registrar_.RemoveAll();
-        InitializeNetwork();
-      }
-      break;
-    }
-    default:
-      NOTREACHED();
-      break;
-  }
+void StartupAppLauncher::OnRefreshTokenAvailable(
+    const std::string& account_id) {
+  ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
+      ->RemoveObserver(this);
+  InitializeNetwork();
+}
+
+void StartupAppLauncher::OnRefreshTokensLoaded() {
+  ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
+      ->RemoveObserver(this);
+  InitializeNetwork();
 }
 
 void StartupAppLauncher::Cleanup() {
diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.h b/chrome/browser/chromeos/app_mode/startup_app_launcher.h
index a4bc841..ac7e5ae 100644
--- a/chrome/browser/chromeos/app_mode/startup_app_launcher.h
+++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.h
@@ -12,8 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
+#include "chrome/browser/signin/oauth2_token_service.h"
 #include "net/base/network_change_notifier.h"
 #include "ui/base/events/event_handler.h"
 
@@ -36,7 +35,7 @@
 // If anything goes wrong, it exits app mode and goes back to login screen.
 class StartupAppLauncher
     : public base::SupportsWeakPtr<StartupAppLauncher>,
-      public content::NotificationObserver,
+      public OAuth2TokenService::Observer,
       public net::NetworkChangeNotifier::NetworkChangeObserver,
       public ui::EventHandler {
  public:
@@ -75,10 +74,9 @@
   static void LoadOAuthFileOnBlockingPool(KioskOAuthParams* auth_params);
   void OnOAuthFileLoaded(KioskOAuthParams* auth_params);
 
-  // content::NotificationObserver overrides.
-  virtual void Observe(int type,
-                       const content::NotificationSource& source,
-                       const content::NotificationDetails& details) OVERRIDE;
+  // OAuth2TokenService::Observer overrides.
+  virtual void OnRefreshTokenAvailable(const std::string& account_id) OVERRIDE;
+  virtual void OnRefreshTokensLoaded() OVERRIDE;
 
   // net::NetworkChangeNotifier::NetworkChangeObserver overrides:
   virtual void OnNetworkChanged(
@@ -93,7 +91,6 @@
   int64 launch_splash_start_time_;
 
   scoped_refptr<extensions::WebstoreStandaloneInstaller> installer_;
-  content::NotificationRegistrar registrar_;
   base::OneShotTimer<StartupAppLauncher> network_wait_timer_;
   KioskOAuthParams auth_params_;
 
diff --git a/chrome/browser/chromeos/audio/audio_devices_pref_handler_impl.cc b/chrome/browser/chromeos/audio/audio_devices_pref_handler_impl.cc
index fcd602a..a027e41 100644
--- a/chrome/browser/chromeos/audio/audio_devices_pref_handler_impl.cc
+++ b/chrome/browser/chromeos/audio/audio_devices_pref_handler_impl.cc
@@ -175,9 +175,16 @@
   registry->RegisterDictionaryPref(prefs::kAudioDevicesVolumePercent);
   registry->RegisterDictionaryPref(prefs::kAudioDevicesMute);
 
-  // TODO(jennyz,rkc): Move the rest of the preferences registered by
-  // AudioPrefHandlerImpl::RegisterPrefs here once we remove the old audio
-  // handler code.
+  // Register the prefs backing the audio muting policies.
+  registry->RegisterBooleanPref(prefs::kAudioOutputAllowed, true);
+  // This pref has moved to the media subsystem but we should verify it is there
+  // before we use it.
+  registry->RegisterBooleanPref(::prefs::kAudioCaptureAllowed, true);
+
+  // Register the legacy audio prefs for migration.
+  registry->RegisterDoublePref(prefs::kAudioVolumePercent,
+                               kDefaultVolumeGainPercent);
+  registry->RegisterIntegerPref(prefs::kAudioMute, kPrefMuteOff);
 }
 
 // static
diff --git a/chrome/browser/chromeos/audio/audio_handler.cc b/chrome/browser/chromeos/audio/audio_handler.cc
deleted file mode 100644
index b45ea0d..0000000
--- a/chrome/browser/chromeos/audio/audio_handler.cc
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/chromeos/audio/audio_handler.h"
-
-#include <algorithm>
-#include <cmath>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/logging.h"
-#include "base/memory/singleton.h"
-#include "chrome/browser/browser_process.h"
-#if defined(USE_CRAS)
-#include "chrome/browser/chromeos/audio/audio_mixer_cras.h"
-#else
-#include "chrome/browser/chromeos/audio/audio_mixer_alsa.h"
-#endif
-#include "base/prefs/pref_registry_simple.h"
-#include "base/prefs/pref_service.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/common/pref_names.h"
-#include "chromeos/audio/audio_pref_handler.h"
-
-using std::max;
-using std::min;
-
-namespace chromeos {
-
-namespace {
-
-// Default value for unmuting, as a percent in the range [0.0, 100.0].
-// Used when sound is unmuted, but volume was less than kMuteThresholdPercent.
-const double kDefaultUnmuteVolumePercent = 4.0;
-
-// Volume value which should be considered as muted in range [0.0, 100.0].
-const double kMuteThresholdPercent = 1.0;
-
-static AudioHandler* g_audio_handler = NULL;
-
-}  // namespace
-
-// static
-void AudioHandler::Initialize(
-    scoped_refptr<AudioPrefHandler> audio_pref_handler) {
-  CHECK(!g_audio_handler);
-#if defined(USE_CRAS)
-  g_audio_handler = new AudioHandler(new AudioMixerCras(), audio_pref_handler);
-#else
-  g_audio_handler = new AudioHandler(new AudioMixerAlsa(), audio_pref_handler);
-#endif
-}
-
-// static
-void AudioHandler::Shutdown() {
-  // We may call Shutdown without calling Initialize, e.g. if we exit early.
-  if (g_audio_handler) {
-    delete g_audio_handler;
-    g_audio_handler = NULL;
-  }
-}
-
-// static
-void AudioHandler::InitializeForTesting(
-    AudioMixer* mixer,
-    scoped_refptr<AudioPrefHandler> audio_pref_handler) {
-  CHECK(!g_audio_handler);
-  g_audio_handler = new AudioHandler(mixer, audio_pref_handler);
-}
-
-// static
-AudioHandler* AudioHandler::GetInstance() {
-  VLOG_IF(1, !g_audio_handler)
-      << "AudioHandler::GetInstance() called with NULL global instance.";
-  return g_audio_handler;
-}
-
-double AudioHandler::GetVolumePercent() {
-  return mixer_->GetVolumePercent();
-}
-
-void AudioHandler::SetVolumePercent(double volume_percent) {
-  volume_percent = min(max(volume_percent, 0.0), 100.0);
-  if (volume_percent <= kMuteThresholdPercent)
-    volume_percent = 0.0;
-  if (IsMuted() && volume_percent > 0.0)
-    SetMuted(false);
-  if (!IsMuted() && volume_percent == 0.0)
-    SetMuted(true);
-  SetVolumePercentInternal(volume_percent, true);
-}
-
-void AudioHandler::SetVolumePercentInternal(double volume_percent,
-                                            bool notify) {
-  mixer_->SetVolumePercent(volume_percent);
-  audio_pref_handler_->SetOutputVolumeValue(volume_percent);
-  if (notify)
-    FOR_EACH_OBSERVER(VolumeObserver, volume_observers_, OnVolumeChanged());
-}
-
-void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) {
-  SetVolumePercent(mixer_->GetVolumePercent() + adjust_by_percent);
-}
-
-bool AudioHandler::IsMuted() {
-  return mixer_->IsMuted();
-}
-
-void AudioHandler::SetMuted(bool mute) {
-  SetMutedInternal(mute, true);
-}
-
-void AudioHandler::SetMutedInternal(bool mute, bool notify) {
-  if (!mixer_->IsMuteLocked()) {
-    mixer_->SetMuted(mute);
-    audio_pref_handler_->SetOutputMuteValue(mute);
-
-    if (!mute) {
-      if (GetVolumePercent() <= kMuteThresholdPercent) {
-        // Avoid the situation when sound has been unmuted, but the volume
-        // is set to a very low value, so user still can't hear any sound.
-        SetVolumePercentInternal(kDefaultUnmuteVolumePercent, notify);
-      }
-    }
-    muted_ = mute;
-    if (notify)
-      FOR_EACH_OBSERVER(VolumeObserver, volume_observers_, OnMuteToggled());
-  }
-}
-
-bool AudioHandler::IsCaptureMuted() {
-  return mixer_->IsCaptureMuted();
-}
-
-void AudioHandler::SetCaptureMuted(bool mute) {
-  if (!mixer_->IsCaptureMuteLocked())
-    mixer_->SetCaptureMuted(mute);
-}
-
-void AudioHandler::AddVolumeObserver(VolumeObserver* observer) {
-  volume_observers_.AddObserver(observer);
-}
-
-void AudioHandler::RemoveVolumeObserver(VolumeObserver* observer) {
-  volume_observers_.RemoveObserver(observer);
-}
-
-void AudioHandler::OnAudioPolicyPrefChanged() {
-  ApplyAudioPolicy();
-}
-
-AudioHandler::AudioHandler(AudioMixer* mixer,
-                           scoped_refptr<AudioPrefHandler> audio_pref_handler)
-    : mixer_(mixer),
-      muted_(false),
-      audio_pref_handler_(audio_pref_handler) {
-  mixer_->Init();
-
-  DCHECK(audio_pref_handler_.get());
-  audio_pref_handler_->AddAudioPrefObserver(this);
-  ApplyAudioPolicy();
-  SetMutedInternal(audio_pref_handler_->GetOutputMuteValue(), false);
-  SetVolumePercentInternal(audio_pref_handler->GetOutputVolumeValue(), false);
-}
-
-AudioHandler::~AudioHandler() {
-  mixer_.reset();
-  audio_pref_handler_->RemoveAudioPrefObserver(this);
-  audio_pref_handler_ = NULL;
-};
-
-void AudioHandler::ApplyAudioPolicy() {
-  mixer_->SetMuteLocked(false);
-  bool muted = false;
-  if (audio_pref_handler_->GetAudioOutputAllowedValue()) {
-    muted = audio_pref_handler_->GetOutputMuteValue();
-    mixer_->SetMuted(muted);
-  } else {
-    muted = true;
-    mixer_->SetMuted(muted);
-    mixer_->SetMuteLocked(true);
-  }
-  if (muted_ != muted) {
-    muted_ = muted;
-    FOR_EACH_OBSERVER(VolumeObserver, volume_observers_, OnMuteToggled());
-  }
-  mixer_->SetCaptureMuteLocked(false);
-  if (audio_pref_handler_->GetAudioCaptureAllowedValue()) {
-    mixer_->SetCaptureMuted(false);
-  } else {
-    mixer_->SetCaptureMuted(true);
-    mixer_->SetCaptureMuteLocked(true);
-  }
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/audio/audio_handler.h b/chrome/browser/chromeos/audio/audio_handler.h
deleted file mode 100644
index d2cb231..0000000
--- a/chrome/browser/chromeos/audio/audio_handler.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_HANDLER_H_
-#define CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_HANDLER_H_
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/observer_list.h"
-#include "base/threading/thread.h"
-#include "chromeos/audio/audio_pref_observer.h"
-
-template <typename T> struct DefaultSingletonTraits;
-
-class PrefChangeRegistrar;
-class PrefRegistrySimple;
-class PrefService;
-
-namespace chromeos {
-
-class AudioMixer;
-class AudioPrefHandler;
-
-// TODO(jennyz): crbug.com/233301.
-// Retire the old AudioHandler and the old audio library code once
-// the new audio dbus and handler code stabilizes.
-class AudioHandler : public AudioPrefObserver {
- public:
-  class VolumeObserver {
-   public:
-    virtual void OnVolumeChanged() = 0;
-    virtual void OnMuteToggled() = 0;
-   protected:
-    VolumeObserver() {}
-    virtual ~VolumeObserver() {}
-    DISALLOW_COPY_AND_ASSIGN(VolumeObserver);
-  };
-
-  static void Initialize(scoped_refptr<AudioPrefHandler> audio_pref_handler);
-  static void Shutdown();
-
-  // Same as Initialize but using the specified audio mixer.  It takes
-  // ownership of |mixer|.
-  static void InitializeForTesting(
-      AudioMixer* mixer,
-      scoped_refptr<AudioPrefHandler> audio_pref_handler);
-
-  // GetInstance returns NULL if not initialized or if already shutdown.
-  static AudioHandler* GetInstance();
-
-  // Gets volume level in our internal 0-100% range, 0 being pure silence.
-  double GetVolumePercent();
-
-  // Sets volume level from 0-100%. If less than kMuteThresholdPercent, then
-  // mutes the sound. If it was muted, and |volume_percent| is larger than
-  // the threshold, then the sound is unmuted.
-  void SetVolumePercent(double volume_percent);
-
-  // Adjusts volume up (positive percentage) or down (negative percentage).
-  void AdjustVolumeByPercent(double adjust_by_percent);
-
-  // Is the volume currently muted?
-  bool IsMuted();
-
-  // Mutes or unmutes all audio.
-  void SetMuted(bool do_mute);
-
-  // Is the capture volume currently muted?
-  bool IsCaptureMuted();
-
-  // Mutes or unmutes all capture devices. If unmutes and the volume was set
-  // to 0, then increases volume to a minimum value (5%).
-  void SetCaptureMuted(bool do_mute);
-
-  void AddVolumeObserver(VolumeObserver* observer);
-  void RemoveVolumeObserver(VolumeObserver* observer);
-
- private:
-  // Defines the delete on exit Singleton traits we like.  Best to have this
-  // and constructor/destructor private as recommended for Singletons.
-  friend struct DefaultSingletonTraits<AudioHandler>;
-
-  // Takes ownership of |mixer|.
-  explicit AudioHandler(AudioMixer* mixer,
-                        scoped_refptr<AudioPrefHandler> audio_pref_handler);
-  virtual ~AudioHandler();
-
-  // Applies the audio muting policies whenever the user logs in or policy
-  // change notification is received.
-  void ApplyAudioPolicy();
-
-  // Sets volume/muted to specified value and notifies observers if |notify|
-  // is true.
-  void SetVolumePercentInternal(double volume_percent, bool notify);
-  void SetMutedInternal(bool do_mute, bool notify);
-
-  // Overriden from AudioPrefObserver.
-  virtual void OnAudioPolicyPrefChanged() OVERRIDE;
-
-  scoped_ptr<AudioMixer> mixer_;
-
-  ObserverList<VolumeObserver> volume_observers_;
-
-  // Track state for triggering callbacks in ApplyAudioPolicy().
-  bool muted_;
-
-  scoped_refptr<AudioPrefHandler> audio_pref_handler_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioHandler);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_HANDLER_H_
diff --git a/chrome/browser/chromeos/audio/audio_mixer.h b/chrome/browser/chromeos/audio/audio_mixer.h
deleted file mode 100644
index e31ff2b..0000000
--- a/chrome/browser/chromeos/audio/audio_mixer.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_H_
-#define CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_H_
-
-#include "base/basictypes.h"
-
-namespace chromeos {
-
-class AudioMixer {
- public:
-  AudioMixer() {}
-  virtual ~AudioMixer() {}
-
-  // Initializes the connection to the device.  This must be called on the UI
-  // thread; blocking tasks may take place in the background.  Note that the
-  // other methods in this interface can be called before Init().
-  virtual void Init() = 0;
-
-  // Returns the most-recently-set volume, in the range [0.0, 100.0].
-  virtual double GetVolumePercent() = 0;
-
-  // Sets the volume, possibly asynchronously.
-  // |percent| should be in the range [0.0, 100.0].
-  virtual void SetVolumePercent(double percent) = 0;
-
-  // Is the device currently muted?
-  virtual bool IsMuted() = 0;
-
-  // Mutes or unmutes the device, possibly asynchronously.  The caller must
-  // first verify that the mute state is not locked by calling IsMuteLocked.
-  virtual void SetMuted(bool mute) = 0;
-
-  // Is the device's mute state currently locked?
-  virtual bool IsMuteLocked() = 0;
-
-  // Locks the mute state of the device to whatever state it currently is in.
-  // Call SetMuteLocked with false to allow changing the mute state again.
-  virtual void SetMuteLocked(bool locked) = 0;
-
-  // Is the capture device currently muted?
-  virtual bool IsCaptureMuted() = 0;
-
-  // Mutes or unmutes the capture device, possible asynchronously.  The caller
-  // must first verify that the mute state is not locked by calling
-  // IsCaptureMuteLocked.
-  virtual void SetCaptureMuted(bool mute) = 0;
-
-  // Is the capture device's mute state currently locked?
-  virtual bool IsCaptureMuteLocked() = 0;
-
-  // Locks the capture mute state of the device.
-  virtual void SetCaptureMuteLocked(bool locked) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AudioMixer);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_H_
diff --git a/chrome/browser/chromeos/audio/audio_mixer_alsa.cc b/chrome/browser/chromeos/audio/audio_mixer_alsa.cc
deleted file mode 100644
index e29c5d62..0000000
--- a/chrome/browser/chromeos/audio/audio_mixer_alsa.cc
+++ /dev/null
@@ -1,500 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/chromeos/audio/audio_mixer_alsa.h"
-
-#include <unistd.h>
-
-#include <alsa/asoundlib.h>
-
-#include <algorithm>
-#include <cmath>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/chromeos/chromeos_version.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_restrictions.h"
-#include "content/public/browser/browser_thread.h"
-
-typedef long alsa_long_t;  // 'long' is required for ALSA API calls.
-
-using content::BrowserThread;
-using std::string;
-
-namespace chromeos {
-
-namespace {
-
-// Name of the ALSA card to which we connect.
-const char kCardName[] = "default";
-
-// Mixer element names.  We'll use the first master element from the list that
-// exists.
-const char* const kMasterElementNames[] = {
-  "Master",   // x86
-  "Digital",  // ARM
-};
-const char kPCMElementName[] = "PCM";
-
-const char kMicElementName[] = "Mic";
-const char kFrontMicElementName[] = "Front Mic";
-
-// Default minimum and maximum volume (before we've loaded the actual range from
-// ALSA), in decibels.
-const double kDefaultMinVolumeDb = -90.0;
-const double kDefaultMaxVolumeDb = 0.0;
-
-// Default volume as a percentage in the range [0.0, 100.0].
-const double kDefaultVolumePercent = 75.0;
-
-// A value of less than 1.0 adjusts quieter volumes in larger steps (giving
-// finer resolution in the higher volumes).
-// TODO(derat): Choose a better mapping between percent and decibels.  The
-// bottom twenty-five percent or so is useless on a CR-48's internal speakers;
-// it's all inaudible.
-const double kVolumeBias = 0.5;
-
-// Number of seconds that we'll sleep between each connection attempt.
-const int kConnectionRetrySleepSec = 1;
-
-// Connection attempt number (1-indexed) for which we'll log an error if we're
-// still failing.  This is set high enough to give the ALSA modules some time to
-// be loaded into the kernel.  We want to log an error eventually if something
-// is broken, but we don't want to continue spamming the log indefinitely.
-const int kConnectionAttemptToLogFailure = 10;
-
-}  // namespace
-
-AudioMixerAlsa::AudioMixerAlsa()
-    : min_volume_db_(kDefaultMinVolumeDb),
-      max_volume_db_(kDefaultMaxVolumeDb),
-      volume_db_(kDefaultMinVolumeDb),
-      is_muted_(false),
-      apply_is_pending_(true),
-      initial_volume_percent_(kDefaultVolumePercent),
-      alsa_mixer_(NULL),
-      pcm_element_(NULL),
-      mic_element_(NULL),
-      front_mic_element_(NULL),
-      disconnected_event_(true, false),
-      num_connection_attempts_(0) {
-}
-
-AudioMixerAlsa::~AudioMixerAlsa() {
-  if (!thread_.get())
-    return;
-  DCHECK(base::MessageLoop::current() != thread_->message_loop());
-
-  thread_->message_loop()->PostTask(
-      FROM_HERE, base::Bind(&AudioMixerAlsa::Disconnect,
-                            base::Unretained(this)));
-  {
-    // http://crbug.com/125206
-    base::ThreadRestrictions::ScopedAllowWait allow_wait;
-    disconnected_event_.Wait();
-  }
-
-  base::ThreadRestrictions::ScopedAllowIO allow_io_for_thread_join;
-  thread_->Stop();
-  thread_.reset();
-}
-
-void AudioMixerAlsa::Init() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!thread_.get()) << "Init() called twice";
-  thread_.reset(new base::Thread("AudioMixerAlsa"));
-  CHECK(thread_->Start());
-  thread_->message_loop()->PostTask(
-      FROM_HERE, base::Bind(&AudioMixerAlsa::Connect, base::Unretained(this)));
-}
-
-double AudioMixerAlsa::GetVolumePercent() {
-  base::AutoLock lock(lock_);
-  return !alsa_mixer_ ?
-      initial_volume_percent_ :
-      DbToPercent(volume_db_);
-}
-
-void AudioMixerAlsa::SetVolumePercent(double percent) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (isnan(percent))
-    percent = 0.0;
-  percent = std::max(std::min(percent, 100.0), 0.0);
-
-  base::AutoLock lock(lock_);
-  if (!alsa_mixer_) {
-    initial_volume_percent_ = percent;
-  } else {
-    volume_db_ = PercentToDb(percent);
-    ApplyStateIfNeeded();
-  }
-}
-
-bool AudioMixerAlsa::IsMuted() {
-  base::AutoLock lock(lock_);
-  return is_muted_;
-}
-
-void AudioMixerAlsa::SetMuted(bool mute) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  base::AutoLock lock(lock_);
-  if (is_mute_locked_) {
-    NOTREACHED() << "Capture mute has been locked!";
-    return;
-  }
-  is_muted_ = mute;
-  ApplyStateIfNeeded();
-}
-
-bool AudioMixerAlsa::IsMuteLocked() {
-  base::AutoLock lock(lock_);
-  return is_mute_locked_;
-}
-
-void AudioMixerAlsa::SetMuteLocked(bool locked) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  base::AutoLock lock(lock_);
-  is_mute_locked_ = locked;
-}
-
-bool AudioMixerAlsa::IsCaptureMuted() {
-  base::AutoLock lock(lock_);
-  return is_capture_muted_;
-}
-
-void AudioMixerAlsa::SetCaptureMuted(bool mute) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  base::AutoLock lock(lock_);
-  if (is_capture_mute_locked_) {
-    NOTREACHED() << "Capture mute has been locked!";
-    return;
-  }
-  is_capture_muted_ = mute;
-  ApplyStateIfNeeded();
-}
-
-bool AudioMixerAlsa::IsCaptureMuteLocked() {
-  base::AutoLock lock(lock_);
-  return is_capture_mute_locked_;
-}
-
-void AudioMixerAlsa::SetCaptureMuteLocked(bool locked) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  base::AutoLock lock(lock_);
-  is_capture_mute_locked_ = locked;
-}
-
-void AudioMixerAlsa::Connect() {
-  DCHECK(base::MessageLoop::current() == thread_->message_loop());
-  DCHECK(!alsa_mixer_);
-
-  if (disconnected_event_.IsSignaled())
-    return;
-
-  // Do not attempt to connect if we're not on the device.
-  if (!base::chromeos::IsRunningOnChromeOS())
-    return;
-
-  if (!ConnectInternal()) {
-    thread_->message_loop()->PostDelayedTask(FROM_HERE,
-        base::Bind(&AudioMixerAlsa::Connect, base::Unretained(this)),
-        base::TimeDelta::FromSeconds(kConnectionRetrySleepSec));
-  }
-}
-
-bool AudioMixerAlsa::ConnectInternal() {
-  DCHECK(base::MessageLoop::current() == thread_->message_loop());
-  num_connection_attempts_++;
-  int err;
-  snd_mixer_t* handle = NULL;
-
-  if ((err = snd_mixer_open(&handle, 0)) < 0) {
-    if (num_connection_attempts_ == kConnectionAttemptToLogFailure)
-      LOG(WARNING) << "Mixer open error: " << snd_strerror(err);
-    return false;
-  }
-
-  if ((err = snd_mixer_attach(handle, kCardName)) < 0) {
-    if (num_connection_attempts_ == kConnectionAttemptToLogFailure)
-      LOG(WARNING) << "Attach to card " << kCardName << " failed: "
-                   << snd_strerror(err);
-    snd_mixer_close(handle);
-    return false;
-  }
-
-  // Verify PCM can be opened, which also instantiates the PCM mixer element
-  // which is needed for finer volume control and for muting by setting to zero.
-  // If it fails, we can still try to use the mixer as best we can.
-  snd_pcm_t* pcm_out_handle;
-  if ((err = snd_pcm_open(&pcm_out_handle,
-                          kCardName,
-                          SND_PCM_STREAM_PLAYBACK,
-                          0)) >= 0) {
-    snd_pcm_close(pcm_out_handle);
-  } else {
-    if (num_connection_attempts_ == kConnectionAttemptToLogFailure)
-      LOG(WARNING) << "PCM open failed: " << snd_strerror(err);
-  }
-
-  if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
-    if (num_connection_attempts_ == kConnectionAttemptToLogFailure)
-      LOG(WARNING) << "Mixer register error: " << snd_strerror(err);
-    snd_mixer_close(handle);
-    return false;
-  }
-
-  if ((err = snd_mixer_load(handle)) < 0) {
-    if (num_connection_attempts_ == kConnectionAttemptToLogFailure)
-      LOG(WARNING) << "Mixer " << kCardName << " load error: %s"
-                   << snd_strerror(err);
-    snd_mixer_close(handle);
-    return false;
-  }
-
-  VLOG(1) << "Opened mixer " << kCardName << " successfully";
-
-  double min_volume_db = kDefaultMinVolumeDb;
-  double max_volume_db = kDefaultMaxVolumeDb;
-
-  snd_mixer_elem_t* master_element = NULL;
-  for (size_t i = 0; i < arraysize(kMasterElementNames); ++i) {
-    master_element = FindElementWithName(handle, kMasterElementNames[i]);
-    if (master_element)
-      break;
-  }
-
-  if (!master_element) {
-    if (num_connection_attempts_ == kConnectionAttemptToLogFailure)
-      LOG(WARNING) << "Unable to find a master element on " << kCardName;
-    snd_mixer_close(handle);
-    return false;
-  }
-
-  alsa_long_t long_low = static_cast<alsa_long_t>(kDefaultMinVolumeDb * 100);
-  alsa_long_t long_high = static_cast<alsa_long_t>(kDefaultMaxVolumeDb * 100);
-  err = snd_mixer_selem_get_playback_dB_range(
-      master_element, &long_low, &long_high);
-  if (err != 0) {
-    if (num_connection_attempts_ == kConnectionAttemptToLogFailure)
-      LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed:"
-                   << snd_strerror(err);
-    snd_mixer_close(handle);
-    return false;
-  }
-  min_volume_db = static_cast<double>(long_low) / 100.0;
-  max_volume_db = static_cast<double>(long_high) / 100.0;
-
-  snd_mixer_elem_t* pcm_element = FindElementWithName(handle, kPCMElementName);
-  if (pcm_element) {
-    alsa_long_t long_low = static_cast<alsa_long_t>(kDefaultMinVolumeDb * 100);
-    alsa_long_t long_high = static_cast<alsa_long_t>(kDefaultMaxVolumeDb * 100);
-    err = snd_mixer_selem_get_playback_dB_range(
-        pcm_element, &long_low, &long_high);
-    if (err != 0) {
-      if (num_connection_attempts_ == kConnectionAttemptToLogFailure)
-        LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed for "
-                     << kPCMElementName << ": " << snd_strerror(err);
-      snd_mixer_close(handle);
-      return false;
-    }
-    min_volume_db += static_cast<double>(long_low) / 100.0;
-    max_volume_db += static_cast<double>(long_high) / 100.0;
-  }
-
-  VLOG(1) << "Volume range is " << min_volume_db << " dB to "
-          << max_volume_db << " dB";
-
-  snd_mixer_elem_t* mic_element = FindElementWithName(handle, kMicElementName);
-  snd_mixer_elem_t* front_mic_element =
-      FindElementWithName(handle, kFrontMicElementName);
-  {
-    base::AutoLock lock(lock_);
-    alsa_mixer_ = handle;
-    master_element_ = master_element;
-    pcm_element_ = pcm_element;
-    mic_element_ = mic_element;
-    front_mic_element_ = front_mic_element;
-    min_volume_db_ = min_volume_db;
-    max_volume_db_ = max_volume_db;
-    volume_db_ = PercentToDb(initial_volume_percent_);
-  }
-
-  ApplyState();
-  return true;
-}
-
-void AudioMixerAlsa::Disconnect() {
-  DCHECK(base::MessageLoop::current() == thread_->message_loop());
-  if (alsa_mixer_) {
-    snd_mixer_close(alsa_mixer_);
-    alsa_mixer_ = NULL;
-  }
-  disconnected_event_.Signal();
-}
-
-void AudioMixerAlsa::ApplyState() {
-  DCHECK(base::MessageLoop::current() == thread_->message_loop());
-  if (!alsa_mixer_)
-    return;
-
-  bool should_mute = false;
-  bool should_mute_capture = false;
-  double new_volume_db = 0;
-  {
-    base::AutoLock lock(lock_);
-    should_mute = is_muted_;
-    should_mute_capture = is_capture_muted_;
-    new_volume_db = should_mute ? min_volume_db_ : volume_db_;
-    apply_is_pending_ = false;
-  }
-
-  if (pcm_element_) {
-    // If a PCM volume slider exists, then first set the Master volume to the
-    // nearest volume >= requested volume, then adjust PCM volume down to get
-    // closer to the requested volume.
-    SetElementVolume(master_element_, new_volume_db, 0.9999f);
-
-    double pcm_volume_db = 0.0;
-    double master_volume_db = 0.0;
-    if (GetElementVolume(master_element_, &master_volume_db))
-      pcm_volume_db = new_volume_db - master_volume_db;
-    SetElementVolume(pcm_element_, pcm_volume_db, 0.5f);
-  } else {
-    SetElementVolume(master_element_, new_volume_db, 0.5f);
-  }
-
-  SetElementMuted(master_element_, should_mute);
-  if (mic_element_)
-    SetElementMuted(mic_element_, should_mute_capture);
-  if (front_mic_element_)
-    SetElementMuted(front_mic_element_, should_mute_capture);
-}
-
-snd_mixer_elem_t* AudioMixerAlsa::FindElementWithName(
-    snd_mixer_t* handle, const string& element_name) const {
-  DCHECK(base::MessageLoop::current() == thread_->message_loop());
-  snd_mixer_selem_id_t* sid = NULL;
-
-  // Using id_malloc/id_free API instead of id_alloca since the latter gives the
-  // warning: the address of 'sid' will always evaluate as 'true'.
-  if (snd_mixer_selem_id_malloc(&sid))
-    return NULL;
-
-  snd_mixer_selem_id_set_index(sid, 0);
-  snd_mixer_selem_id_set_name(sid, element_name.c_str());
-  snd_mixer_elem_t* element = snd_mixer_find_selem(handle, sid);
-  if (!element)
-    VLOG(1) << "Unable to find control " << snd_mixer_selem_id_get_name(sid);
-
-  snd_mixer_selem_id_free(sid);
-  return element;
-}
-
-bool AudioMixerAlsa::GetElementVolume(snd_mixer_elem_t* element,
-                                      double* current_volume_db) {
-  DCHECK(base::MessageLoop::current() == thread_->message_loop());
-  alsa_long_t long_volume = 0;
-  int alsa_result = snd_mixer_selem_get_playback_dB(
-      element, static_cast<snd_mixer_selem_channel_id_t>(0), &long_volume);
-  if (alsa_result != 0) {
-    LOG(WARNING) << "snd_mixer_selem_get_playback_dB() failed: "
-                 << snd_strerror(alsa_result);
-    return false;
-  }
-  *current_volume_db = static_cast<double>(long_volume) / 100.0;
-  return true;
-}
-
-bool AudioMixerAlsa::SetElementVolume(snd_mixer_elem_t* element,
-                                      double new_volume_db,
-                                      double rounding_bias) {
-  DCHECK(base::MessageLoop::current() == thread_->message_loop());
-  alsa_long_t volume_low = 0;
-  alsa_long_t volume_high = 0;
-  int alsa_result = snd_mixer_selem_get_playback_volume_range(
-      element, &volume_low, &volume_high);
-  if (alsa_result != 0) {
-    LOG(WARNING) << "snd_mixer_selem_get_playback_volume_range() failed: "
-                 << snd_strerror(alsa_result);
-    return false;
-  }
-  alsa_long_t volume_range = volume_high - volume_low;
-  if (volume_range <= 0)
-    return false;
-
-  alsa_long_t db_low_int = 0;
-  alsa_long_t db_high_int = 0;
-  alsa_result =
-      snd_mixer_selem_get_playback_dB_range(element, &db_low_int, &db_high_int);
-  if (alsa_result != 0) {
-    LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed: "
-                 << snd_strerror(alsa_result);
-    return false;
-  }
-
-  double db_low = static_cast<double>(db_low_int) / 100.0;
-  double db_high = static_cast<double>(db_high_int) / 100.0;
-  double db_step = static_cast<double>(db_high - db_low) / volume_range;
-  if (db_step <= 0.0)
-    return false;
-
-  if (new_volume_db < db_low)
-    new_volume_db = db_low;
-
-  alsa_long_t value = static_cast<alsa_long_t>(
-      rounding_bias + (new_volume_db - db_low) / db_step) + volume_low;
-  alsa_result = snd_mixer_selem_set_playback_volume_all(element, value);
-  if (alsa_result != 0) {
-    LOG(WARNING) << "snd_mixer_selem_set_playback_volume_all() failed: "
-                 << snd_strerror(alsa_result);
-    return false;
-  }
-
-  VLOG(1) << "Set volume " << snd_mixer_selem_get_name(element)
-          << " to " << new_volume_db << " ==> "
-          << (value - volume_low) * db_step + db_low << " dB";
-
-  return true;
-}
-
-void AudioMixerAlsa::SetElementMuted(snd_mixer_elem_t* element, bool mute) {
-  DCHECK(base::MessageLoop::current() == thread_->message_loop());
-  int alsa_result = snd_mixer_selem_set_playback_switch_all(element, !mute);
-  if (alsa_result != 0) {
-    LOG(WARNING) << "snd_mixer_selem_set_playback_switch_all() failed: "
-                 << snd_strerror(alsa_result);
-  } else {
-    VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(element)
-            << " to " << mute;
-  }
-}
-
-double AudioMixerAlsa::DbToPercent(double db) const {
-  lock_.AssertAcquired();
-  if (db < min_volume_db_)
-    return 0.0;
-  return 100.0 * pow((db - min_volume_db_) /
-      (max_volume_db_ - min_volume_db_), 1/kVolumeBias);
-}
-
-double AudioMixerAlsa::PercentToDb(double percent) const {
-  lock_.AssertAcquired();
-  return pow(percent / 100.0, kVolumeBias) *
-      (max_volume_db_ - min_volume_db_) + min_volume_db_;
-}
-
-void AudioMixerAlsa::ApplyStateIfNeeded() {
-  lock_.AssertAcquired();
-  if (!apply_is_pending_) {
-    thread_->message_loop()->PostTask(
-        FROM_HERE,
-        base::Bind(&AudioMixerAlsa::ApplyState, base::Unretained(this)));
-  }
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/audio/audio_mixer_alsa.h b/chrome/browser/chromeos/audio/audio_mixer_alsa.h
deleted file mode 100644
index c954d2e..0000000
--- a/chrome/browser/chromeos/audio/audio_mixer_alsa.h
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_ALSA_H_
-#define CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_ALSA_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/synchronization/lock.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
-#include "chrome/browser/chromeos/audio/audio_mixer.h"
-
-struct _snd_mixer_elem;
-struct _snd_mixer;
-
-namespace chromeos {
-
-// Simple wrapper around ALSA's mixer functions.
-// Interaction with ALSA takes place on a background thread.
-class AudioMixerAlsa : public AudioMixer {
- public:
-  AudioMixerAlsa();
-  virtual ~AudioMixerAlsa();
-
-  // AudioMixer implementation.
-  virtual void Init() OVERRIDE;
-  virtual double GetVolumePercent() OVERRIDE;
-  virtual void SetVolumePercent(double percent) OVERRIDE;
-  virtual bool IsMuted() OVERRIDE;
-  virtual void SetMuted(bool mute) OVERRIDE;
-  virtual bool IsMuteLocked() OVERRIDE;
-  virtual void SetMuteLocked(bool locked) OVERRIDE;
-  virtual bool IsCaptureMuted() OVERRIDE;
-  virtual void SetCaptureMuted(bool mute) OVERRIDE;
-  virtual bool IsCaptureMuteLocked() OVERRIDE;
-  virtual void SetCaptureMuteLocked(bool locked) OVERRIDE;
-
- private:
-  // Tries to connect to ALSA.  On failure, posts a delayed Connect() task to
-  // try again.
-  void Connect();
-
-  // Helper method called by Connect().  On success, initializes
-  // |min_volume_db_|, |max_volume_db_|, |alsa_mixer_|, and |element_*| and
-  // returns true.
-  bool ConnectInternal();
-
-  // Disconnects from ALSA if currently connected and signals
-  // |disconnected_event_|.
-  void Disconnect();
-
-  // Updates |alsa_mixer_| for current values of |volume_db_| and |is_muted_|.
-  // No-op if not connected.
-  void ApplyState();
-
-  // Finds the element named |element_name|.  Returns NULL on failure.
-  _snd_mixer_elem* FindElementWithName(_snd_mixer* handle,
-                                       const std::string& element_name) const;
-
-  // Queries |element|'s current volume, copying it to |new_volume_db|.
-  // Returns true on success.
-  bool GetElementVolume(_snd_mixer_elem* element, double* current_volume_db);
-
-  // Sets |element|'s volume.  Since volume is done in steps, we may not get the
-  // exact volume asked for.  |rounding_bias| is added in before rounding to the
-  // nearest volume step (use 0.5 to round to nearest).
-  bool SetElementVolume(_snd_mixer_elem* element,
-                        double new_volume_db,
-                        double rounding_bias);
-
-  // Mutes or unmutes |element|.
-  void SetElementMuted(_snd_mixer_elem* element, bool mute);
-
-  // Converts between percentages (in the range [0.0, 100.0]) and decibels.
-  // |lock_| must be held.
-  double DbToPercent(double db) const;
-  double PercentToDb(double percent) const;
-
-  // Calls ApplyState on the proper thread only if no other call is currently
-  // pending.
-  void ApplyStateIfNeeded();
-
-  // Guards |min_volume_db_|, |max_volume_db_|, |volume_db_|, |is_muted_|,
-  // |is_mute_locked_|, |is_capture_muted_|, |is_capture_mute_locked_|,
-  // |apply_is_pending_|, and |initial_volume_percent_|.
-  base::Lock lock_;
-
-  // Volume range limits are computed once in ConnectInternal().
-  double min_volume_db_;
-  double max_volume_db_;
-
-  // Most recently-requested volume, in decibels.  This variable is updated
-  // immediately by SetVolumePercent() (post-initialization); the actual mixer
-  // volume is updated later on |thread_| by ApplyState().
-  double volume_db_;
-
-  // Most recently-requested muting state.
-  bool is_muted_;
-  bool is_capture_muted_;
-
-  // If set to true the mute state of the respective channel is locked and can
-  // not be changed until it is unlocked.  We don't have control over ALSA so
-  // other sound clients might unmute channels but we can do best effort to
-  // mimic CRAS's locking functionality.
-  bool is_mute_locked_;
-  bool is_capture_mute_locked_;
-
-  // Is there already a pending call to ApplyState() scheduled on |thread_|?
-  bool apply_is_pending_;
-
-  // Before |min_volume_db_| and |max_volume_db_| are fetched from ALSA, we
-  // can't convert percentages to decibels.  The default initial volume and any
-  // subsequent requests we get early on are stored here and then applied during
-  // initialization.  This variable is unused after initialization.
-  double initial_volume_percent_;
-
-  // Connection to ALSA.  NULL if not connected.
-  _snd_mixer* alsa_mixer_;
-
-  // Master mixer.
-  _snd_mixer_elem* master_element_;
-
-  // PCM mixer.  May be NULL if the driver doesn't expose one.
-  _snd_mixer_elem* pcm_element_;
-
-  // Mic mixers.  Used to mute capture.  Both might be NULL on some drivers.
-  _snd_mixer_elem* mic_element_;
-  _snd_mixer_elem* front_mic_element_;
-
-  // Signalled after Disconnect() finishes (which is itself invoked by the
-  // d'tor).
-  base::WaitableEvent disconnected_event_;
-
-  // Background thread used for interacting with ALSA.
-  scoped_ptr<base::Thread> thread_;
-
-  // Number of times that we've attempted to connect to ALSA.  Just used to keep
-  // us from spamming the logs.
-  int num_connection_attempts_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioMixerAlsa);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_ALSA_H_
diff --git a/chrome/browser/chromeos/audio/audio_mixer_cras.cc b/chrome/browser/chromeos/audio/audio_mixer_cras.cc
deleted file mode 100644
index 2de62e1..0000000
--- a/chrome/browser/chromeos/audio/audio_mixer_cras.cc
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/chromeos/audio/audio_mixer_cras.h"
-
-#include <cmath>
-#include <cras_client.h>
-#include <unistd.h>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_restrictions.h"
-#include "content/public/browser/browser_thread.h"
-
-using content::BrowserThread;
-
-namespace chromeos {
-
-namespace {
-
-// Default volume as a percentage in the range [0.0, 100.0].
-const double kDefaultVolumePercent = 75.0;
-
-// Number of seconds that we'll sleep between each connection attempt.
-const int kConnectionRetrySleepSec = 1;
-
-}  // namespace
-
-AudioMixerCras::AudioMixerCras()
-    : client_(NULL),
-      client_connected_(false),
-      volume_percent_(kDefaultVolumePercent),
-      is_muted_(false),
-      is_mute_locked_(false),
-      is_capture_muted_(false),
-      is_capture_mute_locked_(false),
-      apply_is_pending_(true) {
-}
-
-AudioMixerCras::~AudioMixerCras() {
-  if (!thread_.get())
-    return;
-  DCHECK(base::MessageLoop::current() != thread_->message_loop());
-
-  base::ThreadRestrictions::ScopedAllowIO allow_io_for_thread_join;
-  thread_->Stop();
-  thread_.reset();
-
-  cras_client_destroy(client_);
-}
-
-void AudioMixerCras::Init() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!thread_.get()) << "Init() called twice";
-
-  thread_.reset(new base::Thread("AudioMixerCras"));
-  CHECK(thread_->Start());
-  thread_->message_loop()->PostTask(
-      FROM_HERE, base::Bind(&AudioMixerCras::Connect, base::Unretained(this)));
-}
-
-double AudioMixerCras::GetVolumePercent() {
-  base::AutoLock lock(lock_);
-  return volume_percent_;
-}
-
-void AudioMixerCras::SetVolumePercent(double percent) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (isnan(percent))
-    percent = 0.0;
-  percent = std::max(std::min(percent, 100.0), 0.0);
-
-  base::AutoLock lock(lock_);
-  volume_percent_ = percent;
-  if (client_connected_ && !apply_is_pending_)
-    thread_->message_loop()->PostTask(FROM_HERE,
-        base::Bind(&AudioMixerCras::ApplyState, base::Unretained(this)));
-}
-
-bool AudioMixerCras::IsMuted() {
-  base::AutoLock lock(lock_);
-  return is_muted_;
-}
-
-void AudioMixerCras::SetMuted(bool mute) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  base::AutoLock lock(lock_);
-  if (is_mute_locked_) {
-    NOTREACHED() << "Mute has been locked!";
-    return;
-  }
-  is_muted_ = mute;
-  ApplyStateIfNeeded();
-}
-
-bool AudioMixerCras::IsMuteLocked() {
-  base::AutoLock lock(lock_);
-  return is_mute_locked_;
-}
-
-void AudioMixerCras::SetMuteLocked(bool locked) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  base::AutoLock lock(lock_);
-  is_mute_locked_ = locked;
-  ApplyStateIfNeeded();
-}
-
-bool AudioMixerCras::IsCaptureMuted() {
-  base::AutoLock lock(lock_);
-  return is_capture_muted_;
-}
-
-void AudioMixerCras::SetCaptureMuted(bool mute) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  base::AutoLock lock(lock_);
-  if (is_capture_mute_locked_) {
-    NOTREACHED() << "Capture mute has been locked!";
-    return;
-  }
-  is_capture_muted_ = mute;
-  ApplyStateIfNeeded();
-}
-
-bool AudioMixerCras::IsCaptureMuteLocked() {
-  base::AutoLock lock(lock_);
-  return is_capture_mute_locked_;
-}
-
-void AudioMixerCras::SetCaptureMuteLocked(bool locked) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  base::AutoLock lock(lock_);
-  is_capture_mute_locked_ = locked;
-  ApplyStateIfNeeded();
-}
-
-void AudioMixerCras::Connect() {
-  DCHECK(base::MessageLoop::current() == thread_->message_loop());
-
-  // Create the client structure.
-  if (client_ == NULL && cras_client_create(&client_) < 0) {
-    LOG(DFATAL) << "cras_client_create() failed";
-    return; // TODO(dgreid) change interface so this can return an error.
-  }
-
-  if (cras_client_connect(client_) != 0) {
-    thread_->message_loop()->PostDelayedTask(FROM_HERE,
-        base::Bind(&AudioMixerCras::Connect, base::Unretained(this)),
-        base::TimeDelta::FromSeconds(kConnectionRetrySleepSec));
-    return;
-  }
-  client_connected_ = true;
-
-  ApplyState();
-}
-
-void AudioMixerCras::ApplyState() {
-  DCHECK(base::MessageLoop::current() == thread_->message_loop());
-  if (!client_connected_)
-    return;
-
-  bool should_mute = false;
-  bool should_lock_mute = false;
-  bool should_mute_capture = false;
-  bool should_lock_capture_mute = false;
-  size_t new_volume = 0;
-  {
-    base::AutoLock lock(lock_);
-    should_mute = is_muted_;
-    should_lock_mute = is_mute_locked_;
-    should_mute_capture = is_capture_muted_;
-    should_lock_capture_mute = is_capture_mute_locked_;
-    new_volume = floor(volume_percent_ + 0.5);
-    apply_is_pending_ = false;
-  }
-
-  // If muting mute before setting volume, if un-muting set volume first.
-  if (should_mute) {
-    cras_client_set_system_mute(client_, should_mute);
-    cras_client_set_system_volume(client_, new_volume);
-  } else {
-    cras_client_set_system_volume(client_, new_volume);
-    cras_client_set_system_mute(client_, should_mute);
-  }
-  cras_client_set_system_mute_locked(client_, should_lock_mute);
-  cras_client_set_system_capture_mute(client_, should_mute_capture);
-  cras_client_set_system_capture_mute_locked(client_, should_lock_capture_mute);
-}
-
-void AudioMixerCras::ApplyStateIfNeeded() {
-  lock_.AssertAcquired();
-  if (client_connected_ && !apply_is_pending_) {
-    thread_->message_loop()->PostTask(
-        FROM_HERE,
-        base::Bind(&AudioMixerCras::ApplyState, base::Unretained(this)));
-  }
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/audio/audio_mixer_cras.h b/chrome/browser/chromeos/audio/audio_mixer_cras.h
deleted file mode 100644
index 5650fb0..0000000
--- a/chrome/browser/chromeos/audio/audio_mixer_cras.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_CRAS_H_
-#define CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_CRAS_H_
-
-#include <cras_client.h>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread.h"
-#include "chrome/browser/chromeos/audio/audio_mixer.h"
-
-namespace chromeos {
-
-// Simple wrapper for sending volume and mute commands to the audio server on
-// ChromeOS.  Interaction happens on a background thread so that initialization
-// can poll until the server is started.
-class AudioMixerCras : public AudioMixer {
- public:
-  AudioMixerCras();
-  virtual ~AudioMixerCras();
-
-  // AudioMixer implementation.
-  virtual void Init() OVERRIDE;
-  virtual double GetVolumePercent() OVERRIDE;
-  virtual void SetVolumePercent(double percent) OVERRIDE;
-  virtual bool IsMuted() OVERRIDE;
-  virtual void SetMuted(bool mute) OVERRIDE;
-  virtual bool IsMuteLocked() OVERRIDE;
-  virtual void SetMuteLocked(bool locked) OVERRIDE;
-  virtual bool IsCaptureMuted() OVERRIDE;
-  virtual void SetCaptureMuted(bool mute) OVERRIDE;
-  virtual bool IsCaptureMuteLocked() OVERRIDE;
-  virtual void SetCaptureMuteLocked(bool locked) OVERRIDE;
-
- private:
-  // Tries to connect to CRAS.  On failure, posts a delayed Connect() task to
-  // try again.  Failure could occur if the CRAS server isn't running yet.
-  void Connect();
-
-  // Updates |client_| for current values of |volume_percent_| and
-  // |is_muted_|. No-op if not connected.
-  void ApplyState();
-
-  // Calls ApplyState on the proper thread only if no other call is currently
-  // pending.
-  void ApplyStateIfNeeded();
-
-  // Interfaces to the audio server.
-  struct cras_client *client_;
-
-  // Indicates if we have connected |client_| to the server.
-  bool client_connected_;
-
-  // Most recently-requested volume, in percent.  This variable is updated
-  // immediately by SetVolumePercent() (post-initialization); the actual mixer
-  // volume is updated later on |thread_| by ApplyState().
-  double volume_percent_;
-
-  // Most recently-requested muting state.
-  bool is_muted_;
-  bool is_mute_locked_;
-  bool is_capture_muted_;
-  bool is_capture_mute_locked_;
-
-  // Is there already a pending call to ApplyState() scheduled on |thread_|?
-  bool apply_is_pending_;
-
-  // Background thread used for interacting with CRAS.
-  scoped_ptr<base::Thread> thread_;
-
-  // Guards |volume_percent_|, |is_muted_|, |is_mute_locked_|,
-  // |is_capture_muted_|, |is_capture_mute_locked_|, and |apply_is_pending_|.
-  base::Lock lock_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioMixerCras);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_CRAS_H_
diff --git a/chrome/browser/chromeos/audio/audio_pref_handler_impl.cc b/chrome/browser/chromeos/audio/audio_pref_handler_impl.cc
deleted file mode 100644
index 2cc885e..0000000
--- a/chrome/browser/chromeos/audio/audio_pref_handler_impl.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2013 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.
-
-#include "chrome/browser/chromeos/audio/audio_pref_handler_impl.h"
-
-#include <algorithm>
-#include <cmath>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/logging.h"
-#include "base/prefs/pref_registry_simple.h"
-#include "base/prefs/pref_service.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/common/pref_names.h"
-
-using std::max;
-using std::min;
-
-namespace chromeos {
-
-namespace {
-
-// Default value for the volume pref, as a percent in the range [0.0, 100.0].
-const double kDefaultVolumePercent = 75.0;
-
-// Values used for muted preference.
-const int kPrefMuteOff = 0;
-const int kPrefMuteOn = 1;
-
-}  // namespace
-
-double AudioPrefHandlerImpl::GetOutputVolumeValue() {
-  return local_state_->GetDouble(prefs::kAudioVolumePercent);
-}
-
-void AudioPrefHandlerImpl::SetOutputVolumeValue(double volume_percent) {
-  local_state_->SetDouble(prefs::kAudioVolumePercent, volume_percent);
-}
-
-bool AudioPrefHandlerImpl::GetOutputMuteValue() {
-  return (local_state_->GetInteger(prefs::kAudioMute) == kPrefMuteOn);
-}
-
-void AudioPrefHandlerImpl::SetOutputMuteValue(bool mute) {
-  local_state_->SetInteger(prefs::kAudioMute,
-                           mute ? kPrefMuteOn : kPrefMuteOff);
-}
-
-bool AudioPrefHandlerImpl::GetAudioCaptureAllowedValue() {
-  return local_state_->GetBoolean(::prefs::kAudioCaptureAllowed);
-}
-
-bool AudioPrefHandlerImpl::GetAudioOutputAllowedValue() {
-  return local_state_->GetBoolean(prefs::kAudioOutputAllowed);
-}
-
-void AudioPrefHandlerImpl::AddAudioPrefObserver(
-    AudioPrefObserver* observer) {
-  observers_.AddObserver(observer);
-}
-
-void AudioPrefHandlerImpl::RemoveAudioPrefObserver(
-    AudioPrefObserver* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-AudioPrefHandlerImpl::AudioPrefHandlerImpl(PrefService* local_state)
-    : local_state_(local_state) {
-  InitializePrefObservers();
-}
-
-AudioPrefHandlerImpl::~AudioPrefHandlerImpl() {
-};
-
-void AudioPrefHandlerImpl::InitializePrefObservers() {
-  pref_change_registrar_.Init(local_state_);
-  base::Closure callback =
-      base::Bind(&AudioPrefHandlerImpl::NotifyAudioPolicyChange,
-                 base::Unretained(this));
-  pref_change_registrar_.Add(prefs::kAudioOutputAllowed, callback);
-  pref_change_registrar_.Add(::prefs::kAudioCaptureAllowed, callback);
-}
-
-void AudioPrefHandlerImpl::NotifyAudioPolicyChange() {
-  FOR_EACH_OBSERVER(AudioPrefObserver,
-                    observers_,
-                    OnAudioPolicyPrefChanged());
-}
-
-// static
-void AudioPrefHandlerImpl::RegisterPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterDoublePref(prefs::kAudioVolumePercent,
-                               kDefaultVolumePercent);
-  registry->RegisterIntegerPref(prefs::kAudioMute, kPrefMuteOff);
-  // Register the prefs backing the audio muting policies.
-  registry->RegisterBooleanPref(prefs::kAudioOutputAllowed, true);
-  // This pref has moved to the media subsystem but we should verify it is there
-  // before we use it.
-  registry->RegisterBooleanPref(::prefs::kAudioCaptureAllowed, true);
-}
-
-// static
-AudioPrefHandler* AudioPrefHandler::Create(PrefService* local_state) {
-  return new AudioPrefHandlerImpl(local_state);
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/audio/audio_pref_handler_impl.h b/chrome/browser/chromeos/audio/audio_pref_handler_impl.h
deleted file mode 100644
index 47f8da0..0000000
--- a/chrome/browser/chromeos/audio/audio_pref_handler_impl.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2013 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_PREF_HANDLER_IMPL_H_
-#define CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_PREF_HANDLER_IMPL_H_
-
-#include "base/observer_list.h"
-#include "base/prefs/pref_change_registrar.h"
-#include "chromeos/audio/audio_pref_handler.h"
-
-class PrefRegistrySimple;
-class PrefService;
-
-namespace chromeos {
-
-// TODO(jennyz,rkc): This class will be removed once we remove the old Audio
-// Handler code.
-// Class which implements AudioPrefHandler interface and register audio
-// preferences as well.
-class AudioPrefHandlerImpl : public AudioPrefHandler {
- public:
-  explicit AudioPrefHandlerImpl(PrefService* local_state);
-
-  // Overriden from AudioPreHandler.
-  virtual double GetOutputVolumeValue() OVERRIDE;
-  virtual void SetOutputVolumeValue(double volume_percent) OVERRIDE;
-  virtual bool GetOutputMuteValue() OVERRIDE;
-  virtual void SetOutputMuteValue(bool mute_on) OVERRIDE;
-  virtual bool GetAudioCaptureAllowedValue() OVERRIDE;
-  virtual bool GetAudioOutputAllowedValue() OVERRIDE;
-  virtual void AddAudioPrefObserver(AudioPrefObserver* observer) OVERRIDE;
-  virtual void RemoveAudioPrefObserver(AudioPrefObserver* observer) OVERRIDE;
-
-  // Registers volume and mute preferences.
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
- protected:
-  virtual ~AudioPrefHandlerImpl();
-
- private:
-  // Initializes the observers for the policy prefs.
-  void InitializePrefObservers();
-
-  // Notifies the AudioPrefObserver for audio policy pref changes.
-  void NotifyAudioPolicyChange();
-
-  PrefService* local_state_;  // not owned
-
-  PrefChangeRegistrar pref_change_registrar_;
-
-  ObserverList<AudioPrefObserver> observers_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioPrefHandlerImpl);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_PREF_HANDLER_IMPL_H_
diff --git a/chrome/browser/chromeos/boot_times_loader.cc b/chrome/browser/chromeos/boot_times_loader.cc
index 2fd1b0a..594ff42 100644
--- a/chrome/browser/chromeos/boot_times_loader.cc
+++ b/chrome/browser/chromeos/boot_times_loader.cc
@@ -62,7 +62,7 @@
          ++i) {
       WebContents* tab = browser->tab_strip_model()->GetWebContentsAt(i);
       if (tab->GetRenderWidgetHostView() == rwhv) {
-        return tab->GetURL().spec();
+        return tab->GetLastCommittedURL().spec();
       }
     }
   }
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index eb78f82..e87caf6 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_launcher.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
-#include "chrome/browser/chromeos/audio/audio_handler.h"
 #include "chrome/browser/chromeos/boot_times_loader.h"
 #include "chrome/browser/chromeos/contacts/contact_manager.h"
 #include "chrome/browser/chromeos/cros/cert_library.h"
@@ -75,7 +74,6 @@
 #include "chrome/browser/chromeos/system/statistics_provider.h"
 #include "chrome/browser/chromeos/system_key_event_listener.h"
 #include "chrome/browser/chromeos/upgrade_detector_chromeos.h"
-#include "chrome/browser/chromeos/web_socket_proxy_controller.h"
 #include "chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/metrics/metrics_service.h"
@@ -90,7 +88,6 @@
 #include "chrome/common/logging_chrome.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/audio/audio_devices_pref_handler.h"
-#include "chromeos/audio/audio_pref_handler.h"
 #include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/chromeos_paths.h"
 #include "chromeos/chromeos_switches.h"
@@ -453,13 +450,8 @@
       content::BrowserThread::GetMessageLoopProxyForThread(
           content::BrowserThread::IO));
 
-  if (ash::switches::UseNewAudioHandler()) {
-    CrasAudioHandler::Initialize(
-        AudioDevicesPrefHandler::Create(g_browser_process->local_state()));
-  } else {
-    AudioHandler::Initialize(
-       AudioPrefHandler::Create(g_browser_process->local_state()));
-  }
+  CrasAudioHandler::Initialize(
+      AudioDevicesPrefHandler::Create(g_browser_process->local_state()));
 
   if (!StartupUtils::IsOobeCompleted())
     system::StatisticsProvider::GetInstance()->LoadOemManifest();
@@ -784,13 +776,7 @@
   // even if Initialize() wasn't called.
   SystemKeyEventListener::Shutdown();
   imageburner::BurnManager::Shutdown();
-  if (ash::switches::UseNewAudioHandler()) {
-    CrasAudioHandler::Shutdown();
-  } else {
-    AudioHandler::Shutdown();
-  }
-
-  WebSocketProxyController::Shutdown();
+  CrasAudioHandler::Shutdown();
 
   // Let classes unregister themselves as observers of the ash::Shell singleton
   // before the shell is destroyed.
diff --git a/chrome/browser/chromeos/contacts/google_contact_store.cc b/chrome/browser/chromeos/contacts/google_contact_store.cc
index a4f40d4..45f0301 100644
--- a/chrome/browser/chromeos/contacts/google_contact_store.cc
+++ b/chrome/browser/chromeos/contacts/google_contact_store.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/chromeos/contacts/contact_database.h"
 #include "chrome/browser/chromeos/contacts/contact_store_observer.h"
 #include "chrome/browser/chromeos/contacts/gdata_contacts_service.h"
+#include "chrome/browser/chromeos/profiles/profile_util.h"
 #include "chrome/browser/google_apis/auth_service.h"
 #include "chrome/browser/google_apis/time_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -413,7 +414,7 @@
     Profile* profile) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(profile);
-  return google_apis::AuthService::CanAuthenticate(profile);
+  return chromeos::IsProfileAssociatedWithGaiaAccount(profile);
 }
 
 ContactStore* GoogleContactStoreFactory::CreateContactStore(Profile* profile) {
diff --git a/chrome/browser/chromeos/dbus/cros_dbus_service.cc b/chrome/browser/chromeos/dbus/cros_dbus_service.cc
index 2843aa0..aefccdb 100644
--- a/chrome/browser/chromeos/dbus/cros_dbus_service.cc
+++ b/chrome/browser/chromeos/dbus/cros_dbus_service.cc
@@ -49,7 +49,16 @@
     if (service_started_)
       return;
 
+    // There are some situations, described in http://crbug.com/234382#c27,
+    // where processes on Linux can wind up stuck in an uninterruptible state
+    // for tens of seconds. If this happens when Chrome is trying to exit,
+    // this unkillable process can wind up clinging to ownership of
+    // kLibCrosServiceName while the system is trying to restart the browser.
+    // This leads to a fatal situation if we don't allow the new browser
+    // instance to replace the old as the owner of kLibCrosServiceName as seen
+    // in http://crbug.com/234382. Hence, REQUIRE_PRIMARY_ALLOW_REPLACEMENT.
     bus_->RequestOwnership(kLibCrosServiceName,
+                           dbus::Bus::REQUIRE_PRIMARY_ALLOW_REPLACEMENT,
                            base::Bind(&CrosDBusServiceImpl::OnOwnership,
                                       base::Unretained(this)));
 
diff --git a/chrome/browser/chromeos/dbus/printer_service_provider.cc b/chrome/browser/chromeos/dbus/printer_service_provider.cc
index e0771f3..f4f5ce9 100644
--- a/chrome/browser/chromeos/dbus/printer_service_provider.cc
+++ b/chrome/browser/chromeos/dbus/printer_service_provider.cc
@@ -48,7 +48,7 @@
 
 Browser* ActivateAndGetBrowserForUrl(GURL url) {
   for (TabContentsIterator it; !it.done(); it.Next()) {
-    if (it->GetURL() == url) {
+    if (it->GetLastCommittedURL() == url) {
       ActivateContents(it.browser(), *it);
       return it.browser();
     }
diff --git a/chrome/browser/chromeos/display/display_preferences.cc b/chrome/browser/chromeos/display/display_preferences.cc
index a6a6b89..168404c 100644
--- a/chrome/browser/chromeos/display/display_preferences.cc
+++ b/chrome/browser/chromeos/display/display_preferences.cc
@@ -135,13 +135,20 @@
     int ui_scale_value = 0;
     if (dict_value->GetInteger("ui-scale", &ui_scale_value))
       ui_scale = static_cast<float>(ui_scale_value) / 1000.0f;
+
+    int width = 0, height = 0;
+    dict_value->GetInteger("width", &width);
+    dict_value->GetInteger("height", &height);
+    gfx::Size resolution_in_pixels(width, height);
+
     gfx::Insets insets;
     if (ValueToInsets(*dict_value, &insets))
       insets_to_set = &insets;
     GetDisplayManager()->RegisterDisplayProperty(id,
                                                  rotation,
                                                  ui_scale,
-                                                 insets_to_set);
+                                                 insets_to_set,
+                                                 resolution_in_pixels);
   }
 }
 
@@ -182,7 +189,8 @@
 
   size_t num = display_manager->GetNumDisplays();
   for (size_t i = 0; i < num; ++i) {
-    int64 id = display_manager->GetDisplayAt(i).id();
+    const gfx::Display& display = display_manager->GetDisplayAt(i);
+    int64 id = display.id();
     ash::internal::DisplayInfo info = display_manager->GetDisplayInfo(id);
 
     scoped_ptr<base::DictionaryValue> property_value(
@@ -190,6 +198,13 @@
     property_value->SetInteger("rotation", static_cast<int>(info.rotation()));
     property_value->SetInteger("ui-scale",
                                static_cast<int>(info.ui_scale() * 1000));
+    gfx::Size resolution;
+    if (!display.IsInternal() &&
+        display_manager->GetSelectedResolutionForDisplayId(id, &resolution)) {
+      property_value->SetInteger("width", resolution.width());
+      property_value->SetInteger("height", resolution.height());
+    }
+
     if (!info.overscan_insets_in_dip().empty())
       InsetsToValue(info.overscan_insets_in_dip(), property_value.get());
     pref_data->Set(base::Int64ToString(id), property_value.release());
diff --git a/chrome/browser/chromeos/display/display_preferences_unittest.cc b/chrome/browser/chromeos/display/display_preferences_unittest.cc
index 023cfdf..a319c5b 100644
--- a/chrome/browser/chromeos/display/display_preferences_unittest.cc
+++ b/chrome/browser/chromeos/display/display_preferences_unittest.cc
@@ -183,7 +183,7 @@
   ash::internal::DisplayManager* display_manager =
       ash::Shell::GetInstance()->display_manager();
 
-  UpdateDisplay("200x200*2,200x200");
+  UpdateDisplay("200x200*2,400x300");
   int64 id1 = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id();
   gfx::Display::SetInternalDisplayId(id1);
   int64 id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
@@ -231,6 +231,11 @@
   EXPECT_EQ(1, rotation);
   EXPECT_EQ(1250, ui_scale);
 
+  // Internal display never registere the resolution.
+  int width = 0, height = 0;
+  EXPECT_FALSE(property->GetInteger("width", &width));
+  EXPECT_FALSE(property->GetInteger("height", &height));
+
   int top = 0, left = 0, bottom = 0, right = 0;
   EXPECT_TRUE(property->GetInteger("insets_top", &top));
   EXPECT_TRUE(property->GetInteger("insets_left", &left));
@@ -252,8 +257,31 @@
   EXPECT_FALSE(property->GetInteger("insets_bottom", &bottom));
   EXPECT_FALSE(property->GetInteger("insets_right", &right));
 
+  // Resolution is saved only when the resolution is set
+  // by DisplayManager::SetDisplayResolution
+  width = 0;
+  height = 0;
+  EXPECT_FALSE(property->GetInteger("width", &width));
+  EXPECT_FALSE(property->GetInteger("height", &height));
+
+  display_manager->SetDisplayResolution(id2, gfx::Size(400, 300));
+
   display_controller->SetPrimaryDisplayId(id2);
 
+  EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id1), &property));
+  width = 0;
+  height = 0;
+  // Internal dispaly shouldn't store its resolution.
+  EXPECT_FALSE(property->GetInteger("width", &width));
+  EXPECT_FALSE(property->GetInteger("height", &height));
+
+  // External dispaly's resolution must be stored this time.
+  EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id2), &property));
+  EXPECT_TRUE(property->GetInteger("width", &width));
+  EXPECT_TRUE(property->GetInteger("height", &height));
+  EXPECT_EQ(400, width);
+  EXPECT_EQ(300, height);
+
   // The layout remains the same.
   EXPECT_TRUE(displays->GetDictionary(key, &layout_value));
   EXPECT_TRUE(ash::DisplayLayout::ConvertFromValue(*layout_value,
@@ -287,7 +315,21 @@
   EXPECT_TRUE(layout_value->GetString(kPrimaryIdKey, &primary_id_str));
   EXPECT_EQ(base::Int64ToString(id2), primary_id_str);
 
-  UpdateDisplay("200x200*2,200x200");
+  EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id1), &property));
+  EXPECT_FALSE(property->GetInteger("width", &width));
+  EXPECT_FALSE(property->GetInteger("height", &height));
+
+  // External dispaly's selected resolution must not change
+  // by mirroring.
+  EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id2), &property));
+  EXPECT_TRUE(property->GetInteger("width", &width));
+  EXPECT_TRUE(property->GetInteger("height", &height));
+  EXPECT_EQ(400, width);
+  EXPECT_EQ(300, height);
+
+  // Set new display's selected resolution.
+  display_manager->SetDisplayResolution(id2 + 1, gfx::Size(500, 400));
+  UpdateDisplay("200x200*2,500x400");
   // Update key as the 2nd display gets new id.
   id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
   key = base::Int64ToString(id1) + "," + base::Int64ToString(id2);
@@ -301,6 +343,13 @@
   EXPECT_FALSE(mirrored);
   EXPECT_TRUE(layout_value->GetString(kPrimaryIdKey, &primary_id_str));
   EXPECT_EQ(base::Int64ToString(id1), primary_id_str);
+
+  // External dispaly's selected resolution must be updated.
+  EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id2), &property));
+  EXPECT_TRUE(property->GetInteger("width", &width));
+  EXPECT_TRUE(property->GetInteger("height", &height));
+  EXPECT_EQ(500, width);
+  EXPECT_EQ(400, height);
 }
 
 TEST_F(DisplayPreferencesTest, StoreForSwappedDisplay) {
diff --git a/chrome/browser/chromeos/drive/async_file_util.cc b/chrome/browser/chromeos/drive/async_file_util.cc
index a2b0e88..94192af 100644
--- a/chrome/browser/chromeos/drive/async_file_util.cc
+++ b/chrome/browser/chromeos/drive/async_file_util.cc
@@ -69,7 +69,7 @@
     const AsyncFileUtil::CreateOrOpenCallback& callback,
     base::PlatformFileError error) {
   // Because the |callback| takes PassPlatformFile as its argument, and
-  // it is necessary to garantee the pointer passed to PassPlatformFile is
+  // it is necessary to guarantee the pointer passed to PassPlatformFile is
   // alive during the |callback| invocation, here we prepare a thin adapter
   // to have PlatformFile on stack frame.
   base::PlatformFile file = base::kInvalidPlatformFileValue;
diff --git a/chrome/browser/chromeos/drive/async_file_util.h b/chrome/browser/chromeos/drive/async_file_util.h
index dfbe2d7..c59b169 100644
--- a/chrome/browser/chromeos/drive/async_file_util.h
+++ b/chrome/browser/chromeos/drive/async_file_util.h
@@ -15,7 +15,7 @@
 
 namespace internal {
 
-// The implementation of fileapis::AsyncFileUtil for Drive File System.
+// The implementation of fileapi::AsyncFileUtil for Drive File System.
 class AsyncFileUtil : public fileapi::AsyncFileUtil {
  public:
   // Callback to return the FileSystemInterface instance. This is an
diff --git a/chrome/browser/chromeos/drive/change_list_loader.cc b/chrome/browser/chromeos/drive/change_list_loader.cc
index e9ede35..bd0f423 100644
--- a/chrome/browser/chromeos/drive/change_list_loader.cc
+++ b/chrome/browser/chromeos/drive/change_list_loader.cc
@@ -72,7 +72,7 @@
     // We only start to check for updates iff the load is done.
     // I.e., we ignore checking updates if not loaded to avoid starting the
     // load without user's explicit interaction (such as opening Drive).
-    util::Log("Checking for updates");
+    util::Log(logging::LOG_INFO, "Checking for updates");
     Load(DirectoryFetchInfo(), callback);
   }
 }
@@ -212,7 +212,8 @@
     FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  util::Log("Fast-fetch complete: %s => %s",
+  util::Log(logging::LOG_INFO,
+            "Fast-fetch complete: %s => %s",
             directory_fetch_info.ToString().c_str(),
             FileErrorToString(error).c_str());
   const std::string& resource_id = directory_fetch_info.resource_id();
@@ -249,10 +250,10 @@
     google_apis::GDataErrorCode status,
     scoped_ptr<google_apis::AboutResource> about_resource) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK_EQ(util::GDataToFileError(status) == FILE_ERROR_OK,
+  DCHECK_EQ(GDataToFileError(status) == FILE_ERROR_OK,
             about_resource.get() != NULL);
 
-  if (util::GDataToFileError(status) == FILE_ERROR_OK) {
+  if (GDataToFileError(status) == FILE_ERROR_OK) {
     DCHECK(about_resource);
     last_known_remote_changestamp_ = about_resource->largest_change_id();
   }
@@ -364,7 +365,7 @@
                         base::TimeTicks::Now() - start_time);
   }
 
-  FileError error = util::GDataToFileError(status);
+  FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
     callback.Run(ScopedVector<ChangeList>(), error);
     return;
@@ -440,7 +441,8 @@
   // enough, but we log this message here, so "Fast-fetch start" and
   // "Fast-fetch complete" always match.
   // TODO(satorux): Distinguish the "not fetching at all" case.
-  util::Log("Fast-fetch start: %s; Server changestamp: %s",
+  util::Log(logging::LOG_INFO,
+            "Fast-fetch start: %s; Server changestamp: %s",
             directory_fetch_info.ToString().c_str(),
             base::Int64ToString(last_known_remote_changestamp_).c_str());
 
@@ -548,7 +550,7 @@
   DCHECK_EQ(directory_fetch_info.resource_id(),
             util::kDriveGrandRootSpecialResourceId);
 
-  FileError error = util::GDataToFileError(status);
+  FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
     callback.Run(error);
     return;
@@ -630,7 +632,9 @@
   // the initial content retrieval.
   const bool should_notify_changed_directories = is_delta_update;
 
-  util::Log("Apply change lists (is delta: %d)", is_delta_update);
+  util::Log(logging::LOG_INFO,
+            "Apply change lists (is delta: %d)",
+            is_delta_update);
   blocking_task_runner_->PostTaskAndReply(
       FROM_HERE,
       base::Bind(&ChangeListProcessor::Apply,
@@ -656,7 +660,8 @@
   DCHECK(!callback.is_null());
 
   const base::TimeDelta elapsed = base::Time::Now() - start_time;
-  util::Log("Change lists applied (elapsed time: %sms)",
+  util::Log(logging::LOG_INFO,
+            "Change lists applied (elapsed time: %sms)",
             base::Int64ToString(elapsed.InMilliseconds()).c_str());
 
   if (should_notify_changed_directories) {
diff --git a/chrome/browser/chromeos/drive/drive_app_registry.cc b/chrome/browser/chromeos/drive/drive_app_registry.cc
index 4171fd6..75b0418 100644
--- a/chrome/browser/chromeos/drive/drive_app_registry.cc
+++ b/chrome/browser/chromeos/drive/drive_app_registry.cc
@@ -147,7 +147,7 @@
     scoped_ptr<google_apis::AppList> app_list) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  FileError error = util::GDataToFileError(gdata_error);
+  FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
     // Failed to fetch the data from the server. We can do nothing here.
     return;
diff --git a/chrome/browser/chromeos/drive/drive_integration_service.cc b/chrome/browser/chromeos/drive/drive_integration_service.cc
index 27ec94b..8ea55a5 100644
--- a/chrome/browser/chromeos/drive/drive_integration_service.cc
+++ b/chrome/browser/chromeos/drive/drive_integration_service.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/chromeos/drive/logging.h"
 #include "chrome/browser/chromeos/drive/resource_metadata.h"
 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h"
+#include "chrome/browser/chromeos/profiles/profile_util.h"
 #include "chrome/browser/download/download_service.h"
 #include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/download/download_util.h"
@@ -52,7 +53,7 @@
 bool IsDriveEnabledForProfile(Profile* profile) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  if (!google_apis::AuthService::CanAuthenticate(profile))
+  if (!chromeos::IsProfileAssociatedWithGaiaAccount(profile))
     return false;
 
   // Disable Drive if preference is set.  This can happen with commandline flag
@@ -166,6 +167,7 @@
         blocking_task_runner_.get(),
         GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction),
         GURL(google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction),
+        GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
         GetDriveUserAgent()));
   } else {
     drive_service_.reset(new GDataWapiService(
@@ -270,7 +272,7 @@
     drive_app_registry_->Update();
 
   const char* status = (enabled ? "enabled" : "disabled");
-  util::Log("Push notification is %s", status);
+  util::Log(logging::LOG_INFO, "Push notification is %s", status);
 }
 
 bool DriveIntegrationService::IsDriveEnabled() {
@@ -335,7 +337,7 @@
       drive_mount_point);
 
   if (success) {
-    util::Log("Drive mount point is added");
+    util::Log(logging::LOG_INFO, "Drive mount point is added");
     FOR_EACH_OBSERVER(DriveIntegrationServiceObserver, observers_,
                       OnFileSystemMounted());
   }
@@ -355,7 +357,7 @@
 
   mount_points->RevokeFileSystem(
       util::GetDriveMountPointPath().BaseName().AsUTF8Unsafe());
-  util::Log("Drive mount point is removed");
+  util::Log(logging::LOG_INFO, "Drive mount point is removed");
 }
 
 void DriveIntegrationService::InitializeAfterMetadataInitialized(
@@ -384,7 +386,7 @@
     const bool registered =
         drive_notification_manager->push_notification_registered();
     const char* status = (registered ? "registered" : "not registered");
-    util::Log("Push notification is %s", status);
+    util::Log(logging::LOG_INFO, "Push notification is %s", status);
 
     if (drive_notification_manager->push_notification_enabled())
       drive_app_registry_->Update();
diff --git a/chrome/browser/chromeos/drive/fake_file_system.cc b/chrome/browser/chromeos/drive/fake_file_system.cc
index 2b8c0dd..4b6f6e4 100644
--- a/chrome/browser/chromeos/drive/fake_file_system.cc
+++ b/chrome/browser/chromeos/drive/fake_file_system.cc
@@ -271,7 +271,7 @@
     scoped_ptr<google_apis::ResourceEntry> gdata_entry) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  FileError error = util::GDataToFileError(gdata_error);
+  FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
     completion_callback.Run(error);
     return;
@@ -309,7 +309,7 @@
     google_apis::GDataErrorCode gdata_error,
     const base::FilePath& temp_file) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  completion_callback.Run(util::GDataToFileError(gdata_error));
+  completion_callback.Run(GDataToFileError(gdata_error));
 }
 
 // Implementation of GetResourceEntryByPath.
@@ -319,7 +319,7 @@
     scoped_ptr<google_apis::AboutResource> about_resource) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  FileError error = util::GDataToFileError(gdata_error);
+  FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
     callback.Run(error, scoped_ptr<ResourceEntry>());
     return;
@@ -360,7 +360,7 @@
     scoped_ptr<google_apis::ResourceList> resource_list) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  FileError error = util::GDataToFileError(gdata_error);
+  FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
     callback.Run(error, scoped_ptr<ResourceEntry>());
     return;
diff --git a/chrome/browser/chromeos/drive/file_cache.cc b/chrome/browser/chromeos/drive/file_cache.cc
index ed94c02..7b190c0 100644
--- a/chrome/browser/chromeos/drive/file_cache.cc
+++ b/chrome/browser/chromeos/drive/file_cache.cc
@@ -518,7 +518,7 @@
     }
   }
   if (!FreeDiskSpaceIfNeededFor(file_size))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   FileCacheEntry cache_entry;
   storage_->GetCacheEntry(resource_id, &cache_entry);
diff --git a/chrome/browser/chromeos/drive/file_cache.h b/chrome/browser/chromeos/drive/file_cache.h
index 0c32481..41573ee 100644
--- a/chrome/browser/chromeos/drive/file_cache.h
+++ b/chrome/browser/chromeos/drive/file_cache.h
@@ -16,12 +16,8 @@
 #include "chrome/browser/chromeos/drive/file_errors.h"
 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h"
 
-class Profile;
-
 namespace base {
-
 class SequencedTaskRunner;
-
 }  // namespace base
 
 namespace drive {
@@ -293,8 +289,8 @@
   DISALLOW_COPY_AND_ASSIGN(FileCache);
 };
 
-// The minimum free space to keep. FileSystem::GetFileByPath() returns
-// GDATA_FILE_ERROR_NO_SPACE if the available space is smaller than
+// The minimum free space to keep. Operations that add cache files return
+// FILE_ERROR_NO_LOCAL_SPACE if the available space is smaller than
 // this value.
 //
 // Copied from cryptohome/homedirs.h.
diff --git a/chrome/browser/chromeos/drive/file_cache_unittest.cc b/chrome/browser/chromeos/drive/file_cache_unittest.cc
index f0ae710..3443506 100644
--- a/chrome/browser/chromeos/drive/file_cache_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_cache_unittest.cc
@@ -775,7 +775,8 @@
   std::string md5("abcdef0123456789");
 
   // Try to store an existing file.
-  TestStoreToCache(resource_id, md5, dummy_file_path_, FILE_ERROR_NO_SPACE,
+  TestStoreToCache(resource_id, md5, dummy_file_path_,
+                   FILE_ERROR_NO_LOCAL_SPACE,
                    TEST_CACHE_STATE_NONE);
 
   // Verify that there's no files added.
diff --git a/chrome/browser/chromeos/drive/file_errors.cc b/chrome/browser/chromeos/drive/file_errors.cc
index 56bc362..05d76f9 100644
--- a/chrome/browser/chromeos/drive/file_errors.cc
+++ b/chrome/browser/chromeos/drive/file_errors.cc
@@ -34,8 +34,8 @@
     case FILE_ERROR_NO_MEMORY:
       return "FILE_ERROR_NO_MEMORY";
 
-    case FILE_ERROR_NO_SPACE:
-      return "FILE_ERROR_NO_SPACE";
+    case FILE_ERROR_NO_SERVER_SPACE:
+      return "FILE_ERROR_NO_SERVER_SPACE";
 
     case FILE_ERROR_NOT_A_DIRECTORY:
       return "FILE_ERROR_NOT_A_DIRECTORY";
@@ -60,6 +60,9 @@
 
     case FILE_ERROR_NO_CONNECTION:
       return "FILE_ERROR_NO_CONNECTION";
+
+    case FILE_ERROR_NO_LOCAL_SPACE:
+      return "FILE_ERROR_NO_LOCAL_SPACE";
   }
 
   NOTREACHED();
@@ -92,7 +95,7 @@
     case FILE_ERROR_NO_MEMORY:
       return base::PLATFORM_FILE_ERROR_NO_MEMORY;
 
-    case FILE_ERROR_NO_SPACE:
+    case FILE_ERROR_NO_SERVER_SPACE:
       return base::PLATFORM_FILE_ERROR_NO_SPACE;
 
     case FILE_ERROR_NOT_A_DIRECTORY:
@@ -118,10 +121,37 @@
 
     case FILE_ERROR_NO_CONNECTION:
       return base::PLATFORM_FILE_ERROR_FAILED;
+
+    case FILE_ERROR_NO_LOCAL_SPACE:
+      return base::PLATFORM_FILE_ERROR_FAILED;
   }
 
   NOTREACHED();
   return base::PLATFORM_FILE_ERROR_FAILED;
 }
 
+FileError GDataToFileError(google_apis::GDataErrorCode status) {
+  switch (status) {
+    case google_apis::HTTP_SUCCESS:
+    case google_apis::HTTP_CREATED:
+    case google_apis::HTTP_NO_CONTENT:
+      return FILE_ERROR_OK;
+    case google_apis::HTTP_UNAUTHORIZED:
+    case google_apis::HTTP_FORBIDDEN:
+      return FILE_ERROR_ACCESS_DENIED;
+    case google_apis::HTTP_NOT_FOUND:
+      return FILE_ERROR_NOT_FOUND;
+    case google_apis::HTTP_NOT_IMPLEMENTED:
+      return FILE_ERROR_INVALID_OPERATION;
+    case google_apis::GDATA_CANCELLED:
+      return FILE_ERROR_ABORT;
+    case google_apis::GDATA_NO_CONNECTION:
+      return FILE_ERROR_NO_CONNECTION;
+    case google_apis::GDATA_NO_SPACE:
+      return FILE_ERROR_NO_SERVER_SPACE;
+    default:
+      return FILE_ERROR_FAILED;
+  }
+}
+
 }  // namespace drive
diff --git a/chrome/browser/chromeos/drive/file_errors.h b/chrome/browser/chromeos/drive/file_errors.h
index 0f1a422..08cf5a1 100644
--- a/chrome/browser/chromeos/drive/file_errors.h
+++ b/chrome/browser/chromeos/drive/file_errors.h
@@ -7,6 +7,7 @@
 
 #include "base/callback_forward.h"
 #include "base/platform_file.h"
+#include "chrome/browser/google_apis/gdata_errorcode.h"
 
 namespace drive {
 
@@ -19,7 +20,7 @@
   FILE_ERROR_ACCESS_DENIED = -5,
   FILE_ERROR_TOO_MANY_OPENED = -6,
   FILE_ERROR_NO_MEMORY = -7,
-  FILE_ERROR_NO_SPACE = -8,
+  FILE_ERROR_NO_SERVER_SPACE = -8,
   FILE_ERROR_NOT_A_DIRECTORY = -9,
   FILE_ERROR_INVALID_OPERATION = -10,
   FILE_ERROR_SECURITY = -11,
@@ -28,6 +29,7 @@
   FILE_ERROR_NOT_EMPTY = -14,
   FILE_ERROR_INVALID_URL = -15,
   FILE_ERROR_NO_CONNECTION = -16,
+  FILE_ERROR_NO_LOCAL_SPACE = -17,
 };
 
 // Used as callbacks for file operations.
@@ -39,6 +41,9 @@
 // Returns a PlatformFileError that corresponds to the FileError provided.
 base::PlatformFileError FileErrorToPlatformError(FileError error);
 
+// Converts GData error code into Drive file error code.
+FileError GDataToFileError(google_apis::GDataErrorCode status);
+
 }  // namespace drive
 
 #endif  // CHROME_BROWSER_CHROMEOS_DRIVE_FILE_ERRORS_H_
diff --git a/chrome/browser/chromeos/drive/file_system.cc b/chrome/browser/chromeos/drive/file_system.cc
index 644209c..6b89008 100644
--- a/chrome/browser/chromeos/drive/file_system.cc
+++ b/chrome/browser/chromeos/drive/file_system.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/drive/job_scheduler.h"
 #include "chrome/browser/chromeos/drive/remove_stale_cache_files.h"
+#include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
 #include "chrome/browser/chromeos/drive/search_metadata.h"
 #include "chrome/browser/chromeos/drive/sync_client.h"
 #include "chrome/browser/drive/drive_api_util.h"
@@ -78,8 +79,7 @@
   if (!file_util::GetFileInfo(local_cache_path, &file_info))
     return FILE_ERROR_NOT_FOUND;
 
-  util::ConvertPlatformFileInfoToResourceEntry(file_info,
-                                               entry->mutable_file_info());
+  SetPlatformFileInfoToResourceEntry(file_info, entry);
   return FILE_ERROR_OK;
 }
 
@@ -703,7 +703,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  FileError error = util::GDataToFileError(status);
+  FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
     callback.Run(error, -1, -1);
     return;
@@ -767,7 +767,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  FileError error = util::GDataToFileError(status);
+  FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
     callback.Run(error, GURL());
     return;
diff --git a/chrome/browser/chromeos/drive/file_system/copy_operation.cc b/chrome/browser/chromeos/drive/file_system/copy_operation.cc
index 851a9ac..b0d007c 100644
--- a/chrome/browser/chromeos/drive/file_system/copy_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/copy_operation.cc
@@ -256,7 +256,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  FileError error = util::GDataToFileError(status);
+  FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
     callback.Run(error);
     return;
@@ -381,7 +381,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  FileError error = util::GDataToFileError(status);
+  FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
     callback.Run(error);
     return;
diff --git a/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc b/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc
index 20d4e7c..b9ced16 100644
--- a/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc
@@ -187,7 +187,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  FileError error = util::GDataToFileError(gdata_error);
+  FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
     callback.Run(error);
     return;
diff --git a/chrome/browser/chromeos/drive/file_system/create_file_operation.cc b/chrome/browser/chromeos/drive/file_system/create_file_operation.cc
index 5ff0039..404cf4d 100644
--- a/chrome/browser/chromeos/drive/file_system/create_file_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/create_file_operation.cc
@@ -199,7 +199,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  FileError error = util::GDataToFileError(gdata_error);
+  FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
     callback.Run(error);
     return;
diff --git a/chrome/browser/chromeos/drive/file_system/download_operation.cc b/chrome/browser/chromeos/drive/file_system/download_operation.cc
index 43fd3cf..eaa2cb5 100644
--- a/chrome/browser/chromeos/drive/file_system/download_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/download_operation.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/drive/job_scheduler.h"
+#include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
 #include "chrome/browser/chromeos/drive/resource_metadata.h"
 #include "chrome/browser/google_apis/gdata_errorcode.h"
 #include "content/public/browser/browser_thread.h"
@@ -55,14 +56,17 @@
   // document.
   if (entry->file_specific_info().is_hosted_document()) {
     base::FilePath gdoc_file_path;
+    base::PlatformFileInfo file_info;
     if (!file_util::CreateTemporaryFileInDir(temporary_file_directory,
                                              &gdoc_file_path) ||
         !util::CreateGDocFile(gdoc_file_path,
                               GURL(entry->file_specific_info().alternate_url()),
-                              entry->resource_id()))
+                              entry->resource_id()) ||
+        !file_util::GetFileInfo(gdoc_file_path, &file_info))
       return FILE_ERROR_FAILED;
 
     *cache_file_path = gdoc_file_path;
+    SetPlatformFileInfoToResourceEntry(file_info, entry);
     return FILE_ERROR_OK;
   }
 
@@ -89,12 +93,8 @@
   // the drive::FS side is also converted to run fully on blocking pool.
   if (cache_entry.is_dirty()) {
     base::PlatformFileInfo file_info;
-    if (file_util::GetFileInfo(*cache_file_path, &file_info)) {
-      PlatformFileInfoProto entry_file_info;
-      util::ConvertPlatformFileInfoToResourceEntry(file_info,
-                                                   &entry_file_info);
-      *entry->mutable_file_info() = entry_file_info;
-    }
+    if (file_util::GetFileInfo(*cache_file_path, &file_info))
+      SetPlatformFileInfoToResourceEntry(file_info, entry);
   }
 
   return FILE_ERROR_OK;
@@ -164,7 +164,7 @@
 
   // Ensure enough space in the cache.
   if (!cache->FreeDiskSpaceIfNeededFor(expected_file_size))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   // Create the temporary file which will store the downloaded content.
   return CreateTemporaryReadableFileInDir(
@@ -185,7 +185,7 @@
     base::FilePath* cache_file_path) {
   DCHECK(cache);
 
-  FileError error = util::GDataToFileError(gdata_error);
+  FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
     base::DeleteFile(downloaded_file_path, false /* recursive */);
     return error;
diff --git a/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc
index 5fddded..2b55291 100644
--- a/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc
@@ -89,7 +89,7 @@
           &error, &file_path, &entry));
   test_util::RunBlockingPoolTask();
 
-  EXPECT_EQ(FILE_ERROR_NO_SPACE, error);
+  EXPECT_EQ(FILE_ERROR_NO_LOCAL_SPACE, error);
 }
 
 TEST_F(DownloadOperationTest,
@@ -183,7 +183,7 @@
           &error, &file_path, &entry));
   test_util::RunBlockingPoolTask();
 
-  EXPECT_EQ(FILE_ERROR_NO_SPACE, error);
+  EXPECT_EQ(FILE_ERROR_NO_LOCAL_SPACE, error);
 }
 
 TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_FromCache) {
diff --git a/chrome/browser/chromeos/drive/file_system/move_operation.cc b/chrome/browser/chromeos/drive/file_system/move_operation.cc
index c7700d0..ded4aac 100644
--- a/chrome/browser/chromeos/drive/file_system/move_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/move_operation.cc
@@ -168,7 +168,7 @@
                                   google_apis::GDataErrorCode status) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  const FileError error = util::GDataToFileError(status);
+  const FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
     callback.Run(error, base::FilePath());
     return;
@@ -198,7 +198,7 @@
                                           google_apis::GDataErrorCode status) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  const FileError error = util::GDataToFileError(status);
+  const FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
     callback.Run(error, base::FilePath());
     return;
@@ -231,7 +231,7 @@
     const FileOperationCallback& callback,
     google_apis::GDataErrorCode status) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  callback.Run(util::GDataToFileError(status));
+  callback.Run(GDataToFileError(status));
 }
 
 }  // namespace file_system
diff --git a/chrome/browser/chromeos/drive/file_system/remove_operation.cc b/chrome/browser/chromeos/drive/file_system/remove_operation.cc
index 6d5f2b4..5c07ccb 100644
--- a/chrome/browser/chromeos/drive/file_system/remove_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/remove_operation.cc
@@ -122,7 +122,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  FileError error = util::GDataToFileError(status);
+  FileError error = GDataToFileError(status);
   if (error != FILE_ERROR_OK) {
     callback.Run(error);
     return;
diff --git a/chrome/browser/chromeos/drive/file_system/search_operation.cc b/chrome/browser/chromeos/drive/file_system/search_operation.cc
index e463ee8..fd9fb34 100644
--- a/chrome/browser/chromeos/drive/file_system/search_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/search_operation.cc
@@ -115,7 +115,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  FileError error = util::GDataToFileError(gdata_error);
+  FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
     callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >());
     return;
diff --git a/chrome/browser/chromeos/drive/file_system/touch_operation.cc b/chrome/browser/chromeos/drive/file_system/touch_operation.cc
index a74f970..b2ce74f 100644
--- a/chrome/browser/chromeos/drive/file_system/touch_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/touch_operation.cc
@@ -92,7 +92,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  FileError error = util::GDataToFileError(gdata_error);
+  FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
     callback.Run(error);
     return;
diff --git a/chrome/browser/chromeos/drive/file_system/update_operation.cc b/chrome/browser/chromeos/drive/file_system/update_operation.cc
index d3e9cd9..027c26e 100644
--- a/chrome/browser/chromeos/drive/file_system/update_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/update_operation.cc
@@ -164,7 +164,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  FileError drive_error = util::GDataToFileError(error);
+  FileError drive_error = GDataToFileError(error);
   if (drive_error != FILE_ERROR_OK) {
     callback.Run(drive_error);
     return;
diff --git a/chrome/browser/chromeos/drive/file_system_backend_delegate.cc b/chrome/browser/chromeos/drive/file_system_backend_delegate.cc
index b2462fc..4c4ed96 100644
--- a/chrome/browser/chromeos/drive/file_system_backend_delegate.cc
+++ b/chrome/browser/chromeos/drive/file_system_backend_delegate.cc
@@ -17,7 +17,6 @@
 #include "webkit/browser/blob/file_stream_reader.h"
 #include "webkit/browser/fileapi/async_file_util.h"
 #include "webkit/browser/fileapi/file_system_context.h"
-#include "webkit/browser/fileapi/file_system_task_runners.h"
 #include "webkit/browser/fileapi/file_system_url.h"
 
 using content::BrowserThread;
@@ -58,7 +57,7 @@
   return scoped_ptr<webkit_blob::FileStreamReader>(
       new internal::WebkitFileStreamReaderImpl(
           base::Bind(&util::GetFileSystemByProfileId, profile_id_),
-          context->task_runners()->file_task_runner(),
+          context->default_file_task_runner(),
           file_path, offset, expected_modification_time));
 }
 
@@ -77,7 +76,7 @@
   return scoped_ptr<fileapi::FileStreamWriter>(
       new internal::WebkitFileStreamWriterImpl(
           base::Bind(&util::GetFileSystemByProfileId, profile_id_),
-          context->task_runners()->file_task_runner(),file_path, offset));
+          context->default_file_task_runner(),file_path, offset));
 }
 
 }  // namespace drive
diff --git a/chrome/browser/chromeos/drive/file_system_util.cc b/chrome/browser/chromeos/drive/file_system_util.cc
index 2fbf077..e84c860 100644
--- a/chrome/browser/chromeos/drive/file_system_util.cc
+++ b/chrome/browser/chromeos/drive/file_system_util.cc
@@ -361,53 +361,6 @@
   }
 }
 
-FileError GDataToFileError(google_apis::GDataErrorCode status) {
-  switch (status) {
-    case google_apis::HTTP_SUCCESS:
-    case google_apis::HTTP_CREATED:
-    case google_apis::HTTP_NO_CONTENT:
-      return FILE_ERROR_OK;
-    case google_apis::HTTP_UNAUTHORIZED:
-    case google_apis::HTTP_FORBIDDEN:
-      return FILE_ERROR_ACCESS_DENIED;
-    case google_apis::HTTP_NOT_FOUND:
-      return FILE_ERROR_NOT_FOUND;
-    case google_apis::HTTP_NOT_IMPLEMENTED:
-      return FILE_ERROR_INVALID_OPERATION;
-    case google_apis::GDATA_CANCELLED:
-      return FILE_ERROR_ABORT;
-    case google_apis::GDATA_NO_CONNECTION:
-      return FILE_ERROR_NO_CONNECTION;
-    default:
-      return FILE_ERROR_FAILED;
-  }
-}
-
-void ConvertResourceEntryToPlatformFileInfo(
-    const PlatformFileInfoProto& entry,
-    base::PlatformFileInfo* file_info) {
-  file_info->size = entry.size();
-  file_info->is_directory = entry.is_directory();
-  file_info->is_symbolic_link = entry.is_symbolic_link();
-  file_info->last_modified = base::Time::FromInternalValue(
-      entry.last_modified());
-  file_info->last_accessed = base::Time::FromInternalValue(
-      entry.last_accessed());
-  file_info->creation_time = base::Time::FromInternalValue(
-      entry.creation_time());
-}
-
-void ConvertPlatformFileInfoToResourceEntry(
-    const base::PlatformFileInfo& file_info,
-    PlatformFileInfoProto* entry) {
-  entry->set_size(file_info.size);
-  entry->set_is_directory(file_info.is_directory);
-  entry->set_is_symbolic_link(file_info.is_symbolic_link);
-  entry->set_last_modified(file_info.last_modified.ToInternalValue());
-  entry->set_last_accessed(file_info.last_accessed.ToInternalValue());
-  entry->set_creation_time(file_info.creation_time.ToInternalValue());
-}
-
 void EmptyFileOperationCallback(FileError error) {
 }
 
diff --git a/chrome/browser/chromeos/drive/file_system_util.h b/chrome/browser/chromeos/drive/file_system_util.h
index 066442a..e9879ad 100644
--- a/chrome/browser/chromeos/drive/file_system_util.h
+++ b/chrome/browser/chromeos/drive/file_system_util.h
@@ -9,14 +9,12 @@
 
 #include "base/callback_forward.h"
 #include "chrome/browser/chromeos/drive/file_errors.h"
-#include "chrome/browser/google_apis/gdata_errorcode.h"
 #include "url/gurl.h"
 
 class Profile;
 
 namespace base {
 class FilePath;
-struct PlatformFileInfo;
 }
 
 namespace fileapi {
@@ -26,7 +24,6 @@
 namespace drive {
 
 class FileSystemInterface;
-class PlatformFileInfoProto;
 class ResourceEntry;
 
 namespace util {
@@ -182,19 +179,6 @@
                            const base::FilePath& directory,
                            const FileOperationCallback& callback);
 
-// Converts GData error code into file platform error code.
-FileError GDataToFileError(google_apis::GDataErrorCode status);
-
-// Converts the resource entry to the platform file.
-void ConvertResourceEntryToPlatformFileInfo(
-    const PlatformFileInfoProto& entry,
-    base::PlatformFileInfo* file_info);
-
-// Converts the platform file info to the resource entry.
-void ConvertPlatformFileInfoToResourceEntry(
-    const base::PlatformFileInfo& file_info,
-    PlatformFileInfoProto* entry);
-
 // Does nothing with |error|. Used with functions taking FileOperationCallback.
 void EmptyFileOperationCallback(FileError error);
 
diff --git a/chrome/browser/chromeos/drive/file_system_util_unittest.cc b/chrome/browser/chromeos/drive/file_system_util_unittest.cc
index 215917e..00f63a6 100644
--- a/chrome/browser/chromeos/drive/file_system_util_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_system_util_unittest.cc
@@ -16,7 +16,6 @@
 #include "webkit/browser/fileapi/external_mount_points.h"
 #include "webkit/browser/fileapi/file_system_backend.h"
 #include "webkit/browser/fileapi/file_system_context.h"
-#include "webkit/browser/fileapi/file_system_task_runners.h"
 #include "webkit/browser/fileapi/file_system_url.h"
 #include "webkit/browser/fileapi/isolated_context.h"
 #include "webkit/browser/fileapi/mock_file_system_options.h"
@@ -99,7 +98,8 @@
       fileapi::ExternalMountPoints::CreateRefCounted();
   scoped_refptr<fileapi::FileSystemContext> context(
       new fileapi::FileSystemContext(
-          fileapi::FileSystemTaskRunners::CreateMockTaskRunners(),
+          base::MessageLoopProxy::current().get(),
+          base::MessageLoopProxy::current().get(),
           mount_points.get(),
           NULL,  // special_storage_policy
           NULL,  // quota_manager_proxy,
diff --git a/chrome/browser/chromeos/drive/file_task_executor.cc b/chrome/browser/chromeos/drive/file_task_executor.cc
index a42f71c..c3c5874 100644
--- a/chrome/browser/chromeos/drive/file_task_executor.cc
+++ b/chrome/browser/chromeos/drive/file_task_executor.cc
@@ -36,7 +36,7 @@
 
 void FileTaskExecutor::Execute(
     const std::vector<FileSystemURL>& file_urls,
-    const file_handler_util::FileTaskFinishedCallback& done) {
+    const file_manager::file_tasks::FileTaskFinishedCallback& done) {
   std::vector<base::FilePath> paths;
   for (size_t i = 0; i < file_urls.size(); ++i) {
     base::FilePath path = util::ExtractDrivePathFromFileSystemUrl(file_urls[i]);
diff --git a/chrome/browser/chromeos/drive/file_task_executor.h b/chrome/browser/chromeos/drive/file_task_executor.h
index 35b5440..ce9c459 100644
--- a/chrome/browser/chromeos/drive/file_task_executor.h
+++ b/chrome/browser/chromeos/drive/file_task_executor.h
@@ -10,7 +10,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/drive/file_errors.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_handler_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
 #include "chrome/browser/google_apis/gdata_errorcode.h"
 
 namespace drive {
@@ -27,8 +27,9 @@
   FileTaskExecutor(Profile* profile, const std::string& app_id);
 
   // Executes file tasks, runs |done| and deletes |this|.
-  void Execute(const std::vector<fileapi::FileSystemURL>& file_urls,
-               const file_handler_util::FileTaskFinishedCallback& done);
+  void Execute(
+      const std::vector<fileapi::FileSystemURL>& file_urls,
+      const file_manager::file_tasks::FileTaskFinishedCallback& done);
 
  private:
   ~FileTaskExecutor();
@@ -44,7 +45,7 @@
   Profile* profile_;
   std::string app_id_;
   int current_index_;
-  file_handler_util::FileTaskFinishedCallback done_;
+  file_manager::file_tasks::FileTaskFinishedCallback done_;
 
   base::WeakPtrFactory<FileTaskExecutor> weak_ptr_factory_;
 };
diff --git a/chrome/browser/chromeos/drive/fileapi_worker.cc b/chrome/browser/chromeos/drive/fileapi_worker.cc
index 3b952e9..a9039d0 100644
--- a/chrome/browser/chromeos/drive/fileapi_worker.cc
+++ b/chrome/browser/chromeos/drive/fileapi_worker.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/chromeos/drive/file_errors.h"
 #include "chrome/browser/chromeos/drive/file_system_interface.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
 #include "content/public/browser/browser_thread.h"
 #include "webkit/common/fileapi/directory_entry.h"
 
@@ -60,7 +61,7 @@
 
   DCHECK(entry);
   base::PlatformFileInfo file_info;
-  util::ConvertResourceEntryToPlatformFileInfo(entry->file_info(), &file_info);
+  ConvertResourceEntryToPlatformFileInfo(*entry, &file_info);
   callback.Run(base::PLATFORM_FILE_OK, file_info);
 }
 
@@ -118,7 +119,7 @@
   // we have to opt out from this check. We do this by unsetting last_modified
   // value in the file info passed to the CreateSnapshot caller.
   base::PlatformFileInfo file_info;
-  util::ConvertResourceEntryToPlatformFileInfo(entry->file_info(), &file_info);
+  ConvertResourceEntryToPlatformFileInfo(*entry, &file_info);
   file_info.last_modified = base::Time();
 
   // If the file is a hosted document, a temporary JSON file is created to
diff --git a/chrome/browser/chromeos/drive/job_scheduler.cc b/chrome/browser/chromeos/drive/job_scheduler.cc
index c8e48af..4028164 100644
--- a/chrome/browser/chromeos/drive/job_scheduler.cc
+++ b/chrome/browser/chromeos/drive/job_scheduler.cc
@@ -8,6 +8,7 @@
 #include "base/prefs/pref_service.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/drive/logging.h"
 #include "chrome/browser/google_apis/drive_api_parser.h"
@@ -692,7 +693,12 @@
   QueueType queue_type = GetJobQueueType(job_info.job_type);
   queue_[queue_type]->Push(job_id, job_entry->context.type);
 
-  util::Log("Job queued: %s - %s", job_info.ToString().c_str(),
+  const std::string retry_prefix = job_entry->retry_count > 0 ?
+      base::StringPrintf(" (retry %d)", job_entry->retry_count) : "";
+  util::Log(logging::LOG_INFO,
+            "Job queued%s: %s - %s",
+            retry_prefix.c_str(),
+            job_info.ToString().c_str(),
             GetQueueInfo(queue_type).c_str());
 }
 
@@ -727,7 +733,8 @@
 
   entry->cancel_callback = entry->task.Run();
 
-  util::Log("Job started: %s - %s",
+  util::Log(logging::LOG_INFO,
+            "Job started: %s - %s",
             job_info->ToString().c_str(),
             GetQueueInfo(queue_type).c_str());
 }
@@ -805,7 +812,9 @@
   queue_[queue_type]->MarkFinished(job_id);
 
   const base::TimeDelta elapsed = base::Time::Now() - job_info->start_time;
-  util::Log("Job done: %s => %s (elapsed time: %sms) - %s",
+  bool success = (GDataToFileError(error) == FILE_ERROR_OK);
+  util::Log(success ? logging::LOG_INFO : logging::LOG_WARNING,
+            "Job done: %s => %s (elapsed time: %sms) - %s",
             job_info->ToString().c_str(),
             GDataErrorCodeToString(error).c_str(),
             base::Int64ToString(elapsed.InMilliseconds()).c_str(),
@@ -1014,7 +1023,8 @@
 
   const base::TimeDelta elapsed = base::Time::Now() - job->job_info.start_time;
   const QueueType queue_type = GetJobQueueType(job->job_info.job_type);
-  util::Log("Job aborted: %s => %s (elapsed time: %sms) - %s",
+  util::Log(logging::LOG_INFO,
+            "Job aborted: %s => %s (elapsed time: %sms) - %s",
             job->job_info.ToString().c_str(),
             GDataErrorCodeToString(error).c_str(),
             base::Int64ToString(elapsed.InMilliseconds()).c_str(),
@@ -1038,7 +1048,7 @@
                                  google_apis::GDataErrorCode error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   FOR_EACH_OBSERVER(JobListObserver, observer_list_,
-                    OnJobDone(job_info, util::GDataToFileError(error)));
+                    OnJobDone(job_info, GDataToFileError(error)));
 }
 
 void JobScheduler::NotifyJobUpdated(const JobInfo& job_info) {
diff --git a/chrome/browser/chromeos/drive/logging.cc b/chrome/browser/chromeos/drive/logging.cc
index 48702a1..40c205a 100644
--- a/chrome/browser/chromeos/drive/logging.cc
+++ b/chrome/browser/chromeos/drive/logging.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/drive/logging.h"
 
+#include <stdarg.h>   // va_list
+
 #include "base/lazy_instance.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/drive/event_logger.h"
@@ -17,7 +19,7 @@
 
 }  // namespace
 
-void Log(const char* format, ...) {
+void Log(logging::LogSeverity severity, const char* format, ...) {
   std::string what;
 
   va_list args;
@@ -25,10 +27,12 @@
   base::StringAppendV(&what, format, args);
   va_end(args);
 
+  DVLOG(1) << what;
+
   // On thread-safety: LazyInstance guarantees thread-safety for the object
   // creation. EventLogger::Log() internally maintains the lock.
   EventLogger* ptr = g_logger.Pointer();
-  ptr->Log("%s", what.c_str());
+  ptr->Log(severity, what);
 }
 
 std::vector<EventLogger::Event> GetLogHistory() {
diff --git a/chrome/browser/chromeos/drive/logging.h b/chrome/browser/chromeos/drive/logging.h
index a2e4625..1e6964d 100644
--- a/chrome/browser/chromeos/drive/logging.h
+++ b/chrome/browser/chromeos/drive/logging.h
@@ -17,7 +17,8 @@
 
 // Logs a message using printf format.
 // This function can be called from any thread.
-void Log(const char* format, ...) PRINTF_FORMAT(1, 2);
+void Log(
+    logging::LogSeverity severity, const char* format, ...) PRINTF_FORMAT(2, 3);
 
 // Returns the log history.
 // This function can be called from any thread.
diff --git a/chrome/browser/chromeos/drive/resource_entry_conversion.cc b/chrome/browser/chromeos/drive/resource_entry_conversion.cc
index a21b456..216ff90 100644
--- a/chrome/browser/chromeos/drive/resource_entry_conversion.cc
+++ b/chrome/browser/chromeos/drive/resource_entry_conversion.cc
@@ -8,14 +8,12 @@
 #include <string>
 
 #include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
+#include "base/platform_file.h"
+#include "base/time/time.h"
 #include "chrome/browser/chromeos/drive/drive.pb.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/drive/drive_api_util.h"
 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
-#include "net/base/escape.h"
-#include "url/gurl.h"
 
 namespace drive {
 
@@ -123,4 +121,28 @@
   return true;
 }
 
+void ConvertResourceEntryToPlatformFileInfo(const ResourceEntry& entry,
+                                            base::PlatformFileInfo* file_info) {
+  file_info->size = entry.file_info().size();
+  file_info->is_directory = entry.file_info().is_directory();
+  file_info->is_symbolic_link = entry.file_info().is_symbolic_link();
+  file_info->last_modified = base::Time::FromInternalValue(
+      entry.file_info().last_modified());
+  file_info->last_accessed = base::Time::FromInternalValue(
+      entry.file_info().last_accessed());
+  file_info->creation_time = base::Time::FromInternalValue(
+      entry.file_info().creation_time());
+}
+
+void SetPlatformFileInfoToResourceEntry(const base::PlatformFileInfo& file_info,
+                                        ResourceEntry* entry) {
+  PlatformFileInfoProto* entry_file_info = entry->mutable_file_info();
+  entry_file_info->set_size(file_info.size);
+  entry_file_info->set_is_directory(file_info.is_directory);
+  entry_file_info->set_is_symbolic_link(file_info.is_symbolic_link);
+  entry_file_info->set_last_modified(file_info.last_modified.ToInternalValue());
+  entry_file_info->set_last_accessed(file_info.last_accessed.ToInternalValue());
+  entry_file_info->set_creation_time(file_info.creation_time.ToInternalValue());
+}
+
 }  // namespace drive
diff --git a/chrome/browser/chromeos/drive/resource_entry_conversion.h b/chrome/browser/chromeos/drive/resource_entry_conversion.h
index 578cbaf..06a6730 100644
--- a/chrome/browser/chromeos/drive/resource_entry_conversion.h
+++ b/chrome/browser/chromeos/drive/resource_entry_conversion.h
@@ -5,6 +5,10 @@
 #ifndef CHROME_BROWSER_CHROMEOS_DRIVE_RESOURCE_ENTRY_CONVERSION_H_
 #define CHROME_BROWSER_CHROMEOS_DRIVE_RESOURCE_ENTRY_CONVERSION_H_
 
+namespace base {
+struct PlatformFileInfo;
+}
+
 namespace google_apis {
 class ResourceEntry;
 }
@@ -17,6 +21,15 @@
 bool ConvertToResourceEntry(const google_apis::ResourceEntry& input,
                             ResourceEntry* output);
 
+// Converts the resource entry to the platform file info.
+void ConvertResourceEntryToPlatformFileInfo(const ResourceEntry& entry,
+                                            base::PlatformFileInfo* file_info);
+
+// Converts the platform file info and sets it to the .file_info field of
+// the resource entry.
+void SetPlatformFileInfoToResourceEntry(const base::PlatformFileInfo& file_info,
+                                        ResourceEntry* entry);
+
 }  // namespace drive
 
 #endif  // CHROME_BROWSER_CHROMEOS_DRIVE_RESOURCE_ENTRY_CONVERSION_H_
diff --git a/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc b/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc
index 9a8bbe3..372be9b 100644
--- a/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc
+++ b/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc
@@ -5,13 +5,13 @@
 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
 
 #include "base/files/file_path.h"
+#include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/drive/drive.pb.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/drive/test_util.h"
 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
 
 namespace drive {
 
@@ -336,4 +336,52 @@
   EXPECT_TRUE(entry.shared_with_me());
 }
 
+TEST(ResourceEntryConversionTest, ToPlatformFileInfo) {
+  ResourceEntry entry;
+  entry.mutable_file_info()->set_size(12345);
+  entry.mutable_file_info()->set_is_directory(true);
+  entry.mutable_file_info()->set_is_symbolic_link(true);
+  entry.mutable_file_info()->set_creation_time(999);
+  entry.mutable_file_info()->set_last_modified(123456789);
+  entry.mutable_file_info()->set_last_accessed(987654321);
+
+  base::PlatformFileInfo file_info;
+  ConvertResourceEntryToPlatformFileInfo(entry, &file_info);
+  EXPECT_EQ(entry.file_info().size(), file_info.size);
+  EXPECT_EQ(entry.file_info().is_directory(), file_info.is_directory);
+  EXPECT_EQ(entry.file_info().is_symbolic_link(), file_info.is_symbolic_link);
+  EXPECT_EQ(base::Time::FromInternalValue(entry.file_info().creation_time()),
+            file_info.creation_time);
+  EXPECT_EQ(base::Time::FromInternalValue(entry.file_info().last_modified()),
+            file_info.last_modified);
+  EXPECT_EQ(base::Time::FromInternalValue(entry.file_info().last_accessed()),
+            file_info.last_accessed);
+}
+
+TEST(ResourceEntryConversionTest, FromPlatformFileInfo) {
+  base::PlatformFileInfo file_info;
+  file_info.size = 12345;
+  file_info.is_directory = true;
+  file_info.is_symbolic_link = true;
+  file_info.last_modified =
+      base::Time::UnixEpoch() + base::TimeDelta::FromDays(999);
+  file_info.last_accessed =
+      base::Time::UnixEpoch() + base::TimeDelta::FromDays(12345);
+  file_info.creation_time =
+      base::Time::UnixEpoch() + base::TimeDelta::FromDays(54321);
+
+  ResourceEntry entry;
+  SetPlatformFileInfoToResourceEntry(file_info, &entry);
+
+  EXPECT_EQ(file_info.size, entry.file_info().size());
+  EXPECT_EQ(file_info.is_directory, entry.file_info().is_directory());
+  EXPECT_EQ(file_info.is_symbolic_link, entry.file_info().is_symbolic_link());
+  EXPECT_EQ(file_info.creation_time,
+            base::Time::FromInternalValue(entry.file_info().creation_time()));
+  EXPECT_EQ(file_info.last_modified,
+            base::Time::FromInternalValue(entry.file_info().last_modified()));
+  EXPECT_EQ(file_info.last_accessed,
+            base::Time::FromInternalValue(entry.file_info().last_accessed()));
+}
+
 }  // namespace drive
diff --git a/chrome/browser/chromeos/drive/resource_metadata.cc b/chrome/browser/chromeos/drive/resource_metadata.cc
index b0ad6a1..38852a8 100644
--- a/chrome/browser/chromeos/drive/resource_metadata.cc
+++ b/chrome/browser/chromeos/drive/resource_metadata.cc
@@ -137,7 +137,7 @@
   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   if (!SetUpDefaultEntries())
     return FILE_ERROR_FAILED;
@@ -170,7 +170,7 @@
   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   if (!storage_->SetLargestChangestamp(0) ||
       !RemoveEntryRecursively(util::kDriveGrandRootSpecialResourceId) ||
@@ -245,7 +245,7 @@
   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   storage_->SetLargestChangestamp(value);
   return FILE_ERROR_OK;
@@ -266,7 +266,7 @@
   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   ResourceEntry existing_entry;
   if (storage_->GetEntry(entry.resource_id(), &existing_entry))
@@ -316,7 +316,7 @@
   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   // Disallow deletion of special entries "/drive" and "/drive/other".
   if (util::IsSpecialResourceId(resource_id))
@@ -434,7 +434,7 @@
   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   ResourceEntry old_entry;
   if (!storage_->GetEntry(entry.resource_id(), &old_entry))
@@ -525,7 +525,7 @@
   DCHECK(out_file_path);
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   ResourceEntry entry, destination;
   if (!FindEntryByPathSync(file_path, &entry) ||
@@ -554,7 +554,7 @@
   DVLOG(1) << "RenameEntry " << file_path.value() << " to " << new_title;
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   ResourceEntry entry;
   if (!FindEntryByPathSync(file_path, &entry))
@@ -627,7 +627,7 @@
   DCHECK(!directory_fetch_info.empty());
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-    return FILE_ERROR_NO_SPACE;
+    return FILE_ERROR_NO_LOCAL_SPACE;
 
   ResourceEntry directory;
   if (!storage_->GetEntry(directory_fetch_info.resource_id(), &directory))
@@ -645,7 +645,7 @@
   for (ResourceEntryMap::const_iterator it = entry_map.begin();
        it != entry_map.end(); ++it) {
     if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-      return FILE_ERROR_NO_SPACE;
+      return FILE_ERROR_NO_LOCAL_SPACE;
 
     const ResourceEntry& entry = it->second;
     // Skip if the parent resource ID does not match. This is needed to
@@ -670,7 +670,7 @@
   storage_->GetChildren(directory.resource_id(), &children);
   for (size_t i = 0; i < children.size(); ++i) {
     if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
-      return FILE_ERROR_NO_SPACE;
+      return FILE_ERROR_NO_LOCAL_SPACE;
 
     if (entry_map.count(children[i]) == 0) {
       if (!RemoveEntryRecursively(children[i]))
diff --git a/chrome/browser/chromeos/drive/resource_metadata_storage.cc b/chrome/browser/chromeos/drive/resource_metadata_storage.cc
index 56a6bc9..48505a5 100644
--- a/chrome/browser/chromeos/drive/resource_metadata_storage.cc
+++ b/chrome/browser/chromeos/drive/resource_metadata_storage.cc
@@ -581,7 +581,7 @@
     return false;
   }
 
-  // Check all entires.
+  // Check all entries.
   size_t num_entries_with_parent = 0;
   size_t num_child_entries = 0;
   ResourceEntry entry;
diff --git a/chrome/browser/chromeos/drive/test_util.h b/chrome/browser/chromeos/drive/test_util.h
index 99fe567..315f56a 100644
--- a/chrome/browser/chromeos/drive/test_util.h
+++ b/chrome/browser/chromeos/drive/test_util.h
@@ -95,7 +95,7 @@
 
 // Registers Drive related preferences in |pref_registry|. Drive related
 // preferences should be registered as TestingPrefServiceSimple will crash if
-// unregistered prefrence is referenced.
+// unregistered preference is referenced.
 void RegisterDrivePrefs(PrefRegistrySimple* pref_registry);
 
 // Fake NetworkChangeNotifier implementation.
diff --git a/chrome/browser/chromeos/enrollment_dialog_view.cc b/chrome/browser/chromeos/enrollment_dialog_view.cc
index c98afee..31e3762 100644
--- a/chrome/browser/chromeos/enrollment_dialog_view.cc
+++ b/chrome/browser/chromeos/enrollment_dialog_view.cc
@@ -45,7 +45,7 @@
   // views::DialogDelegateView overrides
   virtual int GetDialogButtons() const OVERRIDE;
   virtual bool Accept() OVERRIDE;
-  virtual void OnClose() OVERRIDE;
+  virtual void OnClosed() OVERRIDE;
   virtual string16 GetDialogButtonLabel(ui::DialogButton button) const OVERRIDE;
 
   // views::WidgetDelegate overrides
@@ -112,7 +112,7 @@
   return true;
 }
 
-void EnrollmentDialogView::OnClose() {
+void EnrollmentDialogView::OnClosed() {
   if (!accepted_)
     return;
   chrome::NavigateParams params(profile_,
diff --git a/chrome/browser/chromeos/extensions/external_cache.h b/chrome/browser/chromeos/extensions/external_cache.h
index e9415b5..2b4b4bf 100644
--- a/chrome/browser/chromeos/extensions/external_cache.h
+++ b/chrome/browser/chromeos/extensions/external_cache.h
@@ -52,6 +52,11 @@
                 Delegate* delegate);
   virtual ~ExternalCache();
 
+  // Returns already cached extensions.
+  const base::DictionaryValue* cached_extensions() {
+    return cached_extensions_.get();
+  }
+
   // Update list of extensions in cache and force update check for them.
   // ExternalCache gets ownership of |prefs|.
   void UpdateExtensionsList(scoped_ptr<base::DictionaryValue> prefs);
diff --git a/chrome/browser/chromeos/extensions/file_manager/drive_test_util.cc b/chrome/browser/chromeos/extensions/file_manager/drive_test_util.cc
index 4fe56ae..7a236e8 100644
--- a/chrome/browser/chromeos/extensions/file_manager/drive_test_util.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/drive_test_util.cc
@@ -11,7 +11,8 @@
 #include "content/public/browser/browser_context.h"
 #include "webkit/browser/fileapi/external_mount_points.h"
 
-namespace drive_test_util {
+namespace file_manager {
+namespace test_util {
 
 namespace {
 
@@ -77,4 +78,5 @@
   LOG(INFO) << "Drive mount point found.";
 }
 
-}  // namespace drive_test_util
+}  // namespace test_util
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/drive_test_util.h b/chrome/browser/chromeos/extensions/file_manager/drive_test_util.h
index dc0dd40..c420d77 100644
--- a/chrome/browser/chromeos/extensions/file_manager/drive_test_util.h
+++ b/chrome/browser/chromeos/extensions/file_manager/drive_test_util.h
@@ -7,13 +7,15 @@
 
 class Profile;
 
-namespace drive_test_util {
+namespace file_manager {
+namespace test_util {
 
 // Waits until Drive mount point for |profile| is added. Drive mount point is
 // added by the browser but tests should use this function to ensure that the
 // Drive mount point is added before accessing Drive.
 void WaitUntilDriveMountPointIsAdded(Profile* profile);
 
-}  // namespace drive_test_util
+}  // namespace test_util
+}  // namespace file_manager
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_DRIVE_TEST_UTIL_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/external_filesystem_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/external_filesystem_apitest.cc
index 1f2a945..cd9aa00 100644
--- a/chrome/browser/chromeos/extensions/file_manager/external_filesystem_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/external_filesystem_apitest.cc
@@ -45,6 +45,7 @@
 
 using extensions::Extension;
 
+namespace file_manager {
 namespace {
 
 // Root dirs for file systems expected by the test extensions.
@@ -303,7 +304,7 @@
 
   // FileSystemExtensionApiTestBase OVERRIDE.
   virtual void AddTestMountPoint() OVERRIDE {
-    drive_test_util::WaitUntilDriveMountPointIsAdded(browser()->profile());
+    test_util::WaitUntilDriveMountPointIsAdded(browser()->profile());
   }
 
  protected:
@@ -431,3 +432,4 @@
 }
 
 }  // namespace
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
index 09b55ea..1b00fc5 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
@@ -35,7 +35,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop_proxy.h"
 #include "base/platform_file.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_handler_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -54,8 +54,8 @@
 using content::BrowserContext;
 using content::BrowserThread;
 using extensions::api::file_browser_handler_internal::FileEntryInfo;
-using file_handler::FileSelector;
-using file_handler::FileSelectorFactory;
+using file_manager::FileSelector;
+using file_manager::FileSelectorFactory;
 
 namespace SelectFile =
     extensions::api::file_browser_handler_internal::SelectFile;
@@ -103,7 +103,7 @@
   virtual ~FileSelectorImpl() OVERRIDE;
 
  protected:
-  // file_handler::FileSelectr overrides.
+  // file_manager::FileSelectr overrides.
   // Shows save as dialog with suggested name in window bound to |browser|.
   // |allowed_extensions| specifies the file extensions allowed to be shown,
   // and selected. Extensions should not include '.'.
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.h b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.h
index 9839f8f..e35780f 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.h
@@ -21,7 +21,7 @@
 class Browser;
 class FileBrowserHandlerInternalSelectFileFunction;
 
-namespace file_handler {
+namespace file_manager {
 
 // Interface that is used by FileBrowserHandlerInternalSelectFileFunction to
 // select the file path that should be reported back to the extension function
@@ -66,8 +66,13 @@
   virtual FileSelector* CreateFileSelector() const = 0;
 };
 
-}  // namespace file_handler
+}  // namespace file_manager
 
+
+// Note that this class is not in 'file_manager' class to be consistent with
+// all other extension functions registered in
+// chrome/common/extensions/api/generated_api.cc being in the global namespace.
+//
 // The fileBrowserHandlerInternal.selectFile extension function implementation.
 // See the file description for more info.
 class FileBrowserHandlerInternalSelectFileFunction
@@ -83,7 +88,7 @@
   // invoked by user gesture.
   // Created object will take the ownership of the |file_selector_factory|.
   FileBrowserHandlerInternalSelectFileFunction(
-      file_handler::FileSelectorFactory* file_selector_factory,
+      file_manager::FileSelectorFactory* file_selector_factory,
       bool enable_user_gesture_check);
 
   // Called by FileSelector implementation when the user selects the file's
@@ -136,7 +141,7 @@
 
   // Factory used to create FileSelector to be used for prompting user to select
   // file.
-  scoped_ptr<file_handler::FileSelectorFactory> file_selector_factory_;
+  scoped_ptr<file_manager::FileSelectorFactory> file_selector_factory_;
   // Whether user gesture check is disabled. This should be true only in tests.
   bool user_gesture_check_enabled_;
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api_test.cc b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api_test.cc
index c63e08e..8608786 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api_test.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api_test.cc
@@ -67,7 +67,7 @@
 // When |SelectFile| is called, it will check that file name suggestion is as
 // expected, and respond to the extension function with specified selection
 // results.
-class MockFileSelector : public file_handler::FileSelector {
+class MockFileSelector : public file_manager::FileSelector {
  public:
   MockFileSelector(const base::FilePath& suggested_name,
                    const std::vector<std::string>& allowed_extensions,
@@ -80,7 +80,7 @@
   }
   virtual ~MockFileSelector() {}
 
-  // file_handler::FileSelector implementation.
+  // file_manager::FileSelector implementation.
   // |browser| is not used.
   virtual void SelectFile(
       const base::FilePath& suggested_name,
@@ -124,7 +124,7 @@
 // Mocks file selector factory for the test.
 // When |CreateFileSelector| is invoked it will create mock file selector for
 // the extension function with test parameters from the object ctor.
-class MockFileSelectorFactory : public file_handler::FileSelectorFactory {
+class MockFileSelectorFactory : public file_manager::FileSelectorFactory {
  public:
   explicit MockFileSelectorFactory(const TestCase& test_case)
       : suggested_name_(test_case.suggested_name),
@@ -134,8 +134,8 @@
   }
   virtual ~MockFileSelectorFactory() {}
 
-  // file_handler::FileSelectorFactory implementation.
-  virtual file_handler::FileSelector* CreateFileSelector() const OVERRIDE {
+  // file_manager::FileSelectorFactory implementation.
+  virtual file_manager::FileSelector* CreateFileSelector() const OVERRIDE {
     return new MockFileSelector(suggested_name_,
                                 allowed_extensions_,
                                 success_,
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc b/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc
index a45c048..9ec48e5 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc
@@ -4,598 +4,72 @@
 
 #include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h"
 
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/types.h>
-#include <utime.h>
-
-#include <map>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/format_macros.h"
-#include "base/logging.h"
-#include "base/memory/scoped_vector.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "chrome/browser/chromeos/drive/drive.pb.h"
-#include "chrome/browser/chromeos/drive/drive_app_registry.h"
-#include "chrome/browser/chromeos/drive/drive_integration_service.h"
-#include "chrome/browser/chromeos/drive/file_system_interface.h"
-#include "chrome/browser/chromeos/drive/file_system_util.h"
-#include "chrome/browser/chromeos/drive/job_list.h"
-#include "chrome/browser/chromeos/drive/logging.h"
 #include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api_factory.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_handler_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
-#include "chrome/browser/chromeos/extensions/file_manager/zip_file_creator.h"
-#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/system/statistics_provider.h"
-#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
-#include "chrome/browser/extensions/extension_function_dispatcher.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_dialog.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_drive.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_misc.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_mount.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_strings.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
 #include "chrome/browser/extensions/extension_function_registry.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
-#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/google_apis/gdata_wapi_parser.h"
-#include "chrome/browser/lifetime/application_lifetime.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/views/select_file_dialog_extension.h"
-#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
-#include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/extensions/extension_icon_set.h"
-#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
-#include "chrome/common/pref_names.h"
-#include "chromeos/disks/disk_mount_manager.h"
-#include "content/public/browser/child_process_security_policy.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/storage_partition.h"
-#include "content/public/common/page_zoom.h"
-#include "grit/app_locale_settings.h"
-#include "grit/generated_resources.h"
-#include "net/base/escape.h"
-#include "net/base/network_change_notifier.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/shell_dialogs/selected_file_info.h"
-#include "ui/webui/web_ui_util.h"
-#include "url/gurl.h"
-#include "webkit/browser/fileapi/file_system_context.h"
-#include "webkit/browser/fileapi/file_system_file_util.h"
-#include "webkit/browser/fileapi/file_system_operation_context.h"
-#include "webkit/browser/fileapi/file_system_url.h"
-#include "webkit/common/fileapi/file_system_types.h"
-#include "webkit/common/fileapi/file_system_util.h"
-
-using extensions::app_file_handler_util::FindFileHandlersForFiles;
-using extensions::app_file_handler_util::PathAndMimeTypeSet;
-using chromeos::disks::DiskMountManager;
-using content::BrowserContext;
-using content::BrowserThread;
-using content::ChildProcessSecurityPolicy;
-using content::WebContents;
-using extensions::Extension;
-using extensions::ZipFileCreator;
-using fileapi::FileSystemURL;
-using google_apis::InstalledApp;
 
 namespace file_manager {
-namespace {
-
-// Default icon path for drive docs.
-const char kDefaultIcon[] = "images/filetype_generic.png";
-const int kPreferredIconSize = 16;
-
-// Error messages.
-const char kFileError[] = "File error %d";
-const char kInvalidFileUrl[] = "Invalid file URL";
-const char kVolumeDevicePathNotFound[] = "Device path not found";
-
-/**
- * List of connection types of drive.
- *
- * Keep this in sync with the DriveConnectionType in volume_manager.js.
- */
-const char kDriveConnectionTypeOffline[] = "offline";
-const char kDriveConnectionTypeMetered[] = "metered";
-const char kDriveConnectionTypeOnline[] = "online";
-
-/**
- * List of reasons of kDriveConnectionType*.
- *
- * Keep this in sync with the DriveConnectionReason in volume_manager.js.
- */
-const char kDriveConnectionReasonNotReady[] = "not_ready";
-const char kDriveConnectionReasonNoNetwork[] = "no_network";
-const char kDriveConnectionReasonNoService[] = "no_service";
-
-// Unescape rules used for parsing query parameters.
-const net::UnescapeRule::Type kUnescapeRuleForQueryParameters =
-    net::UnescapeRule::SPACES |
-    net::UnescapeRule::URL_SPECIAL_CHARS |
-    net::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
-
-const DiskMountManager::Disk* GetVolumeAsDisk(const std::string& mount_path) {
-  DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
-
-  DiskMountManager::MountPointMap::const_iterator mount_point_it =
-      disk_mount_manager->mount_points().find(mount_path);
-  if (mount_point_it == disk_mount_manager->mount_points().end())
-    return NULL;
-
-  const DiskMountManager::Disk* disk = disk_mount_manager->FindDiskBySourcePath(
-      mount_point_it->second.source_path);
-
-  return (disk && disk->is_hidden()) ? NULL : disk;
-}
-
-base::DictionaryValue* CreateValueFromDisk(
-    Profile* profile,
-    const std::string& extension_id,
-    const DiskMountManager::Disk* volume) {
-  base::DictionaryValue* volume_info = new base::DictionaryValue();
-
-  std::string mount_path;
-  if (!volume->mount_path().empty()) {
-    base::FilePath relative_mount_path;
-    file_manager_util::ConvertFileToRelativeFileSystemPath(
-        profile, extension_id, base::FilePath(volume->mount_path()),
-        &relative_mount_path);
-    mount_path = relative_mount_path.value();
-  }
-
-  volume_info->SetString("devicePath", volume->device_path());
-  volume_info->SetString("mountPath", mount_path);
-  volume_info->SetString("systemPath", volume->system_path());
-  volume_info->SetString("filePath", volume->file_path());
-  volume_info->SetString("deviceLabel", volume->device_label());
-  volume_info->SetString("driveLabel", volume->drive_label());
-  volume_info->SetString("deviceType",
-      DiskMountManager::DeviceTypeToString(volume->device_type()));
-  volume_info->SetInteger("totalSize", volume->total_size_in_bytes());
-  volume_info->SetBoolean("isParent", volume->is_parent());
-  volume_info->SetBoolean("isReadOnly", volume->is_read_only());
-  volume_info->SetBoolean("hasMedia", volume->has_media());
-  volume_info->SetBoolean("isOnBootDevice", volume->on_boot_device());
-
-  return volume_info;
-}
-
-base::DictionaryValue* CreateValueFromMountPoint(Profile* profile,
-    const DiskMountManager::MountPointInfo& mount_point_info,
-    const std::string& extension_id,
-    const GURL& extension_source_url) {
-
-  base::DictionaryValue *mount_info = new base::DictionaryValue();
-
-  mount_info->SetString("mountType",
-                        DiskMountManager::MountTypeToString(
-                            mount_point_info.mount_type));
-  mount_info->SetString("sourcePath", mount_point_info.source_path);
-
-  base::FilePath relative_mount_path;
-  // Convert mount point path to relative path with the external file system
-  // exposed within File API.
-  if (file_manager_util::ConvertFileToRelativeFileSystemPath(
-          profile,
-          extension_id,
-          base::FilePath(mount_point_info.mount_path),
-          &relative_mount_path)) {
-    mount_info->SetString("mountPath", relative_mount_path.value());
-  }
-
-  mount_info->SetString("mountCondition",
-      DiskMountManager::MountConditionToString(
-          mount_point_info.mount_condition));
-
-  return mount_info;
-}
-
-void SetDriveMountPointPermissions(
-    Profile* profile,
-    const std::string& extension_id,
-    content::RenderViewHost* render_view_host) {
-  if (!render_view_host ||
-      !render_view_host->GetSiteInstance() || !render_view_host->GetProcess()) {
-    return;
-  }
-
-  content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
-  fileapi::ExternalFileSystemBackend* backend =
-      BrowserContext::GetStoragePartition(profile, site_instance)->
-      GetFileSystemContext()->external_backend();
-  if (!backend)
-    return;
-
-  const base::FilePath mount_point = drive::util::GetDriveMountPointPath();
-  // Grant R/W permissions to drive 'folder'. File API layer still
-  // expects this to be satisfied.
-  ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile(
-      render_view_host->GetProcess()->GetID(), mount_point);
-
-  base::FilePath mount_point_virtual;
-  if (backend->GetVirtualPath(mount_point, &mount_point_virtual))
-    backend->GrantFileAccessToExtension(extension_id, mount_point_virtual);
-}
-
-// Finds an icon in the list of icons. If unable to find an icon of the exact
-// size requested, returns one with the next larger size. If all icons are
-// smaller than the preferred size, we'll return the largest one available.
-// Icons must be sorted by the icon size, smallest to largest. If there are no
-// icons in the list, returns an empty URL.
-GURL FindPreferredIcon(const InstalledApp::IconList& icons,
-                       int preferred_size) {
-  GURL result;
-  if (icons.empty())
-    return result;
-  result = icons.rbegin()->second;
-  for (InstalledApp::IconList::const_reverse_iterator iter = icons.rbegin();
-       iter != icons.rend() && iter->first >= preferred_size; ++iter) {
-        result = iter->second;
-  }
-  return result;
-}
-
-// Retrieves total and remaining available size on |mount_path|.
-void GetSizeStatsOnBlockingPool(const std::string& mount_path,
-                                size_t* total_size_kb,
-                                size_t* remaining_size_kb) {
-  uint64_t total_size_in_bytes = 0;
-  uint64_t remaining_size_in_bytes = 0;
-
-  struct statvfs stat = {};  // Zero-clear
-  if (HANDLE_EINTR(statvfs(mount_path.c_str(), &stat)) == 0) {
-    total_size_in_bytes =
-        static_cast<uint64_t>(stat.f_blocks) * stat.f_frsize;
-    remaining_size_in_bytes =
-        static_cast<uint64_t>(stat.f_bfree) * stat.f_frsize;
-  }
-  *total_size_kb = static_cast<size_t>(total_size_in_bytes / 1024);
-  *remaining_size_kb = static_cast<size_t>(remaining_size_in_bytes / 1024);
-}
-
-// Make a set of unique filename suffixes out of the list of file URLs.
-std::set<std::string> GetUniqueSuffixes(base::ListValue* file_url_list,
-                                        fileapi::FileSystemContext* context) {
-  std::set<std::string> suffixes;
-  for (size_t i = 0; i < file_url_list->GetSize(); ++i) {
-    std::string url_str;
-    if (!file_url_list->GetString(i, &url_str))
-      return std::set<std::string>();
-    FileSystemURL url = context->CrackURL(GURL(url_str));
-    if (!url.is_valid() || url.path().empty())
-      return std::set<std::string>();
-    // We'll skip empty suffixes.
-    if (!url.path().Extension().empty())
-      suffixes.insert(url.path().Extension());
-  }
-  return suffixes;
-}
-
-// Make a set of unique MIME types out of the list of MIME types.
-std::set<std::string> GetUniqueMimeTypes(base::ListValue* mime_type_list) {
-  std::set<std::string> mime_types;
-  for (size_t i = 0; i < mime_type_list->GetSize(); ++i) {
-    std::string mime_type;
-    if (!mime_type_list->GetString(i, &mime_type))
-      return std::set<std::string>();
-    // We'll skip empty MIME types.
-    if (!mime_type.empty())
-      mime_types.insert(mime_type);
-  }
-  return mime_types;
-}
-
-void LogDefaultTask(const std::set<std::string>& mime_types,
-                    const std::set<std::string>& suffixes,
-                    const std::string& task_id) {
-  if (!mime_types.empty()) {
-    std::string mime_types_str;
-    for (std::set<std::string>::const_iterator iter = mime_types.begin();
-        iter != mime_types.end(); ++iter) {
-      if (iter == mime_types.begin()) {
-        mime_types_str = *iter;
-      } else {
-        mime_types_str += ", " + *iter;
-      }
-    }
-    VLOG(1) << "Associating task " << task_id
-            << " with the following MIME types: ";
-    VLOG(1) << "  " << mime_types_str;
-  }
-
-  if (!suffixes.empty()) {
-    std::string suffixes_str;
-    for (std::set<std::string>::const_iterator iter = suffixes.begin();
-        iter != suffixes.end(); ++iter) {
-      if (iter == suffixes.begin()) {
-        suffixes_str = *iter;
-      } else {
-        suffixes_str += ", " + *iter;
-      }
-    }
-    VLOG(1) << "Associating task " << task_id
-            << " with the following suffixes: ";
-    VLOG(1) << "  " << suffixes_str;
-  }
-}
-
-// Does nothing with a bool parameter. Used as a placeholder for calling
-// ClearCacheAndRemountFileSystem(). TODO(yoshiki): Handle an error from
-// ClearCacheAndRemountFileSystem() properly: http://crbug.com/140511.
-void DoNothingWithBool(bool /* success */) {
-}
-
-void FillDriveEntryPropertiesValue(
-    const drive::ResourceEntry& entry_proto,
-    DictionaryValue* property_dict) {
-  property_dict->SetBoolean("sharedWithMe", entry_proto.shared_with_me());
-
-  if (!entry_proto.has_file_specific_info())
-    return;
-
-  const drive::FileSpecificInfo& file_specific_info =
-      entry_proto.file_specific_info();
-
-  property_dict->SetString("thumbnailUrl", file_specific_info.thumbnail_url());
-  property_dict->SetBoolean("isHosted",
-                            file_specific_info.is_hosted_document());
-  property_dict->SetString("contentMimeType",
-                           file_specific_info.content_mime_type());
-}
-
-void GetMimeTypesForFileURLs(const std::vector<base::FilePath>& file_paths,
-                             PathAndMimeTypeSet* files) {
-  for (std::vector<base::FilePath>::const_iterator iter = file_paths.begin();
-       iter != file_paths.end(); ++iter) {
-    files->insert(
-        std::make_pair(*iter, file_manager_util::GetMimeTypeForPath(*iter)));
-  }
-}
-
-// Retrieves the maximum file name length of the file system of |path|.
-// Returns 0 if it could not be queried.
-size_t GetFileNameMaxLengthOnBlockingPool(const std::string& path) {
-  struct statvfs stat = {};
-  if (statvfs(path.c_str(), &stat) != 0) {
-    // The filesystem seems not supporting statvfs(). Assume it to be a commonly
-    // used bound 255, and log the failure.
-    LOG(ERROR) << "Cannot statvfs() the name length limit for: " << path;
-    return 255;
-  }
-  return stat.f_namemax;
-}
-
-// Sets last modified date.
-bool SetLastModifiedOnBlockingPool(const base::FilePath& local_path,
-                                   time_t timestamp) {
-  if (local_path.empty())
-    return false;
-
-  struct stat stat_buffer;
-  if (stat(local_path.value().c_str(), &stat_buffer) != 0)
-    return false;
-
-  struct utimbuf times;
-  times.actime = stat_buffer.st_atime;
-  times.modtime = timestamp;
-  return utime(local_path.value().c_str(), &times) == 0;
-}
-
-// Returns a task id for the web app with |app_id|.
-std::string MakeWebAppTaskId(const std::string& app_id) {
-  // TODO(gspencer): For now, the action id is always "open-with", but we
-  // could add any actions that the drive app supports.
-  return file_handler_util::MakeTaskID(
-      app_id, file_handler_util::kTaskDrive, "open-with");
-}
-
-// Returns the ID of the tab associated with the dispatcher. Returns 0 on
-// error.
-int32 GetTabId(ExtensionFunctionDispatcher* dispatcher) {
-  if (!dispatcher) {
-    LOG(WARNING) << "No dispatcher";
-    return 0;
-  }
-  if (!dispatcher->delegate()) {
-    LOG(WARNING) << "No delegate";
-    return 0;
-  }
-  WebContents* web_contents =
-      dispatcher->delegate()->GetAssociatedWebContents();
-  if (!web_contents) {
-    LOG(WARNING) << "No associated tab contents";
-    return 0;
-  }
-  return ExtensionTabUtil::GetTabId(web_contents);
-}
-
-// Returns the local FilePath associated with |url|. If the file isn't of the
-// type FileSystemBackend handles, returns an empty
-// FilePath. |render_view_host| and |profile| are needed to obtain the
-// FileSystemContext currently in use.
-//
-// Local paths will look like "/home/chronos/user/Downloads/foo/bar.txt" or
-// "/special/drive/foo/bar.txt".
-base::FilePath GetLocalPathFromURL(
-    content::RenderViewHost* render_view_host,
-    Profile* profile,
-    const GURL& url) {
-  DCHECK(render_view_host);
-  DCHECK(profile);
-
-  content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
-  scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      BrowserContext::GetStoragePartition(profile, site_instance)->
-          GetFileSystemContext();
-
-  const fileapi::FileSystemURL filesystem_url(
-      file_system_context->CrackURL(url));
-  base::FilePath path;
-  if (!chromeos::FileSystemBackend::CanHandleURL(filesystem_url))
-    return base::FilePath();
-  return filesystem_url.path();
-}
-
-// The typedefs are used for GetSelectedFileInfo().
-typedef std::vector<GURL> UrlList;
-typedef std::vector<ui::SelectedFileInfo> SelectedFileInfoList;
-typedef base::Callback<void(const SelectedFileInfoList&)>
-    GetSelectedFileInfoCallback;
-
-// The struct is used for GetSelectedFileInfo().
-struct GetSelectedFileInfoParams {
-  bool for_opening;
-  GetSelectedFileInfoCallback callback;
-  std::vector<base::FilePath> file_paths;
-  SelectedFileInfoList selected_files;
-};
-
-// Forward declarations of helper functions for GetSelectedFileInfo().
-void GetSelectedFileInfoInternal(Profile* profile,
-                                 scoped_ptr<GetSelectedFileInfoParams> params);
-void ContinueGetSelectedFileInfo(Profile* profile,
-                                 scoped_ptr<GetSelectedFileInfoParams> params,
-                                 drive::FileError error,
-                                 const base::FilePath& local_file_path,
-                                 scoped_ptr<drive::ResourceEntry> entry);
-
-// Runs |callback| with SelectedFileInfoList created from |file_urls|.
-void GetSelectedFileInfo(content::RenderViewHost* render_view_host,
-                         Profile* profile,
-                         const UrlList& file_urls,
-                         bool for_opening,
-                         GetSelectedFileInfoCallback callback) {
-  DCHECK(render_view_host);
-  DCHECK(profile);
-
-  scoped_ptr<GetSelectedFileInfoParams> params(new GetSelectedFileInfoParams);
-  params->for_opening = for_opening;
-  params->callback = callback;
-
-  for (size_t i = 0; i < file_urls.size(); ++i) {
-    const GURL& file_url = file_urls[i];
-    const base::FilePath path = GetLocalPathFromURL(
-        render_view_host, profile, file_url);
-    if (!path.empty()) {
-      DVLOG(1) << "Selected: file path: " << path.value();
-      params->file_paths.push_back(path);
-    }
-  }
-
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&GetSelectedFileInfoInternal,
-                 profile,
-                 base::Passed(&params)));
-}
-
-// Part of GetSelectedFileInfo().
-void GetSelectedFileInfoInternal(Profile* profile,
-                                 scoped_ptr<GetSelectedFileInfoParams> params) {
-  DCHECK(profile);
-
-  for (size_t i = params->selected_files.size();
-       i < params->file_paths.size(); ++i) {
-    const base::FilePath& file_path = params->file_paths[i];
-    // When opening a drive file, we should get local file path.
-    if (params->for_opening &&
-        drive::util::IsUnderDriveMountPoint(file_path)) {
-      drive::DriveIntegrationService* integration_service =
-          drive::DriveIntegrationServiceFactory::GetForProfile(profile);
-      // |integration_service| is NULL if Drive is disabled.
-      if (!integration_service) {
-        ContinueGetSelectedFileInfo(profile,
-                                    params.Pass(),
-                                    drive::FILE_ERROR_FAILED,
-                                    base::FilePath(),
-                                    scoped_ptr<drive::ResourceEntry>());
-        return;
-      }
-      integration_service->file_system()->GetFileByPath(
-          drive::util::ExtractDrivePath(file_path),
-          base::Bind(&ContinueGetSelectedFileInfo,
-                     profile,
-                     base::Passed(&params)));
-      return;
-    } else {
-      params->selected_files.push_back(
-          ui::SelectedFileInfo(file_path, base::FilePath()));
-    }
-  }
-  params->callback.Run(params->selected_files);
-}
-
-// Part of GetSelectedFileInfo().
-void ContinueGetSelectedFileInfo(Profile* profile,
-                                 scoped_ptr<GetSelectedFileInfoParams> params,
-                                 drive::FileError error,
-                                 const base::FilePath& local_file_path,
-                                 scoped_ptr<drive::ResourceEntry> entry) {
-  DCHECK(profile);
-
-  const int index = params->selected_files.size();
-  const base::FilePath& file_path = params->file_paths[index];
-  base::FilePath local_path;
-  if (error == drive::FILE_ERROR_OK) {
-    local_path = local_file_path;
-  } else {
-    DLOG(ERROR) << "Failed to get " << file_path.value()
-                << " with error code: " << error;
-  }
-  params->selected_files.push_back(ui::SelectedFileInfo(file_path, local_path));
-  GetSelectedFileInfoInternal(profile, params.Pass());
-}
-
-}  // namespace
 
 FileBrowserPrivateAPI::FileBrowserPrivateAPI(Profile* profile)
     : event_router_(new FileManagerEventRouter(profile)) {
   ExtensionFunctionRegistry* registry =
       ExtensionFunctionRegistry::GetInstance();
-  registry->RegisterFunction<LogoutUserFunction>();
-  registry->RegisterFunction<CancelFileDialogFunction>();
-  registry->RegisterFunction<ExecuteTasksFunction>();
-  registry->RegisterFunction<SetDefaultTaskFunction>();
-  registry->RegisterFunction<FileDialogStringsFunction>();
+  // Tasks related functions.
+  registry->RegisterFunction<ExecuteTaskFunction>();
   registry->RegisterFunction<GetFileTasksFunction>();
-  registry->RegisterFunction<GetVolumeMetadataFunction>();
-  registry->RegisterFunction<RequestFileSystemFunction>();
-  registry->RegisterFunction<AddFileWatchBrowserFunction>();
-  registry->RegisterFunction<RemoveFileWatchBrowserFunction>();
-  registry->RegisterFunction<SelectFileFunction>();
-  registry->RegisterFunction<SelectFilesFunction>();
-  registry->RegisterFunction<AddMountFunction>();
-  registry->RegisterFunction<RemoveMountFunction>();
-  registry->RegisterFunction<GetMountPointsFunction>();
-  registry->RegisterFunction<GetSizeStatsFunction>();
-  registry->RegisterFunction<FormatDeviceFunction>();
+  registry->RegisterFunction<SetDefaultTaskFunction>();
   registry->RegisterFunction<ViewFilesFunction>();
+
+  // Drive related functions.
   registry->RegisterFunction<GetDriveEntryPropertiesFunction>();
   registry->RegisterFunction<PinDriveFileFunction>();
   registry->RegisterFunction<GetDriveFilesFunction>();
   registry->RegisterFunction<CancelFileTransfersFunction>();
   registry->RegisterFunction<TransferFileFunction>();
-  registry->RegisterFunction<GetPreferencesFunction>();
-  registry->RegisterFunction<SetPreferencesFunction>();
   registry->RegisterFunction<SearchDriveFunction>();
   registry->RegisterFunction<SearchDriveMetadataFunction>();
   registry->RegisterFunction<ClearDriveCacheFunction>();
   registry->RegisterFunction<GetDriveConnectionStateFunction>();
-  registry->RegisterFunction<SetLastModifiedFunction>();
-  registry->RegisterFunction<ZipSelectionFunction>();
-  registry->RegisterFunction<ValidatePathNameLengthFunction>();
-  registry->RegisterFunction<ZoomFunction>();
   registry->RegisterFunction<RequestAccessTokenFunction>();
   registry->RegisterFunction<GetShareUrlFunction>();
+
+  // Select file dialog related functions.
+  registry->RegisterFunction<CancelFileDialogFunction>();
+  registry->RegisterFunction<SelectFileFunction>();
+  registry->RegisterFunction<SelectFilesFunction>();
+
+  // Mount points releated functions.
+  registry->RegisterFunction<AddMountFunction>();
+  registry->RegisterFunction<RemoveMountFunction>();
+  registry->RegisterFunction<GetMountPointsFunction>();
+
+  // Hundreds of strings for the file manager.
+  registry->RegisterFunction<GetStringsFunction>();
+
+  // File system related functions.
+  registry->RegisterFunction<RequestFileSystemFunction>();
+  registry->RegisterFunction<AddFileWatchFunction>();
+  registry->RegisterFunction<RemoveFileWatchFunction>();
+  registry->RegisterFunction<SetLastModifiedFunction>();
+  registry->RegisterFunction<GetSizeStatsFunction>();
+  registry->RegisterFunction<GetVolumeMetadataFunction>();
+  registry->RegisterFunction<ValidatePathNameLengthFunction>();
+  registry->RegisterFunction<FormatDeviceFunction>();
+
+  // Miscellaneous functions.
+  registry->RegisterFunction<LogoutUserFunction>();
+  registry->RegisterFunction<GetPreferencesFunction>();
+  registry->RegisterFunction<SetPreferencesFunction>();
+  registry->RegisterFunction<ZipSelectionFunction>();
+  registry->RegisterFunction<ZoomFunction>();
   event_router_->ObserveFileSystemEvents();
 }
 
@@ -611,2633 +85,4 @@
   return FileBrowserPrivateAPIFactory::GetForProfile(profile);
 }
 
-LogoutUserFunction::LogoutUserFunction() {
-}
-
-LogoutUserFunction::~LogoutUserFunction() {
-}
-
-bool LogoutUserFunction::RunImpl() {
-  chrome::AttemptUserExit();
-  return true;
-}
-
-RequestFileSystemFunction::RequestFileSystemFunction() {
-}
-
-RequestFileSystemFunction::~RequestFileSystemFunction() {
-}
-
-void RequestFileSystemFunction::DidOpenFileSystem(
-    scoped_refptr<fileapi::FileSystemContext> file_system_context,
-    base::PlatformFileError result,
-    const std::string& name,
-    const GURL& root_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (result != base::PLATFORM_FILE_OK) {
-    DidFail(result);
-    return;
-  }
-
-  // RenderViewHost may have gone while the task is posted asynchronously.
-  if (!render_view_host()) {
-    DidFail(base::PLATFORM_FILE_ERROR_FAILED);
-    return;
-  }
-
-  // Set up file permission access.
-  const int child_id = render_view_host()->GetProcess()->GetID();
-  if (!SetupFileSystemAccessPermissions(file_system_context,
-                                        child_id,
-                                        GetExtension())) {
-    DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
-    return;
-  }
-
-  // Add drive mount point immediately when we kick of first instance of file
-  // manager. The actual mount event will be sent to UI only when we perform
-  // proper authentication.
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  if (integration_service)
-    SetDriveMountPointPermissions(profile_, extension_id(), render_view_host());
-  DictionaryValue* dict = new DictionaryValue();
-  SetResult(dict);
-  dict->SetString("name", name);
-  dict->SetString("path", root_path.spec());
-  dict->SetInteger("error", drive::FILE_ERROR_OK);
-  SendResponse(true);
-}
-
-void RequestFileSystemFunction::DidFail(
-    base::PlatformFileError error_code) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  error_ = base::StringPrintf(kFileError, static_cast<int>(error_code));
-  SendResponse(false);
-}
-
-bool RequestFileSystemFunction::SetupFileSystemAccessPermissions(
-    scoped_refptr<fileapi::FileSystemContext> file_system_context,
-    int child_id,
-    scoped_refptr<const extensions::Extension> extension) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (!extension.get())
-    return false;
-
-  // Make sure that only component extension can access the entire
-  // local file system.
-  if (extension_->location() != extensions::Manifest::COMPONENT) {
-    NOTREACHED() << "Private method access by non-component extension "
-                 << extension->id();
-    return false;
-  }
-
-  fileapi::ExternalFileSystemBackend* backend =
-      file_system_context->external_backend();
-  if (!backend)
-    return false;
-
-  // Grant full access to File API from this component extension.
-  backend->GrantFullAccessToExtension(extension_->id());
-
-  // Grant R/W file permissions to the renderer hosting component
-  // extension for all paths exposed by our local file system backend.
-  std::vector<base::FilePath> root_dirs = backend->GetRootDirectories();
-  for (size_t i = 0; i < root_dirs.size(); ++i) {
-    ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile(
-        child_id, root_dirs[i]);
-  }
-  return true;
-}
-
-bool RequestFileSystemFunction::RunImpl() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (!dispatcher() || !render_view_host() || !render_view_host()->GetProcess())
-    return false;
-
-  set_log_on_completion(true);
-
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
-  scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      BrowserContext::GetStoragePartition(profile_, site_instance)->
-          GetFileSystemContext();
-
-  const GURL origin_url = source_url_.GetOrigin();
-  file_system_context->OpenFileSystem(
-      origin_url,
-      fileapi::kFileSystemTypeExternal,
-      fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
-      base::Bind(&RequestFileSystemFunction::DidOpenFileSystem,
-                 this,
-                 file_system_context));
-  return true;
-}
-
-FileWatchBrowserFunctionBase::FileWatchBrowserFunctionBase() {
-}
-
-FileWatchBrowserFunctionBase::~FileWatchBrowserFunctionBase() {
-}
-
-void FileWatchBrowserFunctionBase::Respond(bool success) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  SetResult(Value::CreateBooleanValue(success));
-  SendResponse(success);
-}
-
-bool FileWatchBrowserFunctionBase::RunImpl() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (!render_view_host() || !render_view_host()->GetProcess())
-    return false;
-
-  // First param is url of a file to watch.
-  std::string url;
-  if (!args_->GetString(0, &url) || url.empty())
-    return false;
-
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
-  scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      BrowserContext::GetStoragePartition(profile(), site_instance)->
-          GetFileSystemContext();
-
-  FileSystemURL file_watch_url = file_system_context->CrackURL(GURL(url));
-  base::FilePath local_path = file_watch_url.path();
-  base::FilePath virtual_path = file_watch_url.virtual_path();
-  if (local_path.empty()) {
-    Respond(false);
-    return true;
-  }
-  PerformFileWatchOperation(local_path, virtual_path, extension_id());
-
-  return true;
-}
-
-AddFileWatchBrowserFunction::AddFileWatchBrowserFunction() {
-}
-
-AddFileWatchBrowserFunction::~AddFileWatchBrowserFunction() {
-}
-
-void AddFileWatchBrowserFunction::PerformFileWatchOperation(
-    const base::FilePath& local_path,
-    const base::FilePath& virtual_path,
-    const std::string& extension_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  FileManagerEventRouter* event_router =
-      FileBrowserPrivateAPI::Get(profile_)->event_router();
-  event_router->AddFileWatch(
-      local_path,
-      virtual_path,
-      extension_id,
-      base::Bind(&AddFileWatchBrowserFunction::Respond, this));
-}
-
-RemoveFileWatchBrowserFunction::RemoveFileWatchBrowserFunction() {
-}
-
-RemoveFileWatchBrowserFunction::~RemoveFileWatchBrowserFunction() {
-}
-
-void RemoveFileWatchBrowserFunction::PerformFileWatchOperation(
-    const base::FilePath& local_path,
-    const base::FilePath& unused,
-    const std::string& extension_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  FileManagerEventRouter* event_router =
-      FileBrowserPrivateAPI::Get(profile_)->event_router();
-  event_router->RemoveFileWatch(local_path, extension_id);
-  Respond(true);
-}
-
-struct GetFileTasksFunction::FileInfo {
-  GURL file_url;
-  base::FilePath file_path;
-  std::string mime_type;
-};
-
-struct GetFileTasksFunction::TaskInfo {
-  TaskInfo(const string16& app_name, const GURL& icon_url)
-      : app_name(app_name), icon_url(icon_url) {
-  }
-
-  string16 app_name;
-  GURL icon_url;
-};
-
-GetFileTasksFunction::GetFileTasksFunction() {
-}
-
-GetFileTasksFunction::~GetFileTasksFunction() {
-}
-
-// static
-void GetFileTasksFunction::GetAvailableDriveTasks(
-    drive::DriveAppRegistry* registry,
-    const FileInfoList& file_info_list,
-    TaskInfoMap* task_info_map) {
-  DCHECK(registry);
-  DCHECK(task_info_map);
-  DCHECK(task_info_map->empty());
-
-  bool is_first = true;
-  for (size_t i = 0; i < file_info_list.size(); ++i) {
-    const FileInfo& file_info = file_info_list[i];
-    if (file_info.file_path.empty())
-      continue;
-
-    ScopedVector<drive::DriveAppInfo> app_info_list;
-    registry->GetAppsForFile(
-        file_info.file_path, file_info.mime_type, &app_info_list);
-
-    if (is_first) {
-      // For the first file, we store all the info.
-      for (size_t j = 0; j < app_info_list.size(); ++j) {
-        const drive::DriveAppInfo& app_info = *app_info_list[j];
-        GURL icon_url =
-            FindPreferredIcon(app_info.app_icons, kPreferredIconSize);
-        task_info_map->insert(std::pair<std::string, TaskInfo>(
-            MakeWebAppTaskId(app_info.app_id),
-            TaskInfo(app_info.app_name, icon_url)));
-      }
-    } else {
-      // For remaining files, take the intersection with the current result,
-      // based on the task id.
-      std::set<std::string> task_id_set;
-      for (size_t j = 0; j < app_info_list.size(); ++j) {
-        task_id_set.insert(MakeWebAppTaskId(app_info_list[j]->app_id));
-      }
-      for (TaskInfoMap::iterator iter = task_info_map->begin();
-           iter != task_info_map->end(); ) {
-        if (task_id_set.find(iter->first) == task_id_set.end()) {
-          task_info_map->erase(iter++);
-        } else {
-          ++iter;
-        }
-      }
-    }
-
-    is_first = false;
-  }
-}
-
-void GetFileTasksFunction::FindDefaultDriveTasks(
-    const FileInfoList& file_info_list,
-    const TaskInfoMap& task_info_map,
-    std::set<std::string>* default_tasks) {
-  DCHECK(default_tasks);
-
-  for (size_t i = 0; i < file_info_list.size(); ++i) {
-    const FileInfo& file_info = file_info_list[i];
-    std::string task_id = file_handler_util::GetDefaultTaskIdFromPrefs(
-        profile_, file_info.mime_type, file_info.file_path.Extension());
-    if (task_info_map.find(task_id) != task_info_map.end())
-      default_tasks->insert(task_id);
-  }
-}
-
-// static
-void GetFileTasksFunction::CreateDriveTasks(
-    const TaskInfoMap& task_info_map,
-    const std::set<std::string>& default_tasks,
-    ListValue* result_list,
-    bool* default_already_set) {
-  DCHECK(result_list);
-  DCHECK(default_already_set);
-
-  for (TaskInfoMap::const_iterator iter = task_info_map.begin();
-       iter != task_info_map.end(); ++iter) {
-    DictionaryValue* task = new DictionaryValue;
-    task->SetString("taskId", iter->first);
-    task->SetString("title", iter->second.app_name);
-
-    const GURL& icon_url = iter->second.icon_url;
-    if (!icon_url.is_empty())
-      task->SetString("iconUrl", icon_url.spec());
-
-    task->SetBoolean("driveApp", true);
-
-    // Once we set a default app, we don't want to set any more.
-    if (!(*default_already_set) &&
-        default_tasks.find(iter->first) != default_tasks.end()) {
-      task->SetBoolean("isDefault", true);
-      *default_already_set = true;
-    } else {
-      task->SetBoolean("isDefault", false);
-    }
-    result_list->Append(task);
-  }
-}
-
-// Find special tasks here for Drive (Blox) apps. Iterate through matching drive
-// apps and add them, with generated task ids. Extension ids will be the app_ids
-// from drive. We'll know that they are drive apps because the extension id will
-// begin with kDriveTaskExtensionPrefix.
-bool GetFileTasksFunction::FindDriveAppTasks(
-    const FileInfoList& file_info_list,
-    ListValue* result_list,
-    bool* default_already_set) {
-  DCHECK(result_list);
-  DCHECK(default_already_set);
-
-  if (file_info_list.empty())
-    return true;
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled. We return true in this
-  // case because there might be other extension tasks, even if we don't have
-  // any to add.
-  if (!integration_service || !integration_service->drive_app_registry())
-    return true;
-
-  drive::DriveAppRegistry* registry =
-      integration_service->drive_app_registry();
-  DCHECK(registry);
-
-  // Map of task_id to TaskInfo of available tasks.
-  TaskInfoMap task_info_map;
-  GetAvailableDriveTasks(registry, file_info_list, &task_info_map);
-  std::set<std::string> default_tasks;
-  FindDefaultDriveTasks(file_info_list, task_info_map, &default_tasks);
-  CreateDriveTasks(
-      task_info_map, default_tasks, result_list, default_already_set);
-  return true;
-}
-
-bool GetFileTasksFunction::FindAppTasks(
-    const std::vector<base::FilePath>& file_paths,
-    ListValue* result_list,
-    bool* default_already_set) {
-  DCHECK(!file_paths.empty());
-  ExtensionService* service = profile_->GetExtensionService();
-  if (!service)
-    return false;
-
-  PathAndMimeTypeSet files;
-  GetMimeTypesForFileURLs(file_paths, &files);
-  std::set<std::string> default_tasks;
-  for (PathAndMimeTypeSet::iterator it = files.begin(); it != files.end();
-       ++it) {
-    default_tasks.insert(file_handler_util::GetDefaultTaskIdFromPrefs(
-        profile_, it->second, it->first.Extension()));
-  }
-
-  for (ExtensionSet::const_iterator iter = service->extensions()->begin();
-       iter != service->extensions()->end();
-       ++iter) {
-    const Extension* extension = iter->get();
-
-    // We don't support using hosted apps to open files.
-    if (!extension->is_platform_app())
-      continue;
-
-    if (profile_->IsOffTheRecord() &&
-        !service->IsIncognitoEnabled(extension->id()))
-      continue;
-
-    typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList;
-    FileHandlerList file_handlers = FindFileHandlersForFiles(*extension, files);
-    if (file_handlers.empty())
-      continue;
-
-    for (FileHandlerList::iterator i = file_handlers.begin();
-         i != file_handlers.end(); ++i) {
-      DictionaryValue* task = new DictionaryValue;
-      std::string task_id = file_handler_util::MakeTaskID(extension->id(),
-          file_handler_util::kTaskApp, (*i)->id);
-      task->SetString("taskId", task_id);
-      task->SetString("title", (*i)->title);
-      if (!(*default_already_set) && ContainsKey(default_tasks, task_id)) {
-        task->SetBoolean("isDefault", true);
-        *default_already_set = true;
-      } else {
-        task->SetBoolean("isDefault", false);
-      }
-
-      GURL best_icon =
-          ExtensionIconSource::GetIconURL(extension,
-                                          kPreferredIconSize,
-                                          ExtensionIconSet::MATCH_BIGGER,
-                                          false,  // grayscale
-                                          NULL);  // exists
-      if (!best_icon.is_empty())
-        task->SetString("iconUrl", best_icon.spec());
-      else
-        task->SetString("iconUrl", kDefaultIcon);
-
-      task->SetBoolean("driveApp", false);
-      result_list->Append(task);
-    }
-  }
-
-  return true;
-}
-
-bool GetFileTasksFunction::RunImpl() {
-  // First argument is the list of files to get tasks for.
-  ListValue* files_list = NULL;
-  if (!args_->GetList(0, &files_list))
-    return false;
-
-  if (files_list->GetSize() == 0)
-    return false;
-
-  // Second argument is the list of mime types of each of the files in the list.
-  ListValue* mime_types_list = NULL;
-  if (!args_->GetList(1, &mime_types_list))
-    return false;
-
-  // MIME types can either be empty, or there needs to be one for each file.
-  if (mime_types_list->GetSize() != files_list->GetSize() &&
-      mime_types_list->GetSize() != 0)
-    return false;
-
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
-  scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      BrowserContext::GetStoragePartition(profile(), site_instance)->
-          GetFileSystemContext();
-
-  // Collect all the URLs, convert them to GURLs, and crack all the urls into
-  // file paths.
-  FileInfoList info_list;
-  std::vector<GURL> file_urls;
-  std::vector<base::FilePath> file_paths;
-  bool has_google_document = false;
-  for (size_t i = 0; i < files_list->GetSize(); ++i) {
-    FileInfo info;
-    std::string file_url_str;
-    if (!files_list->GetString(i, &file_url_str))
-      return false;
-
-    if (mime_types_list->GetSize() != 0 &&
-        !mime_types_list->GetString(i, &info.mime_type))
-      return false;
-
-    GURL file_url(file_url_str);
-    fileapi::FileSystemURL file_system_url(
-        file_system_context->CrackURL(file_url));
-    if (!chromeos::FileSystemBackend::CanHandleURL(file_system_url))
-      continue;
-
-    file_urls.push_back(file_url);
-    file_paths.push_back(file_system_url.path());
-
-    info.file_url = file_url;
-    info.file_path = file_system_url.path();
-    info_list.push_back(info);
-
-    if (google_apis::ResourceEntry::ClassifyEntryKindByFileExtension(
-            info.file_path) &
-        google_apis::ResourceEntry::KIND_OF_GOOGLE_DOCUMENT) {
-      has_google_document = true;
-    }
-  }
-
-  ListValue* result_list = new ListValue();
-  SetResult(result_list);
-
-  // Find the Drive apps first, because we want them to take precedence
-  // when setting the default app.
-  bool default_already_set = false;
-  // Google document are not opened by drive apps but file manager.
-  if (!has_google_document) {
-    if (!FindDriveAppTasks(info_list, result_list, &default_already_set))
-      return false;
-  }
-
-  // Take the union of platform app file handlers, and all previous Drive
-  // and extension tasks. As above, we know there aren't duplicates because
-  // they're entirely different kinds of
-  // tasks.
-  if (!FindAppTasks(file_paths, result_list, &default_already_set))
-    return false;
-
-  // Take the union of Drive and extension tasks: Because any Drive tasks we
-  // found must apply to all of the files (intersection), and because the same
-  // is true of the extensions, we simply take the union of two lists by adding
-  // the extension tasks to the Drive task list. We know there aren't duplicates
-  // because they're entirely different kinds of tasks, but there could be both
-  // kinds of tasks for a file type (an image file, for instance).
-  file_handler_util::FileBrowserHandlerList common_tasks;
-  file_handler_util::FileBrowserHandlerList default_tasks;
-  if (!file_handler_util::FindCommonTasks(profile_, file_urls, &common_tasks))
-    return false;
-  file_handler_util::FindDefaultTasks(profile_, file_paths,
-                                      common_tasks, &default_tasks);
-
-  ExtensionService* service =
-      extensions::ExtensionSystem::Get(profile_)->extension_service();
-  for (file_handler_util::FileBrowserHandlerList::const_iterator iter =
-           common_tasks.begin();
-       iter != common_tasks.end();
-       ++iter) {
-    const FileBrowserHandler* handler = *iter;
-    const std::string extension_id = handler->extension_id();
-    const Extension* extension = service->GetExtensionById(extension_id, false);
-    CHECK(extension);
-    DictionaryValue* task = new DictionaryValue;
-    task->SetString("taskId", file_handler_util::MakeTaskID(
-        extension_id, file_handler_util::kTaskFile, handler->id()));
-    task->SetString("title", handler->title());
-    // TODO(zelidrag): Figure out how to expose icon URL that task defined in
-    // manifest instead of the default extension icon.
-    GURL icon =
-        ExtensionIconSource::GetIconURL(extension,
-                                        extension_misc::EXTENSION_ICON_BITTY,
-                                        ExtensionIconSet::MATCH_BIGGER,
-                                        false, NULL);     // grayscale
-    task->SetString("iconUrl", icon.spec());
-    task->SetBoolean("driveApp", false);
-
-    // Only set the default if there isn't already a default set.
-    if (!default_already_set &&
-        std::find(default_tasks.begin(), default_tasks.end(), *iter) !=
-            default_tasks.end()) {
-      task->SetBoolean("isDefault", true);
-      default_already_set = true;
-    } else {
-      task->SetBoolean("isDefault", false);
-    }
-
-    result_list->Append(task);
-  }
-
-  SendResponse(true);
-  return true;
-}
-
-ExecuteTasksFunction::ExecuteTasksFunction() {
-}
-
-ExecuteTasksFunction::~ExecuteTasksFunction() {
-}
-
-bool ExecuteTasksFunction::RunImpl() {
-  // First param is task id that was to the extension with getFileTasks call.
-  std::string task_id;
-  if (!args_->GetString(0, &task_id) || !task_id.size())
-    return false;
-
-  // TODO(kaznacheev): Crack the task_id here, store it in the Executor
-  // and avoid passing it around.
-
-  // The second param is the list of files that need to be executed with this
-  // task.
-  ListValue* files_list = NULL;
-  if (!args_->GetList(1, &files_list))
-    return false;
-
-  std::string extension_id;
-  std::string task_type;
-  std::string action_id;
-  if (!file_handler_util::CrackTaskID(
-      task_id, &extension_id, &task_type, &action_id)) {
-    LOG(WARNING) << "Invalid task " << task_id;
-    return false;
-  }
-
-  if (!files_list->GetSize())
-    return true;
-
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
-  scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      BrowserContext::GetStoragePartition(profile(), site_instance)->
-          GetFileSystemContext();
-
-  std::vector<FileSystemURL> file_urls;
-  for (size_t i = 0; i < files_list->GetSize(); i++) {
-    std::string file_url_str;
-    if (!files_list->GetString(i, &file_url_str)) {
-      error_ = kInvalidFileUrl;
-      return false;
-    }
-    FileSystemURL url = file_system_context->CrackURL(GURL(file_url_str));
-    if (!chromeos::FileSystemBackend::CanHandleURL(url)) {
-      error_ = kInvalidFileUrl;
-      return false;
-    }
-    file_urls.push_back(url);
-  }
-
-  int32 tab_id = GetTabId(dispatcher());
-  return file_handler_util::ExecuteFileTask(
-      profile(),
-      source_url(),
-      extension_->id(),
-      tab_id,
-      extension_id,
-      task_type,
-      action_id,
-      file_urls,
-      base::Bind(&ExecuteTasksFunction::OnTaskExecuted, this));
-}
-
-void ExecuteTasksFunction::OnTaskExecuted(bool success) {
-  SetResult(new base::FundamentalValue(success));
-  SendResponse(true);
-}
-
-SetDefaultTaskFunction::SetDefaultTaskFunction() {
-}
-
-SetDefaultTaskFunction::~SetDefaultTaskFunction() {
-}
-
-bool SetDefaultTaskFunction::RunImpl() {
-  // First param is task id that was to the extension with setDefaultTask call.
-  std::string task_id;
-  if (!args_->GetString(0, &task_id) || !task_id.size())
-    return false;
-
-  base::ListValue* file_url_list;
-  if (!args_->GetList(1, &file_url_list))
-    return false;
-
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
-  scoped_refptr<fileapi::FileSystemContext> context =
-      BrowserContext::GetStoragePartition(profile(), site_instance)->
-          GetFileSystemContext();
-
-  std::set<std::string> suffixes =
-      GetUniqueSuffixes(file_url_list, context.get());
-
-  // MIME types are an optional parameter.
-  base::ListValue* mime_type_list;
-  std::set<std::string> mime_types;
-  if (args_->GetList(2, &mime_type_list) && !mime_type_list->empty()) {
-    if (mime_type_list->GetSize() != file_url_list->GetSize())
-      return false;
-    mime_types = GetUniqueMimeTypes(mime_type_list);
-  }
-
-  if (VLOG_IS_ON(1))
-    LogDefaultTask(mime_types, suffixes, task_id);
-
-  // If there weren't any mime_types, and all the suffixes were blank,
-  // then we "succeed", but don't actually associate with anything.
-  // Otherwise, any time we set the default on a file with no extension
-  // on the local drive, we'd fail.
-  // TODO(gspencer): Fix file manager so that it never tries to set default in
-  // cases where extensionless local files are part of the selection.
-  if (suffixes.empty() && mime_types.empty()) {
-    SetResult(new base::FundamentalValue(true));
-    return true;
-  }
-
-  file_handler_util::UpdateDefaultTask(profile_, task_id, suffixes, mime_types);
-
-  return true;
-}
-
-ViewFilesFunction::ViewFilesFunction() {
-}
-
-ViewFilesFunction::~ViewFilesFunction() {
-}
-
-bool ViewFilesFunction::RunImpl() {
-  if (args_->GetSize() < 1) {
-    return false;
-  }
-
-  ListValue* path_list = NULL;
-  args_->GetList(0, &path_list);
-  DCHECK(path_list);
-
-  std::string internal_task_id;
-  args_->GetString(1, &internal_task_id);
-
-  std::vector<base::FilePath> files;
-  for (size_t i = 0; i < path_list->GetSize(); ++i) {
-    std::string url_as_string;
-    path_list->GetString(i, &url_as_string);
-    base::FilePath path = GetLocalPathFromURL(
-        render_view_host(), profile(), GURL(url_as_string));
-    if (path.empty())
-      return false;
-    files.push_back(path);
-  }
-
-  Browser* browser = chrome::FindOrCreateTabbedBrowser(
-      profile_, chrome::HOST_DESKTOP_TYPE_ASH);
-  bool success = browser;
-
-  if (browser) {
-    for (size_t i = 0; i < files.size(); ++i) {
-      bool handled = file_manager_util::ExecuteBuiltinHandler(
-          browser, files[i], internal_task_id);
-      if (!handled && files.size() == 1)
-        success = false;
-    }
-  }
-
-  SetResult(Value::CreateBooleanValue(success));
-  SendResponse(true);
-  return true;
-}
-
-SelectFileFunction::SelectFileFunction() {
-}
-
-SelectFileFunction::~SelectFileFunction() {
-}
-
-bool SelectFileFunction::RunImpl() {
-  if (args_->GetSize() != 4) {
-    return false;
-  }
-  std::string file_url;
-  args_->GetString(0, &file_url);
-  UrlList file_paths;
-  file_paths.push_back(GURL(file_url));
-  bool for_opening = false;
-  args_->GetBoolean(2, &for_opening);
-
-  GetSelectedFileInfo(
-      render_view_host(),
-      profile(),
-      file_paths,
-      for_opening,
-      base::Bind(&SelectFileFunction::GetSelectedFileInfoResponse, this));
-  return true;
-}
-
-void SelectFileFunction::GetSelectedFileInfoResponse(
-    const std::vector<ui::SelectedFileInfo>& files) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (files.size() != 1) {
-    SendResponse(false);
-    return;
-  }
-  int index;
-  args_->GetInteger(1, &index);
-  int32 tab_id = GetTabId(dispatcher());
-  SelectFileDialogExtension::OnFileSelected(tab_id, files[0], index);
-  SendResponse(true);
-}
-
-SelectFilesFunction::SelectFilesFunction() {
-}
-
-SelectFilesFunction::~SelectFilesFunction() {
-}
-
-bool SelectFilesFunction::RunImpl() {
-  if (args_->GetSize() != 2) {
-    return false;
-  }
-
-  ListValue* path_list = NULL;
-  args_->GetList(0, &path_list);
-  DCHECK(path_list);
-
-  std::string virtual_path;
-  size_t len = path_list->GetSize();
-  UrlList file_urls;
-  file_urls.reserve(len);
-  for (size_t i = 0; i < len; ++i) {
-    path_list->GetString(i, &virtual_path);
-    file_urls.push_back(GURL(virtual_path));
-  }
-
-  GetSelectedFileInfo(
-      render_view_host(),
-      profile(),
-      file_urls,
-      true,  // for_opening
-      base::Bind(&SelectFilesFunction::GetSelectedFileInfoResponse, this));
-  return true;
-}
-
-void SelectFilesFunction::GetSelectedFileInfoResponse(
-    const std::vector<ui::SelectedFileInfo>& files) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  int32 tab_id = GetTabId(dispatcher());
-  SelectFileDialogExtension::OnMultiFilesSelected(tab_id, files);
-  SendResponse(true);
-}
-
-CancelFileDialogFunction::CancelFileDialogFunction() {
-}
-
-CancelFileDialogFunction::~CancelFileDialogFunction() {
-}
-
-bool CancelFileDialogFunction::RunImpl() {
-  int32 tab_id = GetTabId(dispatcher());
-  SelectFileDialogExtension::OnFileSelectionCanceled(tab_id);
-  SendResponse(true);
-  return true;
-}
-
-AddMountFunction::AddMountFunction() {
-}
-
-AddMountFunction::~AddMountFunction() {
-}
-
-bool AddMountFunction::RunImpl() {
-  // The third argument is simply ignored.
-  if (args_->GetSize() != 2 && args_->GetSize() != 3) {
-    error_ = "Invalid argument count";
-    return false;
-  }
-
-  std::string file_url;
-  if (!args_->GetString(0, &file_url)) {
-    return false;
-  }
-
-  std::string mount_type_str;
-  if (!args_->GetString(1, &mount_type_str)) {
-    return false;
-  }
-
-  drive::util::Log("%s[%d] called. (source: '%s', type:'%s')",
-                   name().c_str(),
-                   request_id(),
-                   file_url.empty() ? "(none)" : file_url.c_str(),
-                   mount_type_str.c_str());
-  set_log_on_completion(true);
-
-  // Set default return source path to the empty string.
-  SetResult(new base::StringValue(""));
-
-  chromeos::MountType mount_type =
-      DiskMountManager::MountTypeFromString(mount_type_str);
-  switch (mount_type) {
-    case chromeos::MOUNT_TYPE_INVALID: {
-      error_ = "Invalid mount type";
-      SendResponse(false);
-      break;
-    }
-    case chromeos::MOUNT_TYPE_GOOGLE_DRIVE: {
-      // Dispatch fake 'mounted' event because JS code depends on it.
-      // TODO(hashimoto): Remove this redanduncy.
-      FileBrowserPrivateAPI::Get(profile_)->event_router()->
-          OnFileSystemMounted();
-
-      // Pass back the drive mount point path as source path.
-      const std::string& drive_path =
-          drive::util::GetDriveMountPointPathAsString();
-      SetResult(new base::StringValue(drive_path));
-      SendResponse(true);
-      break;
-    }
-    default: {
-      const base::FilePath path = GetLocalPathFromURL(
-          render_view_host(), profile(), GURL(file_url));
-
-      if (path.empty()) {
-        SendResponse(false);
-        break;
-      }
-
-      const base::FilePath::StringType display_name = path.BaseName().value();
-
-      // Check if the source path is under Drive cache directory.
-      if (drive::util::IsUnderDriveMountPoint(path)) {
-        drive::DriveIntegrationService* integration_service =
-            drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-        drive::FileSystemInterface* file_system =
-            integration_service ? integration_service->file_system() : NULL;
-        if (!file_system) {
-          SendResponse(false);
-          break;
-        }
-        file_system->MarkCacheFileAsMounted(
-            drive::util::ExtractDrivePath(path),
-            base::Bind(&AddMountFunction::OnMountedStateSet,
-                       this, mount_type_str, display_name));
-      } else {
-        OnMountedStateSet(mount_type_str, display_name,
-                          drive::FILE_ERROR_OK, path);
-      }
-      break;
-    }
-  }
-
-  return true;
-}
-
-void AddMountFunction::OnMountedStateSet(
-    const std::string& mount_type,
-    const base::FilePath::StringType& file_name,
-    drive::FileError error,
-    const base::FilePath& file_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (error != drive::FILE_ERROR_OK) {
-    SendResponse(false);
-    return;
-  }
-
-  DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
-  // Pass back the actual source path of the mount point.
-  SetResult(new base::StringValue(file_path.value()));
-  SendResponse(true);
-  // MountPath() takes a std::string.
-  disk_mount_manager->MountPath(
-      file_path.AsUTF8Unsafe(), base::FilePath(file_name).Extension(),
-      file_name, DiskMountManager::MountTypeFromString(mount_type));
-}
-
-RemoveMountFunction::RemoveMountFunction() {
-}
-
-RemoveMountFunction::~RemoveMountFunction() {
-}
-
-bool RemoveMountFunction::RunImpl() {
-  if (args_->GetSize() != 1) {
-    return false;
-  }
-
-  std::string mount_path;
-  if (!args_->GetString(0, &mount_path)) {
-    return false;
-  }
-
-  drive::util::Log("%s[%d] called. (mount_path: '%s')",
-                   name().c_str(),
-                   request_id(),
-                   mount_path.c_str());
-  set_log_on_completion(true);
-
-  UrlList file_paths;
-  file_paths.push_back(GURL(mount_path));
-  GetSelectedFileInfo(
-      render_view_host(),
-      profile(),
-      file_paths,
-      true,  // for_opening
-      base::Bind(&RemoveMountFunction::GetSelectedFileInfoResponse, this));
-  return true;
-}
-
-void RemoveMountFunction::GetSelectedFileInfoResponse(
-    const std::vector<ui::SelectedFileInfo>& files) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (files.size() != 1) {
-    SendResponse(false);
-    return;
-  }
-
-  // TODO(tbarzic): Send response when callback is received, it would make more
-  // sense than remembering issued unmount requests in file manager and showing
-  // errors for them when MountCompleted event is received.
-  DiskMountManager::GetInstance()->UnmountPath(
-      files[0].local_path.value(),
-      chromeos::UNMOUNT_OPTIONS_NONE,
-      DiskMountManager::UnmountPathCallback());
-  SendResponse(true);
-}
-
-GetMountPointsFunction::GetMountPointsFunction() {
-}
-
-GetMountPointsFunction::~GetMountPointsFunction() {
-}
-
-bool GetMountPointsFunction::RunImpl() {
-  if (args_->GetSize())
-    return false;
-
-  base::ListValue *mounts = new base::ListValue();
-  SetResult(mounts);
-
-  DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
-  DiskMountManager::MountPointMap mount_points =
-      disk_mount_manager->mount_points();
-
-  std::string log_string = "[";
-  const char *separator = "";
-  for (DiskMountManager::MountPointMap::const_iterator it =
-           mount_points.begin();
-       it != mount_points.end();
-       ++it) {
-    mounts->Append(CreateValueFromMountPoint(profile_, it->second,
-        extension_->id(), source_url_));
-    log_string += separator + it->first;
-    separator = ", ";
-  }
-
-  log_string += "]";
-
-  drive::util::Log("%s[%d] succeeded. (results: '%s', %"PRIuS" mount points)",
-                   name().c_str(),
-                   request_id(),
-                   log_string.c_str(),
-                   mount_points.size());
-
-  SendResponse(true);
-  return true;
-}
-
-SetLastModifiedFunction::SetLastModifiedFunction() {
-}
-
-SetLastModifiedFunction::~SetLastModifiedFunction() {
-}
-
-bool SetLastModifiedFunction::RunImpl() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (args_->GetSize() != 2) {
-    return false;
-  }
-
-  std::string file_url;
-  if (!args_->GetString(0, &file_url))
-    return false;
-
-  std::string timestamp;
-  if (!args_->GetString(1, &timestamp))
-    return false;
-
-  base::FilePath local_path = GetLocalPathFromURL(
-      render_view_host(), profile(), GURL(file_url));
-
-  base::PostTaskAndReplyWithResult(
-      BrowserThread::GetBlockingPool(),
-      FROM_HERE,
-      base::Bind(&SetLastModifiedOnBlockingPool,
-                 local_path,
-                 strtoul(timestamp.c_str(), NULL, 0)),
-      base::Bind(&SetLastModifiedFunction::SendResponse,
-                 this));
-  return true;
-}
-
-GetSizeStatsFunction::GetSizeStatsFunction() {
-}
-
-GetSizeStatsFunction::~GetSizeStatsFunction() {
-}
-
-bool GetSizeStatsFunction::RunImpl() {
-  if (args_->GetSize() != 1) {
-    return false;
-  }
-
-  std::string mount_url;
-  if (!args_->GetString(0, &mount_url))
-    return false;
-
-  base::FilePath file_path = GetLocalPathFromURL(
-      render_view_host(), profile(), GURL(mount_url));
-  if (file_path.empty())
-    return false;
-
-  if (file_path == drive::util::GetDriveMountPointPath()) {
-    drive::DriveIntegrationService* integration_service =
-        drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-    // |integration_service| is NULL if Drive is disabled.
-    if (!integration_service) {
-      // If stats couldn't be gotten for drive, result should be left
-      // undefined. See comments in GetDriveAvailableSpaceCallback().
-      SendResponse(true);
-      return true;
-    }
-
-    drive::FileSystemInterface* file_system =
-        integration_service->file_system();
-
-    file_system->GetAvailableSpace(
-        base::Bind(&GetSizeStatsFunction::GetDriveAvailableSpaceCallback,
-                   this));
-
-  } else {
-    size_t* total_size_kb = new size_t(0);
-    size_t* remaining_size_kb = new size_t(0);
-    BrowserThread::PostBlockingPoolTaskAndReply(
-        FROM_HERE,
-        base::Bind(&GetSizeStatsOnBlockingPool,
-                   file_path.value(),
-                   total_size_kb,
-                   remaining_size_kb),
-        base::Bind(&GetSizeStatsFunction::GetSizeStatsCallback,
-                   this,
-                   base::Owned(total_size_kb),
-                   base::Owned(remaining_size_kb)));
-  }
-  return true;
-}
-
-void GetSizeStatsFunction::GetDriveAvailableSpaceCallback(
-    drive::FileError error,
-    int64 bytes_total,
-    int64 bytes_used) {
-  if (error == drive::FILE_ERROR_OK) {
-    int64 bytes_remaining = bytes_total - bytes_used;
-    const size_t total_size_kb = static_cast<size_t>(bytes_total/1024);
-    const size_t remaining_size_kb = static_cast<size_t>(bytes_remaining/1024);
-    GetSizeStatsCallback(&total_size_kb, &remaining_size_kb);
-  } else {
-    // If stats couldn't be gotten for drive, result should be left undefined.
-    SendResponse(true);
-  }
-}
-
-void GetSizeStatsFunction::GetSizeStatsCallback(
-    const size_t* total_size_kb,
-    const size_t* remaining_size_kb) {
-  base::DictionaryValue* sizes = new base::DictionaryValue();
-  SetResult(sizes);
-
-  sizes->SetInteger("totalSizeKB", *total_size_kb);
-  sizes->SetInteger("remainingSizeKB", *remaining_size_kb);
-
-  SendResponse(true);
-}
-
-FormatDeviceFunction::FormatDeviceFunction() {
-}
-
-FormatDeviceFunction::~FormatDeviceFunction() {
-}
-
-bool FormatDeviceFunction::RunImpl() {
-  if (args_->GetSize() != 1) {
-    return false;
-  }
-
-  std::string volume_file_url;
-  if (!args_->GetString(0, &volume_file_url)) {
-    NOTREACHED();
-    return false;
-  }
-
-  base::FilePath file_path = GetLocalPathFromURL(
-      render_view_host(), profile(), GURL(volume_file_url));
-  if (file_path.empty())
-    return false;
-
-  DiskMountManager::GetInstance()->FormatMountedDevice(file_path.value());
-  SendResponse(true);
-  return true;
-}
-
-GetVolumeMetadataFunction::GetVolumeMetadataFunction() {
-}
-
-GetVolumeMetadataFunction::~GetVolumeMetadataFunction() {
-}
-
-bool GetVolumeMetadataFunction::RunImpl() {
-  if (args_->GetSize() != 1) {
-    error_ = "Invalid argument count";
-    return false;
-  }
-
-  std::string volume_mount_url;
-  if (!args_->GetString(0, &volume_mount_url)) {
-    NOTREACHED();
-    return false;
-  }
-
-  base::FilePath file_path = GetLocalPathFromURL(
-      render_view_host(), profile(), GURL(volume_mount_url));
-  if (file_path.empty()) {
-    error_ = "Invalid mount path.";
-    return false;
-  }
-
-  results_.reset();
-
-  const DiskMountManager::Disk* volume = GetVolumeAsDisk(file_path.value());
-  if (volume) {
-    DictionaryValue* volume_info =
-        CreateValueFromDisk(profile_, extension_->id(), volume);
-    SetResult(volume_info);
-  }
-
-  SendResponse(true);
-  return true;
-}
-
-FileDialogStringsFunction::FileDialogStringsFunction() {
-}
-
-FileDialogStringsFunction::~FileDialogStringsFunction() {
-}
-
-bool FileDialogStringsFunction::RunImpl() {
-  DictionaryValue* dict = new DictionaryValue();
-  SetResult(dict);
-
-#define SET_STRING(id, idr) \
-  dict->SetString(id, l10n_util::GetStringUTF16(idr))
-
-  SET_STRING("WEB_FONT_FAMILY", IDS_WEB_FONT_FAMILY);
-  SET_STRING("WEB_FONT_SIZE", IDS_WEB_FONT_SIZE);
-
-  SET_STRING("ROOT_DIRECTORY_LABEL", IDS_FILE_BROWSER_ROOT_DIRECTORY_LABEL);
-  SET_STRING("ARCHIVE_DIRECTORY_LABEL",
-             IDS_FILE_BROWSER_ARCHIVE_DIRECTORY_LABEL);
-  SET_STRING("REMOVABLE_DIRECTORY_LABEL",
-             IDS_FILE_BROWSER_REMOVABLE_DIRECTORY_LABEL);
-  SET_STRING("DOWNLOADS_DIRECTORY_LABEL",
-             IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_LABEL);
-  SET_STRING("DRIVE_DIRECTORY_LABEL", IDS_FILE_BROWSER_DRIVE_DIRECTORY_LABEL);
-  SET_STRING("DRIVE_MY_DRIVE_LABEL", IDS_FILE_BROWSER_DRIVE_MY_DRIVE_LABEL);
-  SET_STRING("DRIVE_OFFLINE_COLLECTION_LABEL",
-             IDS_FILE_BROWSER_DRIVE_OFFLINE_COLLECTION_LABEL);
-  SET_STRING("DRIVE_SHARED_WITH_ME_COLLECTION_LABEL",
-             IDS_FILE_BROWSER_DRIVE_SHARED_WITH_ME_COLLECTION_LABEL);
-  SET_STRING("DRIVE_RECENT_COLLECTION_LABEL",
-             IDS_FILE_BROWSER_DRIVE_RECENT_COLLECTION_LABEL);
-  SET_STRING("NAME_COLUMN_LABEL", IDS_FILE_BROWSER_NAME_COLUMN_LABEL);
-  SET_STRING("SIZE_COLUMN_LABEL", IDS_FILE_BROWSER_SIZE_COLUMN_LABEL);
-  SET_STRING("SIZE_BYTES", IDS_FILE_BROWSER_SIZE_BYTES);
-  SET_STRING("SIZE_KB", IDS_FILE_BROWSER_SIZE_KB);
-  SET_STRING("SIZE_MB", IDS_FILE_BROWSER_SIZE_MB);
-  SET_STRING("SIZE_GB", IDS_FILE_BROWSER_SIZE_GB);
-  SET_STRING("SIZE_TB", IDS_FILE_BROWSER_SIZE_TB);
-  SET_STRING("SIZE_PB", IDS_FILE_BROWSER_SIZE_PB);
-
-  SET_STRING("SHORTCUT_CTRL", IDS_FILE_BROWSER_SHORTCUT_CTRL);
-  SET_STRING("SHORTCUT_ALT", IDS_FILE_BROWSER_SHORTCUT_ALT);
-  SET_STRING("SHORTCUT_SHIFT", IDS_FILE_BROWSER_SHORTCUT_SHIFT);
-  SET_STRING("SHORTCUT_META", IDS_FILE_BROWSER_SHORTCUT_META);
-  SET_STRING("SHORTCUT_SPACE", IDS_FILE_BROWSER_SHORTCUT_SPACE);
-  SET_STRING("SHORTCUT_ENTER", IDS_FILE_BROWSER_SHORTCUT_ENTER);
-
-  SET_STRING("TYPE_COLUMN_LABEL", IDS_FILE_BROWSER_TYPE_COLUMN_LABEL);
-  SET_STRING("DATE_COLUMN_LABEL", IDS_FILE_BROWSER_DATE_COLUMN_LABEL);
-  SET_STRING("PREVIEW_COLUMN_LABEL", IDS_FILE_BROWSER_PREVIEW_COLUMN_LABEL);
-  SET_STRING("OFFLINE_COLUMN_LABEL", IDS_FILE_BROWSER_OFFLINE_COLUMN_LABEL);
-
-  SET_STRING("DOWNLOADS_DIRECTORY_WARNING",
-             IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_WARNING);
-
-  SET_STRING("ERROR_CREATING_FOLDER", IDS_FILE_BROWSER_ERROR_CREATING_FOLDER);
-  SET_STRING("ERROR_INVALID_CHARACTER",
-             IDS_FILE_BROWSER_ERROR_INVALID_CHARACTER);
-  SET_STRING("ERROR_RESERVED_NAME", IDS_FILE_BROWSER_ERROR_RESERVED_NAME);
-  SET_STRING("ERROR_HIDDEN_NAME", IDS_FILE_BROWSER_ERROR_HIDDEN_NAME);
-  SET_STRING("ERROR_WHITESPACE_NAME", IDS_FILE_BROWSER_ERROR_WHITESPACE_NAME);
-  SET_STRING("ERROR_NEW_FOLDER_EMPTY_NAME",
-             IDS_FILE_BROWSER_ERROR_NEW_FOLDER_EMPTY_NAME);
-  SET_STRING("ERROR_LONG_NAME", IDS_FILE_BROWSER_ERROR_LONG_NAME);
-  SET_STRING("NEW_FOLDER_BUTTON_LABEL",
-             IDS_FILE_BROWSER_NEW_FOLDER_BUTTON_LABEL);
-  SET_STRING("NEW_WINDOW_BUTTON_LABEL",
-             IDS_FILE_BROWSER_NEW_WINDOW_BUTTON_LABEL);
-  SET_STRING("CHANGE_DEFAULT_APP_BUTTON_LABEL",
-             IDS_FILE_BROWSER_CHANGE_DEFAULT_APP_BUTTON_LABEL);
-  SET_STRING("FILENAME_LABEL", IDS_FILE_BROWSER_FILENAME_LABEL);
-  SET_STRING("PREPARING_LABEL", IDS_FILE_BROWSER_PREPARING_LABEL);
-  SET_STRING("DRAGGING_MULTIPLE_ITEMS",
-             IDS_FILE_BROWSER_DRAGGING_MULTIPLE_ITEMS);
-
-  SET_STRING("DIMENSIONS_LABEL", IDS_FILE_BROWSER_DIMENSIONS_LABEL);
-  SET_STRING("DIMENSIONS_FORMAT", IDS_FILE_BROWSER_DIMENSIONS_FORMAT);
-
-  SET_STRING("IMAGE_DIMENSIONS", IDS_FILE_BROWSER_IMAGE_DIMENSIONS);
-  SET_STRING("VOLUME_LABEL", IDS_FILE_BROWSER_VOLUME_LABEL);
-  SET_STRING("READ_ONLY", IDS_FILE_BROWSER_READ_ONLY);
-
-  SET_STRING("ARCHIVE_MOUNT_FAILED", IDS_FILE_BROWSER_ARCHIVE_MOUNT_FAILED);
-  SET_STRING("UNMOUNT_FAILED", IDS_FILE_BROWSER_UNMOUNT_FAILED);
-  SET_STRING("MOUNT_ARCHIVE", IDS_FILE_BROWSER_MOUNT_ARCHIVE);
-  SET_STRING("FORMAT_DEVICE_BUTTON_LABEL",
-             IDS_FILE_BROWSER_FORMAT_DEVICE_BUTTON_LABEL);
-  SET_STRING("UNMOUNT_DEVICE_BUTTON_LABEL",
-             IDS_FILE_BROWSER_UNMOUNT_DEVICE_BUTTON_LABEL);
-  SET_STRING("CLOSE_ARCHIVE_BUTTON_LABEL",
-             IDS_FILE_BROWSER_CLOSE_ARCHIVE_BUTTON_LABEL);
-
-  SET_STRING("SEARCH_TEXT_LABEL", IDS_FILE_BROWSER_SEARCH_TEXT_LABEL);
-
-  SET_STRING("ACTION_VIEW", IDS_FILE_BROWSER_ACTION_VIEW);
-  SET_STRING("ACTION_OPEN", IDS_FILE_BROWSER_ACTION_OPEN);
-  SET_STRING("ACTION_OPEN_GDOC", IDS_FILE_BROWSER_ACTION_OPEN_GDOC);
-  SET_STRING("ACTION_OPEN_GSHEET", IDS_FILE_BROWSER_ACTION_OPEN_GSHEET);
-  SET_STRING("ACTION_OPEN_GSLIDES", IDS_FILE_BROWSER_ACTION_OPEN_GSLIDES);
-  SET_STRING("ACTION_WATCH", IDS_FILE_BROWSER_ACTION_WATCH);
-  SET_STRING("ACTION_LISTEN", IDS_FILE_BROWSER_ACTION_LISTEN);
-  SET_STRING("INSTALL_CRX", IDS_FILE_BROWSER_INSTALL_CRX);
-  SET_STRING("SEND_TO_DRIVE", IDS_FILE_BROWSER_SEND_TO_DRIVE);
-
-  SET_STRING("GALLERY_NO_IMAGES", IDS_FILE_BROWSER_GALLERY_NO_IMAGES);
-  SET_STRING("GALLERY_ITEMS_SELECTED", IDS_FILE_BROWSER_GALLERY_ITEMS_SELECTED);
-  SET_STRING("GALLERY_MOSAIC", IDS_FILE_BROWSER_GALLERY_MOSAIC);
-  SET_STRING("GALLERY_SLIDE", IDS_FILE_BROWSER_GALLERY_SLIDE);
-  SET_STRING("GALLERY_DELETE", IDS_FILE_BROWSER_GALLERY_DELETE);
-  SET_STRING("GALLERY_SLIDESHOW", IDS_FILE_BROWSER_GALLERY_SLIDESHOW);
-
-  SET_STRING("GALLERY_EDIT", IDS_FILE_BROWSER_GALLERY_EDIT);
-  SET_STRING("GALLERY_PRINT", IDS_FILE_BROWSER_GALLERY_PRINT);
-  SET_STRING("GALLERY_SHARE", IDS_FILE_BROWSER_GALLERY_SHARE);
-  SET_STRING("GALLERY_ENTER_WHEN_DONE",
-             IDS_FILE_BROWSER_GALLERY_ENTER_WHEN_DONE);
-  SET_STRING("GALLERY_AUTOFIX", IDS_FILE_BROWSER_GALLERY_AUTOFIX);
-  SET_STRING("GALLERY_FIXED", IDS_FILE_BROWSER_GALLERY_FIXED);
-  SET_STRING("GALLERY_CROP", IDS_FILE_BROWSER_GALLERY_CROP);
-  SET_STRING("GALLERY_EXPOSURE", IDS_FILE_BROWSER_GALLERY_EXPOSURE);
-  SET_STRING("GALLERY_BRIGHTNESS", IDS_FILE_BROWSER_GALLERY_BRIGHTNESS);
-  SET_STRING("GALLERY_CONTRAST", IDS_FILE_BROWSER_GALLERY_CONTRAST);
-  SET_STRING("GALLERY_ROTATE_LEFT", IDS_FILE_BROWSER_GALLERY_ROTATE_LEFT);
-  SET_STRING("GALLERY_ROTATE_RIGHT", IDS_FILE_BROWSER_GALLERY_ROTATE_RIGHT);
-  SET_STRING("GALLERY_UNDO", IDS_FILE_BROWSER_GALLERY_UNDO);
-  SET_STRING("GALLERY_REDO", IDS_FILE_BROWSER_GALLERY_REDO);
-  SET_STRING("GALLERY_FILE_EXISTS", IDS_FILE_BROWSER_GALLERY_FILE_EXISTS);
-  SET_STRING("GALLERY_SAVED", IDS_FILE_BROWSER_GALLERY_SAVED);
-  SET_STRING("GALLERY_OVERWRITE_ORIGINAL",
-             IDS_FILE_BROWSER_GALLERY_OVERWRITE_ORIGINAL);
-  SET_STRING("GALLERY_OVERWRITE_BUBBLE",
-             IDS_FILE_BROWSER_GALLERY_OVERWRITE_BUBBLE);
-  SET_STRING("GALLERY_UNSAVED_CHANGES",
-             IDS_FILE_BROWSER_GALLERY_UNSAVED_CHANGES);
-  SET_STRING("GALLERY_READONLY_WARNING",
-             IDS_FILE_BROWSER_GALLERY_READONLY_WARNING);
-  SET_STRING("GALLERY_IMAGE_ERROR", IDS_FILE_BROWSER_GALLERY_IMAGE_ERROR);
-  SET_STRING("GALLERY_IMAGE_TOO_BIG_ERROR",
-             IDS_FILE_BROWSER_GALLERY_IMAGE_TOO_BIG_ERROR);
-  SET_STRING("GALLERY_VIDEO_ERROR", IDS_FILE_BROWSER_GALLERY_VIDEO_ERROR);
-  SET_STRING("GALLERY_VIDEO_DECODING_ERROR",
-             IDS_FILE_BROWSER_GALLERY_VIDEO_DECODING_ERROR);
-  SET_STRING("GALLERY_VIDEO_LOOPED_MODE",
-             IDS_FILE_BROWSER_GALLERY_VIDEO_LOOPED_MODE);
-  SET_STRING("AUDIO_ERROR", IDS_FILE_BROWSER_AUDIO_ERROR);
-  SET_STRING("GALLERY_IMAGE_OFFLINE", IDS_FILE_BROWSER_GALLERY_IMAGE_OFFLINE);
-  SET_STRING("GALLERY_VIDEO_OFFLINE", IDS_FILE_BROWSER_GALLERY_VIDEO_OFFLINE);
-  SET_STRING("AUDIO_OFFLINE", IDS_FILE_BROWSER_AUDIO_OFFLINE);
-  // Reusing strings, but with alias starting with GALLERY.
-  dict->SetString("GALLERY_FILE_HIDDEN_NAME",
-      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_ERROR_HIDDEN_NAME));
-  dict->SetString("GALLERY_OK_LABEL",
-      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_OK_LABEL));
-  dict->SetString("GALLERY_CANCEL_LABEL",
-      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_CANCEL_LABEL));
-  dict->SetString("GALLERY_CONFIRM_DELETE_ONE",
-      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_CONFIRM_DELETE_ONE));
-  dict->SetString("GALLERY_CONFIRM_DELETE_SOME",
-      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_CONFIRM_DELETE_SOME));
-
-  SET_STRING("ACTION_CHOICE_OPENING_METHOD",
-             IDS_FILE_BROWSER_ACTION_CHOICE_OPENING_METHOD);
-  SET_STRING("ACTION_CHOICE_PHOTOS_DRIVE",
-             IDS_FILE_BROWSER_ACTION_CHOICE_PHOTOS_DRIVE);
-  SET_STRING("ACTION_CHOICE_DRIVE_NOT_REACHED",
-             IDS_FILE_BROWSER_ACTION_CHOICE_DRIVE_NOT_REACHED);
-  SET_STRING("ACTION_CHOICE_VIEW_FILES",
-             IDS_FILE_BROWSER_ACTION_CHOICE_VIEW_FILES);
-  SET_STRING("ACTION_CHOICE_WATCH_SINGLE_VIDEO",
-             IDS_FILE_BROWSER_ACTION_CHOICE_WATCH_SINGLE_VIDEO);
-  SET_STRING("ACTION_CHOICE_ONCE", IDS_FILE_BROWSER_ACTION_CHOICE_ONCE);
-  SET_STRING("ACTION_CHOICE_ALWAYS", IDS_FILE_BROWSER_ACTION_CHOICE_ALWAYS);
-  SET_STRING("ACTION_CHOICE_COUNTER_NO_MEDIA",
-             IDS_FILE_BROWSER_ACTION_CHOICE_COUNTER_NO_MEDIA);
-  SET_STRING("ACTION_CHOICE_COUNTER", IDS_FILE_BROWSER_ACTION_CHOICE_COUNTER);
-  SET_STRING("ACTION_CHOICE_LOADING_USB",
-             IDS_FILE_BROWSER_ACTION_CHOICE_LOADING_USB);
-  SET_STRING("ACTION_CHOICE_LOADING_SD",
-             IDS_FILE_BROWSER_ACTION_CHOICE_LOADING_SD);
-
-  SET_STRING("PHOTO_IMPORT_TITLE", IDS_FILE_BROWSER_PHOTO_IMPORT_TITLE);
-  SET_STRING("PHOTO_IMPORT_IMPORT_BUTTON",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_IMPORT_BUTTON);
-  SET_STRING("PHOTO_IMPORT_CANCEL_BUTTON",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_CANCEL_BUTTON);
-  SET_STRING("PHOTO_IMPORT_DRIVE_ERROR",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_DRIVE_ERROR);
-  SET_STRING("PHOTO_IMPORT_DESTINATION_ERROR",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_DESTINATION_ERROR);
-  SET_STRING("PHOTO_IMPORT_SOURCE_ERROR",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_SOURCE_ERROR);
-  SET_STRING("PHOTO_IMPORT_UNKNOWN_DATE",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_UNKNOWN_DATE);
-  SET_STRING("PHOTO_IMPORT_NEW_ALBUM_NAME",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_NEW_ALBUM_NAME);
-  SET_STRING("PHOTO_IMPORT_SELECT_ALBUM_CAPTION",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_SELECT_ALBUM_CAPTION);
-  SET_STRING("PHOTO_IMPORT_SELECT_ALBUM_CAPTION_PLURAL",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_SELECT_ALBUM_CAPTION_PLURAL);
-  SET_STRING("PHOTO_IMPORT_IMPORTING_ERROR",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_IMPORTING_ERROR);
-  SET_STRING("PHOTO_IMPORT_IMPORTING", IDS_FILE_BROWSER_PHOTO_IMPORT_IMPORTING);
-  SET_STRING("PHOTO_IMPORT_IMPORT_COMPLETE",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_IMPORT_COMPLETE);
-  SET_STRING("PHOTO_IMPORT_CAPTION", IDS_FILE_BROWSER_PHOTO_IMPORT_CAPTION);
-  SET_STRING("PHOTO_IMPORT_ONE_SELECTED",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_ONE_SELECTED);
-  SET_STRING("PHOTO_IMPORT_MANY_SELECTED",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_MANY_SELECTED);
-  SET_STRING("PHOTO_IMPORT_SELECT_ALL",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_SELECT_ALL);
-  SET_STRING("PHOTO_IMPORT_SELECT_NONE",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_SELECT_NONE);
-  SET_STRING("PHOTO_IMPORT_DELETE_AFTER",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_DELETE_AFTER);
-  SET_STRING("PHOTO_IMPORT_MY_PHOTOS_DIRECTORY_NAME",
-             IDS_FILE_BROWSER_PHOTO_IMPORT_MY_PHOTOS_DIRECTORY_NAME);
-
-  SET_STRING("CONFIRM_OVERWRITE_FILE", IDS_FILE_BROWSER_CONFIRM_OVERWRITE_FILE);
-  SET_STRING("FILE_ALREADY_EXISTS", IDS_FILE_BROWSER_FILE_ALREADY_EXISTS);
-  SET_STRING("DIRECTORY_ALREADY_EXISTS",
-             IDS_FILE_BROWSER_DIRECTORY_ALREADY_EXISTS);
-  SET_STRING("ERROR_RENAMING", IDS_FILE_BROWSER_ERROR_RENAMING);
-  SET_STRING("RENAME_PROMPT", IDS_FILE_BROWSER_RENAME_PROMPT);
-  SET_STRING("RENAME_BUTTON_LABEL", IDS_FILE_BROWSER_RENAME_BUTTON_LABEL);
-
-  SET_STRING("ERROR_DELETING", IDS_FILE_BROWSER_ERROR_DELETING);
-  SET_STRING("DELETE_BUTTON_LABEL", IDS_FILE_BROWSER_DELETE_BUTTON_LABEL);
-
-  SET_STRING("PASTE_BUTTON_LABEL", IDS_FILE_BROWSER_PASTE_BUTTON_LABEL);
-
-  SET_STRING("COPY_BUTTON_LABEL", IDS_FILE_BROWSER_COPY_BUTTON_LABEL);
-  SET_STRING("CUT_BUTTON_LABEL", IDS_FILE_BROWSER_CUT_BUTTON_LABEL);
-  SET_STRING("ZIP_SELECTION_BUTTON_LABEL",
-             IDS_FILE_BROWSER_ZIP_SELECTION_BUTTON_LABEL);
-  SET_STRING("CREATE_FOLDER_SHORTCUT_BUTTON_LABEL",
-             IDS_FILE_BROWSER_CREATE_FOLDER_SHORTCUT_BUTTON_LABEL);
-  SET_STRING("REMOVE_FOLDER_SHORTCUT_BUTTON_LABEL",
-             IDS_FILE_BROWSER_REMOVE_FOLDER_SHORTCUT_BUTTON_LABEL);
-  SET_STRING("SHARE_BUTTON_LABEL",
-             IDS_FILE_BROWSER_SHARE_BUTTON_LABEL);
-
-  SET_STRING("OPEN_WITH_BUTTON_LABEL", IDS_FILE_BROWSER_OPEN_WITH_BUTTON_LABEL);
-
-  SET_STRING("TRANSFER_ITEMS_REMAINING",
-             IDS_FILE_BROWSER_TRANSFER_ITEMS_REMAINING);
-  SET_STRING("TRANSFER_CANCELLED", IDS_FILE_BROWSER_TRANSFER_CANCELLED);
-  SET_STRING("TRANSFER_TARGET_EXISTS_ERROR",
-             IDS_FILE_BROWSER_TRANSFER_TARGET_EXISTS_ERROR);
-  SET_STRING("TRANSFER_FILESYSTEM_ERROR",
-             IDS_FILE_BROWSER_TRANSFER_FILESYSTEM_ERROR);
-  SET_STRING("TRANSFER_UNEXPECTED_ERROR",
-             IDS_FILE_BROWSER_TRANSFER_UNEXPECTED_ERROR);
-  SET_STRING("COPY_FILE_NAME", IDS_FILE_BROWSER_COPY_FILE_NAME);
-  SET_STRING("COPY_ITEMS_REMAINING", IDS_FILE_BROWSER_COPY_ITEMS_REMAINING);
-  SET_STRING("COPY_CANCELLED", IDS_FILE_BROWSER_COPY_CANCELLED);
-  SET_STRING("COPY_TARGET_EXISTS_ERROR",
-             IDS_FILE_BROWSER_COPY_TARGET_EXISTS_ERROR);
-  SET_STRING("COPY_FILESYSTEM_ERROR", IDS_FILE_BROWSER_COPY_FILESYSTEM_ERROR);
-  SET_STRING("COPY_UNEXPECTED_ERROR", IDS_FILE_BROWSER_COPY_UNEXPECTED_ERROR);
-  SET_STRING("MOVE_FILE_NAME", IDS_FILE_BROWSER_MOVE_FILE_NAME);
-  SET_STRING("MOVE_ITEMS_REMAINING", IDS_FILE_BROWSER_MOVE_ITEMS_REMAINING);
-  SET_STRING("MOVE_CANCELLED", IDS_FILE_BROWSER_MOVE_CANCELLED);
-  SET_STRING("MOVE_TARGET_EXISTS_ERROR",
-             IDS_FILE_BROWSER_MOVE_TARGET_EXISTS_ERROR);
-  SET_STRING("MOVE_FILESYSTEM_ERROR", IDS_FILE_BROWSER_MOVE_FILESYSTEM_ERROR);
-  SET_STRING("MOVE_UNEXPECTED_ERROR", IDS_FILE_BROWSER_MOVE_UNEXPECTED_ERROR);
-  SET_STRING("ZIP_FILE_NAME", IDS_FILE_BROWSER_ZIP_FILE_NAME);
-  SET_STRING("ZIP_ITEMS_REMAINING", IDS_FILE_BROWSER_ZIP_ITEMS_REMAINING);
-  SET_STRING("ZIP_CANCELLED", IDS_FILE_BROWSER_ZIP_CANCELLED);
-  SET_STRING("ZIP_TARGET_EXISTS_ERROR",
-             IDS_FILE_BROWSER_ZIP_TARGET_EXISTS_ERROR);
-  SET_STRING("ZIP_FILESYSTEM_ERROR", IDS_FILE_BROWSER_ZIP_FILESYSTEM_ERROR);
-  SET_STRING("ZIP_UNEXPECTED_ERROR", IDS_FILE_BROWSER_ZIP_UNEXPECTED_ERROR);
-  SET_STRING("SHARE_ERROR", IDS_FILE_BROWSER_SHARE_ERROR);
-
-  SET_STRING("DELETED_MESSAGE_PLURAL", IDS_FILE_BROWSER_DELETED_MESSAGE_PLURAL);
-  SET_STRING("DELETED_MESSAGE", IDS_FILE_BROWSER_DELETED_MESSAGE);
-  SET_STRING("DELETE_ERROR", IDS_FILE_BROWSER_DELETE_ERROR);
-  SET_STRING("UNDO_DELETE", IDS_FILE_BROWSER_UNDO_DELETE);
-
-  SET_STRING("CANCEL_LABEL", IDS_FILE_BROWSER_CANCEL_LABEL);
-  SET_STRING("OPEN_LABEL", IDS_FILE_BROWSER_OPEN_LABEL);
-  SET_STRING("SAVE_LABEL", IDS_FILE_BROWSER_SAVE_LABEL);
-  SET_STRING("OK_LABEL", IDS_FILE_BROWSER_OK_LABEL);
-  SET_STRING("VIEW_LABEL", IDS_FILE_BROWSER_VIEW_LABEL);
-
-  SET_STRING("DEFAULT_NEW_FOLDER_NAME",
-             IDS_FILE_BROWSER_DEFAULT_NEW_FOLDER_NAME);
-  SET_STRING("MORE_FILES", IDS_FILE_BROWSER_MORE_FILES);
-
-  SET_STRING("CONFIRM_DELETE_ONE", IDS_FILE_BROWSER_CONFIRM_DELETE_ONE);
-  SET_STRING("CONFIRM_DELETE_SOME", IDS_FILE_BROWSER_CONFIRM_DELETE_SOME);
-
-  SET_STRING("UNKNOWN_FILESYSTEM_WARNING",
-             IDS_FILE_BROWSER_UNKNOWN_FILESYSTEM_WARNING);
-  SET_STRING("UNSUPPORTED_FILESYSTEM_WARNING",
-             IDS_FILE_BROWSER_UNSUPPORTED_FILESYSTEM_WARNING);
-  SET_STRING("FORMATTING_WARNING", IDS_FILE_BROWSER_FORMATTING_WARNING);
-
-  SET_STRING("DRIVE_MENU_HELP", IDS_FILE_BROWSER_DRIVE_MENU_HELP);
-  SET_STRING("DRIVE_SHOW_HOSTED_FILES_OPTION",
-             IDS_FILE_BROWSER_DRIVE_SHOW_HOSTED_FILES_OPTION);
-  SET_STRING("DRIVE_MOBILE_CONNECTION_OPTION",
-             IDS_FILE_BROWSER_DRIVE_MOBILE_CONNECTION_OPTION);
-  SET_STRING("DRIVE_CLEAR_LOCAL_CACHE",
-             IDS_FILE_BROWSER_DRIVE_CLEAR_LOCAL_CACHE);
-  SET_STRING("DRIVE_SPACE_AVAILABLE_LONG",
-             IDS_FILE_BROWSER_DRIVE_SPACE_AVAILABLE_LONG);
-  SET_STRING("DRIVE_BUY_MORE_SPACE", IDS_FILE_BROWSER_DRIVE_BUY_MORE_SPACE);
-  SET_STRING("DRIVE_BUY_MORE_SPACE_LINK",
-             IDS_FILE_BROWSER_DRIVE_BUY_MORE_SPACE_LINK);
-  SET_STRING("DRIVE_VISIT_DRIVE_GOOGLE_COM",
-             IDS_FILE_BROWSER_DRIVE_VISIT_DRIVE_GOOGLE_COM);
-
-  SET_STRING("SELECT_FOLDER_TITLE", IDS_FILE_BROWSER_SELECT_FOLDER_TITLE);
-  SET_STRING("SELECT_OPEN_FILE_TITLE", IDS_FILE_BROWSER_SELECT_OPEN_FILE_TITLE);
-  SET_STRING("SELECT_OPEN_MULTI_FILE_TITLE",
-             IDS_FILE_BROWSER_SELECT_OPEN_MULTI_FILE_TITLE);
-  SET_STRING("SELECT_SAVEAS_FILE_TITLE",
-             IDS_FILE_BROWSER_SELECT_SAVEAS_FILE_TITLE);
-
-  SET_STRING("MANY_FILES_SELECTED", IDS_FILE_BROWSER_MANY_FILES_SELECTED);
-  SET_STRING("MANY_DIRECTORIES_SELECTED",
-             IDS_FILE_BROWSER_MANY_DIRECTORIES_SELECTED);
-  SET_STRING("MANY_ENTRIES_SELECTED", IDS_FILE_BROWSER_MANY_ENTRIES_SELECTED);
-  SET_STRING("CALCULATING_SIZE", IDS_FILE_BROWSER_CALCULATING_SIZE);
-
-  SET_STRING("OFFLINE_HEADER", IDS_FILE_BROWSER_OFFLINE_HEADER);
-  SET_STRING("OFFLINE_MESSAGE", IDS_FILE_BROWSER_OFFLINE_MESSAGE);
-  SET_STRING("OFFLINE_MESSAGE_PLURAL", IDS_FILE_BROWSER_OFFLINE_MESSAGE_PLURAL);
-  SET_STRING("HOSTED_OFFLINE_MESSAGE", IDS_FILE_BROWSER_HOSTED_OFFLINE_MESSAGE);
-  SET_STRING("HOSTED_OFFLINE_MESSAGE_PLURAL",
-             IDS_FILE_BROWSER_HOSTED_OFFLINE_MESSAGE_PLURAL);
-  SET_STRING("CONFIRM_MOBILE_DATA_USE",
-             IDS_FILE_BROWSER_CONFIRM_MOBILE_DATA_USE);
-  SET_STRING("CONFIRM_MOBILE_DATA_USE_PLURAL",
-             IDS_FILE_BROWSER_CONFIRM_MOBILE_DATA_USE_PLURAL);
-  SET_STRING("DRIVE_OUT_OF_SPACE_HEADER",
-             IDS_FILE_BROWSER_DRIVE_OUT_OF_SPACE_HEADER);
-  SET_STRING("DRIVE_OUT_OF_SPACE_MESSAGE",
-             IDS_FILE_BROWSER_DRIVE_OUT_OF_SPACE_MESSAGE);
-  SET_STRING("DRIVE_SERVER_OUT_OF_SPACE_HEADER",
-             IDS_FILE_BROWSER_DRIVE_SERVER_OUT_OF_SPACE_HEADER);
-  SET_STRING("DRIVE_SERVER_OUT_OF_SPACE_MESSAGE",
-             IDS_FILE_BROWSER_DRIVE_SERVER_OUT_OF_SPACE_MESSAGE);
-  SET_STRING("DRIVE_WELCOME_TITLE", IDS_FILE_BROWSER_DRIVE_WELCOME_TITLE);
-  SET_STRING("DRIVE_WELCOME_TEXT_SHORT",
-             IDS_FILE_BROWSER_DRIVE_WELCOME_TEXT_SHORT);
-  SET_STRING("DRIVE_WELCOME_TEXT_LONG",
-             IDS_FILE_BROWSER_DRIVE_WELCOME_TEXT_LONG);
-  SET_STRING("DRIVE_WELCOME_DISMISS", IDS_FILE_BROWSER_DRIVE_WELCOME_DISMISS);
-  SET_STRING("DRIVE_WELCOME_TITLE_ALTERNATIVE",
-             IDS_FILE_BROWSER_DRIVE_WELCOME_TITLE_ALTERNATIVE);
-  SET_STRING("DRIVE_WELCOME_TITLE_ALTERNATIVE_1TB",
-             IDS_FILE_BROWSER_DRIVE_WELCOME_TITLE_ALTERNATIVE_1TB);
-  SET_STRING("DRIVE_WELCOME_CHECK_ELIGIBILITY",
-             IDS_FILE_BROWSER_DRIVE_WELCOME_CHECK_ELIGIBILITY);
-  SET_STRING("NO_ACTION_FOR_FILE", IDS_FILE_BROWSER_NO_ACTION_FOR_FILE);
-  SET_STRING("NO_ACTION_FOR_EXECUTABLE",
-             IDS_FILE_BROWSER_NO_ACTION_FOR_EXECUTABLE);
-
-  // MP3 metadata extractor plugin
-  SET_STRING("ID3_ALBUM", IDS_FILE_BROWSER_ID3_ALBUM);                // TALB
-  SET_STRING("ID3_BPM", IDS_FILE_BROWSER_ID3_BPM);                    // TBPM
-  SET_STRING("ID3_COMPOSER", IDS_FILE_BROWSER_ID3_COMPOSER);          // TCOM
-  SET_STRING("ID3_COPYRIGHT_MESSAGE",
-             IDS_FILE_BROWSER_ID3_COPYRIGHT_MESSAGE);                 // TCOP
-  SET_STRING("ID3_DATE", IDS_FILE_BROWSER_ID3_DATE);                  // TDAT
-  SET_STRING("ID3_PLAYLIST_DELAY",
-             IDS_FILE_BROWSER_ID3_PLAYLIST_DELAY);                    // TDLY
-  SET_STRING("ID3_ENCODED_BY", IDS_FILE_BROWSER_ID3_ENCODED_BY);      // TENC
-  SET_STRING("ID3_LYRICIST", IDS_FILE_BROWSER_ID3_LYRICIST);          // TEXT
-  SET_STRING("ID3_FILE_TYPE", IDS_FILE_BROWSER_ID3_FILE_TYPE);        // TFLT
-  SET_STRING("ID3_TIME", IDS_FILE_BROWSER_ID3_TIME);                  // TIME
-  SET_STRING("ID3_TITLE", IDS_FILE_BROWSER_ID3_TITLE);                // TIT2
-  SET_STRING("ID3_LENGTH", IDS_FILE_BROWSER_ID3_LENGTH);              // TLEN
-  SET_STRING("ID3_FILE_OWNER", IDS_FILE_BROWSER_ID3_FILE_OWNER);      // TOWN
-  SET_STRING("ID3_LEAD_PERFORMER",
-             IDS_FILE_BROWSER_ID3_LEAD_PERFORMER);                    // TPE1
-  SET_STRING("ID3_BAND", IDS_FILE_BROWSER_ID3_BAND);                  // TPE2
-  SET_STRING("ID3_TRACK_NUMBER", IDS_FILE_BROWSER_ID3_TRACK_NUMBER);  // TRCK
-  SET_STRING("ID3_YEAR", IDS_FILE_BROWSER_ID3_YEAR);                  // TYER
-  SET_STRING("ID3_COPYRIGHT", IDS_FILE_BROWSER_ID3_COPYRIGHT);        // WCOP
-  SET_STRING("ID3_OFFICIAL_AUDIO_FILE_WEBPAGE",
-             IDS_FILE_BROWSER_ID3_OFFICIAL_AUDIO_FILE_WEBPAGE);       // WOAF
-  SET_STRING("ID3_OFFICIAL_ARTIST",
-             IDS_FILE_BROWSER_ID3_OFFICIAL_ARTIST);                   // WOAR
-  SET_STRING("ID3_OFFICIAL_AUDIO_SOURCE_WEBPAGE",
-             IDS_FILE_BROWSER_ID3_OFFICIAL_AUDIO_SOURCE_WEBPAGE);     // WOAS
-  SET_STRING("ID3_PUBLISHERS_OFFICIAL_WEBPAGE",
-             IDS_FILE_BROWSER_ID3_PUBLISHERS_OFFICIAL_WEBPAGE);       // WPUB
-  SET_STRING("ID3_USER_DEFINED_URL_LINK_FRAME",
-             IDS_FILE_BROWSER_ID3_USER_DEFINED_URL_LINK_FRAME);       // WXXX
-
-  // File types
-  SET_STRING("FOLDER", IDS_FILE_BROWSER_FOLDER);
-  SET_STRING("GENERIC_FILE_TYPE", IDS_FILE_BROWSER_GENERIC_FILE_TYPE);
-  SET_STRING("NO_EXTENSION_FILE_TYPE", IDS_FILE_BROWSER_NO_EXTENSION_FILE_TYPE);
-  SET_STRING("DEVICE", IDS_FILE_BROWSER_DEVICE);
-  SET_STRING("IMAGE_FILE_TYPE", IDS_FILE_BROWSER_IMAGE_FILE_TYPE);
-  SET_STRING("VIDEO_FILE_TYPE", IDS_FILE_BROWSER_VIDEO_FILE_TYPE);
-  SET_STRING("AUDIO_FILE_TYPE", IDS_FILE_BROWSER_AUDIO_FILE_TYPE);
-  SET_STRING("HTML_DOCUMENT_FILE_TYPE",
-             IDS_FILE_BROWSER_HTML_DOCUMENT_FILE_TYPE);
-  SET_STRING("ZIP_ARCHIVE_FILE_TYPE", IDS_FILE_BROWSER_ZIP_ARCHIVE_FILE_TYPE);
-  SET_STRING("RAR_ARCHIVE_FILE_TYPE", IDS_FILE_BROWSER_RAR_ARCHIVE_FILE_TYPE);
-  SET_STRING("TAR_ARCHIVE_FILE_TYPE", IDS_FILE_BROWSER_TAR_ARCHIVE_FILE_TYPE);
-  SET_STRING("TAR_BZIP2_ARCHIVE_FILE_TYPE",
-             IDS_FILE_BROWSER_TAR_BZIP2_ARCHIVE_FILE_TYPE);
-  SET_STRING("TAR_GZIP_ARCHIVE_FILE_TYPE",
-             IDS_FILE_BROWSER_TAR_GZIP_ARCHIVE_FILE_TYPE);
-  SET_STRING("PLAIN_TEXT_FILE_TYPE", IDS_FILE_BROWSER_PLAIN_TEXT_FILE_TYPE);
-  SET_STRING("PDF_DOCUMENT_FILE_TYPE", IDS_FILE_BROWSER_PDF_DOCUMENT_FILE_TYPE);
-  SET_STRING("WORD_DOCUMENT_FILE_TYPE",
-             IDS_FILE_BROWSER_WORD_DOCUMENT_FILE_TYPE);
-  SET_STRING("POWERPOINT_PRESENTATION_FILE_TYPE",
-             IDS_FILE_BROWSER_POWERPOINT_PRESENTATION_FILE_TYPE);
-  SET_STRING("EXCEL_FILE_TYPE", IDS_FILE_BROWSER_EXCEL_FILE_TYPE);
-
-  SET_STRING("GDOC_DOCUMENT_FILE_TYPE",
-             IDS_FILE_BROWSER_GDOC_DOCUMENT_FILE_TYPE);
-  SET_STRING("GSHEET_DOCUMENT_FILE_TYPE",
-             IDS_FILE_BROWSER_GSHEET_DOCUMENT_FILE_TYPE);
-  SET_STRING("GSLIDES_DOCUMENT_FILE_TYPE",
-             IDS_FILE_BROWSER_GSLIDES_DOCUMENT_FILE_TYPE);
-  SET_STRING("GDRAW_DOCUMENT_FILE_TYPE",
-             IDS_FILE_BROWSER_GDRAW_DOCUMENT_FILE_TYPE);
-  SET_STRING("GTABLE_DOCUMENT_FILE_TYPE",
-             IDS_FILE_BROWSER_GTABLE_DOCUMENT_FILE_TYPE);
-  SET_STRING("GLINK_DOCUMENT_FILE_TYPE",
-             IDS_FILE_BROWSER_GLINK_DOCUMENT_FILE_TYPE);
-
-  SET_STRING("DRIVE_LOADING", IDS_FILE_BROWSER_DRIVE_LOADING);
-  SET_STRING("DRIVE_CANNOT_REACH", IDS_FILE_BROWSER_DRIVE_CANNOT_REACH);
-  SET_STRING("DRIVE_LEARN_MORE", IDS_FILE_BROWSER_DRIVE_LEARN_MORE);
-  SET_STRING("DRIVE_RETRY", IDS_FILE_BROWSER_DRIVE_RETRY);
-
-  SET_STRING("AUDIO_PLAYER_TITLE", IDS_FILE_BROWSER_AUDIO_PLAYER_TITLE);
-  SET_STRING("AUDIO_PLAYER_DEFAULT_ARTIST",
-             IDS_FILE_BROWSER_AUDIO_PLAYER_DEFAULT_ARTIST);
-
-  SET_STRING("FILE_ERROR_GENERIC", IDS_FILE_BROWSER_FILE_ERROR_GENERIC);
-  SET_STRING("FILE_ERROR_NOT_FOUND", IDS_FILE_BROWSER_FILE_ERROR_NOT_FOUND);
-  SET_STRING("FILE_ERROR_SECURITY", IDS_FILE_BROWSER_FILE_ERROR_SECURITY);
-  SET_STRING("FILE_ERROR_NOT_READABLE",
-             IDS_FILE_BROWSER_FILE_ERROR_NOT_READABLE);
-  SET_STRING("FILE_ERROR_NO_MODIFICATION_ALLOWED",
-             IDS_FILE_BROWSER_FILE_ERROR_NO_MODIFICATION_ALLOWED);
-  SET_STRING("FILE_ERROR_INVALID_STATE",
-             IDS_FILE_BROWSER_FILE_ERROR_INVALID_STATE);
-  SET_STRING("FILE_ERROR_INVALID_MODIFICATION",
-             IDS_FILE_BROWSER_FILE_ERROR_INVALID_MODIFICATION);
-  SET_STRING("FILE_ERROR_PATH_EXISTS", IDS_FILE_BROWSER_FILE_ERROR_PATH_EXISTS);
-  SET_STRING("FILE_ERROR_QUOTA_EXCEEDED",
-             IDS_FILE_BROWSER_FILE_ERROR_QUOTA_EXCEEDED);
-
-  SET_STRING("SEARCH_DRIVE_HTML", IDS_FILE_BROWSER_SEARCH_DRIVE_HTML);
-  SET_STRING("SEARCH_NO_MATCHING_FILES_HTML",
-             IDS_FILE_BROWSER_SEARCH_NO_MATCHING_FILES_HTML);
-  SET_STRING("SEARCH_EXPAND", IDS_FILE_BROWSER_SEARCH_EXPAND);
-  SET_STRING("SEARCH_SPINNER", IDS_FILE_BROWSER_SEARCH_SPINNER);
-
-  SET_STRING("CHANGE_DEFAULT_MENU_ITEM",
-             IDS_FILE_BROWSER_CHANGE_DEFAULT_MENU_ITEM);
-  SET_STRING("CHANGE_DEFAULT_CAPTION", IDS_FILE_BROWSER_CHANGE_DEFAULT_CAPTION);
-  SET_STRING("DEFAULT_ACTION_LABEL", IDS_FILE_BROWSER_DEFAULT_ACTION_LABEL);
-
-  SET_STRING("DETAIL_VIEW_TOOLTIP", IDS_FILE_BROWSER_DETAIL_VIEW_TOOLTIP);
-  SET_STRING("THUMBNAIL_VIEW_TOOLTIP", IDS_FILE_BROWSER_THUMBNAIL_VIEW_TOOLTIP);
-
-  SET_STRING("TIME_TODAY", IDS_FILE_BROWSER_TIME_TODAY);
-  SET_STRING("TIME_YESTERDAY", IDS_FILE_BROWSER_TIME_YESTERDAY);
-
-  SET_STRING("ALL_FILES_FILTER", IDS_FILE_BROWSER_ALL_FILES_FILTER);
-
-  SET_STRING("SPACE_AVAILABLE", IDS_FILE_BROWSER_SPACE_AVAILABLE);
-  SET_STRING("WAITING_FOR_SPACE_INFO", IDS_FILE_BROWSER_WAITING_FOR_SPACE_INFO);
-  SET_STRING("FAILED_SPACE_INFO", IDS_FILE_BROWSER_FAILED_SPACE_INFO);
-
-  SET_STRING("DRIVE_NOT_REACHED", IDS_FILE_BROWSER_DRIVE_NOT_REACHED);
-
-  SET_STRING("HELP_LINK_LABEL", IDS_FILE_BROWSER_HELP_LINK_LABEL);
-#undef SET_STRING
-
-  dict->SetBoolean("PDF_VIEW_ENABLED",
-      file_manager_util::ShouldBeOpenedWithPlugin(profile(), ".pdf"));
-  dict->SetBoolean("SWF_VIEW_ENABLED",
-      file_manager_util::ShouldBeOpenedWithPlugin(profile(), ".swf"));
-
-  webui::SetFontAndTextDirection(dict);
-
-  std::string board;
-  chromeos::system::StatisticsProvider* provider =
-      chromeos::system::StatisticsProvider::GetInstance();
-  if (!provider->GetMachineStatistic(chromeos::system::kMachineInfoBoard,
-                                     &board)) {
-    board = "unknown";
-  }
-  dict->SetString(chromeos::system::kMachineInfoBoard, board);
-  return true;
-}
-
-GetDriveEntryPropertiesFunction::GetDriveEntryPropertiesFunction() {
-}
-
-GetDriveEntryPropertiesFunction::~GetDriveEntryPropertiesFunction() {
-}
-
-bool GetDriveEntryPropertiesFunction::RunImpl() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  std::string file_url_str;
-  if (args_->GetSize() != 1 || !args_->GetString(0, &file_url_str))
-    return false;
-
-  GURL file_url = GURL(file_url_str);
-  file_path_ = drive::util::ExtractDrivePath(
-      GetLocalPathFromURL(render_view_host(), profile(), file_url));
-
-  properties_.reset(new base::DictionaryValue);
-  properties_->SetString("fileUrl", file_url.spec());
-
-  // Start getting the file info.
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled.
-  if (!integration_service) {
-    CompleteGetFileProperties(drive::FILE_ERROR_FAILED);
-    return true;
-  }
-
-  integration_service->file_system()->GetResourceEntryByPath(
-      file_path_,
-      base::Bind(&GetDriveEntryPropertiesFunction::OnGetFileInfo, this));
-  return true;
-}
-
-void GetDriveEntryPropertiesFunction::OnGetFileInfo(
-    drive::FileError error,
-    scoped_ptr<drive::ResourceEntry> entry) {
-  DCHECK(properties_);
-
-  if (error != drive::FILE_ERROR_OK) {
-    CompleteGetFileProperties(error);
-    return;
-  }
-  DCHECK(entry);
-
-  FillDriveEntryPropertiesValue(*entry, properties_.get());
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled.
-  if (!integration_service) {
-    CompleteGetFileProperties(drive::FILE_ERROR_FAILED);
-    return;
-  }
-
-  // The properties meaningful for directories are already filled in
-  // FillDriveEntryPropertiesValue().
-  if (entry.get() && !entry->has_file_specific_info()) {
-    CompleteGetFileProperties(error);
-    return;
-  }
-
-  const drive::FileSpecificInfo& file_specific_info =
-      entry->file_specific_info();
-
-  // Get drive WebApps that can accept this file.
-  ScopedVector<drive::DriveAppInfo> drive_apps;
-  integration_service->drive_app_registry()->GetAppsForFile(
-      file_path_, file_specific_info.content_mime_type(), &drive_apps);
-  if (!drive_apps.empty()) {
-    std::string default_task_id = file_handler_util::GetDefaultTaskIdFromPrefs(
-        profile_,
-        file_specific_info.content_mime_type(),
-        file_path_.Extension());
-    std::string default_app_id;
-    file_handler_util::CrackTaskID(
-        default_task_id, &default_app_id, NULL, NULL);
-
-    ListValue* apps = new ListValue();
-    properties_->Set("driveApps", apps);
-    for (ScopedVector<drive::DriveAppInfo>::const_iterator it =
-             drive_apps.begin();
-         it != drive_apps.end(); ++it) {
-      const drive::DriveAppInfo* app_info = *it;
-      DictionaryValue* app = new DictionaryValue();
-      app->SetString("appId", app_info->app_id);
-      app->SetString("appName", app_info->app_name);
-      GURL app_icon = FindPreferredIcon(app_info->app_icons,
-                                        kPreferredIconSize);
-      if (!app_icon.is_empty())
-        app->SetString("appIcon", app_icon.spec());
-      GURL doc_icon = FindPreferredIcon(app_info->document_icons,
-                                        kPreferredIconSize);
-      if (!doc_icon.is_empty())
-        app->SetString("docIcon", doc_icon.spec());
-      app->SetString("objectType", app_info->object_type);
-      app->SetBoolean("isPrimary", default_app_id == app_info->app_id);
-      apps->Append(app);
-    }
-  }
-
-  integration_service->file_system()->GetCacheEntryByResourceId(
-      entry->resource_id(),
-      base::Bind(&GetDriveEntryPropertiesFunction::CacheStateReceived, this));
-}
-
-void GetDriveEntryPropertiesFunction::CacheStateReceived(
-    bool /* success */,
-    const drive::FileCacheEntry& cache_entry) {
-  // In case of an error (i.e. success is false), cache_entry.is_*() all
-  // returns false.
-  properties_->SetBoolean("isPinned", cache_entry.is_pinned());
-  properties_->SetBoolean("isPresent", cache_entry.is_present());
-  properties_->SetBoolean("isDirty", cache_entry.is_dirty());
-
-  CompleteGetFileProperties(drive::FILE_ERROR_OK);
-}
-
-void GetDriveEntryPropertiesFunction::CompleteGetFileProperties(
-    drive::FileError error) {
-  if (error != drive::FILE_ERROR_OK)
-    properties_->SetInteger("errorCode", error);
-  SetResult(properties_.release());
-  SendResponse(true);
-}
-
-PinDriveFileFunction::PinDriveFileFunction() {
-}
-
-PinDriveFileFunction::~PinDriveFileFunction() {
-}
-
-bool PinDriveFileFunction::RunImpl() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  std::string url;
-  bool set_pin = false;
-  if (args_->GetSize() != 2 ||
-      !args_->GetString(0, &url) ||
-      !args_->GetBoolean(1, &set_pin))
-    return false;
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  drive::FileSystemInterface* file_system =
-      integration_service ? integration_service->file_system() : NULL;
-  if (!file_system)  // |file_system| is NULL if Drive is disabled.
-    return false;
-
-  base::FilePath drive_path =
-      drive::util::ExtractDrivePath(
-          GetLocalPathFromURL(render_view_host(), profile(), GURL(url)));
-  if (set_pin) {
-    file_system->Pin(drive_path,
-                     base::Bind(&PinDriveFileFunction::OnPinStateSet, this));
-  } else {
-    file_system->Unpin(drive_path,
-                       base::Bind(&PinDriveFileFunction::OnPinStateSet, this));
-  }
-  return true;
-}
-
-void PinDriveFileFunction::OnPinStateSet(drive::FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (error == drive::FILE_ERROR_OK) {
-    SendResponse(true);
-  } else {
-    error_ = drive::FileErrorToString(error);
-    SendResponse(false);
-  }
-}
-
-GetDriveFilesFunction::GetDriveFilesFunction()
-    : local_paths_(NULL) {
-}
-
-GetDriveFilesFunction::~GetDriveFilesFunction() {
-}
-
-bool GetDriveFilesFunction::RunImpl() {
-  ListValue* file_urls_as_strings = NULL;
-  if (!args_->GetList(0, &file_urls_as_strings))
-    return false;
-
-  // Convert the list of strings to a list of GURLs.
-  for (size_t i = 0; i < file_urls_as_strings->GetSize(); ++i) {
-    std::string file_url_as_string;
-    if (!file_urls_as_strings->GetString(i, &file_url_as_string))
-      return false;
-    const base::FilePath path = GetLocalPathFromURL(
-        render_view_host(), profile(), GURL(file_url_as_string));
-    DCHECK(drive::util::IsUnderDriveMountPoint(path));
-    base::FilePath drive_path = drive::util::ExtractDrivePath(path);
-    remaining_drive_paths_.push(drive_path);
-  }
-
-  local_paths_ = new ListValue;
-  GetFileOrSendResponse();
-  return true;
-}
-
-void GetDriveFilesFunction::GetFileOrSendResponse() {
-  // Send the response if all files are obtained.
-  if (remaining_drive_paths_.empty()) {
-    SetResult(local_paths_);
-    SendResponse(true);
-    return;
-  }
-
-  // Get the file on the top of the queue.
-  base::FilePath drive_path = remaining_drive_paths_.front();
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled.
-  if (!integration_service) {
-    OnFileReady(drive::FILE_ERROR_FAILED, drive_path,
-                scoped_ptr<drive::ResourceEntry>());
-    return;
-  }
-
-  integration_service->file_system()->GetFileByPath(
-      drive_path,
-      base::Bind(&GetDriveFilesFunction::OnFileReady, this));
-}
-
-
-void GetDriveFilesFunction::OnFileReady(
-    drive::FileError error,
-    const base::FilePath& local_path,
-    scoped_ptr<drive::ResourceEntry> entry) {
-  base::FilePath drive_path = remaining_drive_paths_.front();
-
-  if (error == drive::FILE_ERROR_OK) {
-    local_paths_->Append(new base::StringValue(local_path.value()));
-    DVLOG(1) << "Got " << drive_path.value() << " as " << local_path.value();
-
-    // TODO(benchan): If the file is a hosted document, a temporary JSON file
-    // is created to represent the document. The JSON file is not cached and
-    // should be deleted after use. We need to somehow communicate with
-    // file_manager.js to manage the lifetime of the temporary file.
-    // See crosbug.com/28058.
-  } else {
-    local_paths_->Append(new base::StringValue(""));
-    DVLOG(1) << "Failed to get " << drive_path.value()
-             << " with error code: " << error;
-  }
-
-  remaining_drive_paths_.pop();
-
-  // Start getting the next file.
-  GetFileOrSendResponse();
-}
-
-CancelFileTransfersFunction::CancelFileTransfersFunction() {
-}
-
-CancelFileTransfersFunction::~CancelFileTransfersFunction() {
-}
-
-bool CancelFileTransfersFunction::RunImpl() {
-  ListValue* url_list = NULL;
-  if (!args_->GetList(0, &url_list))
-    return false;
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled.
-  if (!integration_service)
-    return false;
-
-  // Create the mapping from file path to job ID.
-  drive::JobListInterface* job_list = integration_service->job_list();
-  DCHECK(job_list);
-  std::vector<drive::JobInfo> jobs = job_list->GetJobInfoList();
-
-  typedef std::map<base::FilePath, std::vector<drive::JobID> > PathToIdMap;
-  PathToIdMap path_to_id_map;
-  for (size_t i = 0; i < jobs.size(); ++i) {
-    if (drive::IsActiveFileTransferJobInfo(jobs[i]))
-      path_to_id_map[jobs[i].file_path].push_back(jobs[i].job_id);
-  }
-
-  // Cancel by Job ID.
-  scoped_ptr<ListValue> responses(new ListValue());
-  for (size_t i = 0; i < url_list->GetSize(); ++i) {
-    std::string url_as_string;
-    url_list->GetString(i, &url_as_string);
-
-    base::FilePath file_path = GetLocalPathFromURL(
-        render_view_host(), profile(), GURL(url_as_string));
-    if (file_path.empty())
-      continue;
-
-    DCHECK(drive::util::IsUnderDriveMountPoint(file_path));
-    file_path = drive::util::ExtractDrivePath(file_path);
-    scoped_ptr<DictionaryValue> result(new DictionaryValue());
-
-    // Cancel all the jobs for the file.
-    PathToIdMap::iterator it = path_to_id_map.find(file_path);
-    if (it != path_to_id_map.end()) {
-      for (size_t i = 0; i < it->second.size(); ++i)
-        job_list->CancelJob(it->second[i]);
-    }
-    result->SetBoolean("canceled", it != path_to_id_map.end());
-    // TODO(kinaba): simplify cancelFileTransfer() to take single URL each time,
-    // and eliminate this field; it is just returning a copy of the argument.
-    result->SetString("fileUrl", url_as_string);
-    responses->Append(result.release());
-  }
-  SetResult(responses.release());
-  SendResponse(true);
-  return true;
-}
-
-TransferFileFunction::TransferFileFunction() {
-}
-
-TransferFileFunction::~TransferFileFunction() {
-}
-
-bool TransferFileFunction::RunImpl() {
-  std::string source_file_url;
-  std::string destination_file_url;
-  if (!args_->GetString(0, &source_file_url) ||
-      !args_->GetString(1, &destination_file_url)) {
-    return false;
-  }
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled.
-  if (!integration_service)
-    return false;
-
-  base::FilePath source_file = GetLocalPathFromURL(
-      render_view_host(), profile(), GURL(source_file_url));
-  base::FilePath destination_file = GetLocalPathFromURL(
-      render_view_host(), profile(), GURL(destination_file_url));
-  if (source_file.empty() || destination_file.empty())
-    return false;
-
-  bool source_file_under_drive =
-      drive::util::IsUnderDriveMountPoint(source_file);
-  bool destination_file_under_drive =
-      drive::util::IsUnderDriveMountPoint(destination_file);
-
-  if (source_file_under_drive && !destination_file_under_drive) {
-    // Transfer a file from drive to local file system.
-    source_file = drive::util::ExtractDrivePath(source_file);
-    integration_service->file_system()->TransferFileFromRemoteToLocal(
-        source_file,
-        destination_file,
-        base::Bind(&TransferFileFunction::OnTransferCompleted, this));
-  } else if (!source_file_under_drive && destination_file_under_drive) {
-    // Transfer a file from local to Drive file system
-    destination_file = drive::util::ExtractDrivePath(destination_file);
-    integration_service->file_system()->TransferFileFromLocalToRemote(
-        source_file,
-        destination_file,
-        base::Bind(&TransferFileFunction::OnTransferCompleted, this));
-  } else {
-    // Local-to-local or Drive-to-Drive file transfers should be done via
-    // FileEntry.copyTo in the File API and are thus not supported here.
-    NOTREACHED();
-    SendResponse(false);
-  }
-  return true;
-}
-
-void TransferFileFunction::OnTransferCompleted(drive::FileError error) {
-  if (error == drive::FILE_ERROR_OK) {
-    SendResponse(true);
-  } else {
-    error_ = base::StringPrintf("%d", static_cast<int>(
-        fileapi::PlatformFileErrorToWebFileError(
-            drive::FileErrorToPlatformError(error))));
-    SendResponse(false);
-  }
-}
-
-GetPreferencesFunction::GetPreferencesFunction() {
-}
-
-GetPreferencesFunction::~GetPreferencesFunction() {
-}
-
-bool GetPreferencesFunction::RunImpl() {
-  scoped_ptr<DictionaryValue> value(new DictionaryValue());
-
-  const PrefService* service = profile_->GetPrefs();
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  bool drive_enabled = (integration_service != NULL);
-
-  if (drive_enabled)
-    SetDriveMountPointPermissions(profile_, extension_id(), render_view_host());
-
-  value->SetBoolean("driveEnabled", drive_enabled);
-
-  value->SetBoolean("cellularDisabled",
-                    service->GetBoolean(prefs::kDisableDriveOverCellular));
-
-  value->SetBoolean("hostedFilesDisabled",
-                    service->GetBoolean(prefs::kDisableDriveHostedFiles));
-
-  value->SetBoolean("use24hourClock",
-                    service->GetBoolean(prefs::kUse24HourClock));
-
-  {
-    bool allow = true;
-    if (!chromeos::CrosSettings::Get()->GetBoolean(
-            chromeos::kAllowRedeemChromeOsRegistrationOffers, &allow)) {
-      allow = true;
-    }
-    value->SetBoolean("allowRedeemOffers", allow);
-  }
-
-  SetResult(value.release());
-
-  drive::util::Log("%s succeeded.", name().c_str());
-  return true;
-}
-
-SetPreferencesFunction::SetPreferencesFunction() {
-}
-
-SetPreferencesFunction::~SetPreferencesFunction() {
-}
-
-bool SetPreferencesFunction::RunImpl() {
-  base::DictionaryValue* value = NULL;
-
-  if (!args_->GetDictionary(0, &value) || !value)
-    return false;
-
-  PrefService* service = profile_->GetPrefs();
-
-  bool tmp;
-
-  if (value->GetBoolean("cellularDisabled", &tmp))
-    service->SetBoolean(prefs::kDisableDriveOverCellular, tmp);
-
-  if (value->GetBoolean("hostedFilesDisabled", &tmp))
-    service->SetBoolean(prefs::kDisableDriveHostedFiles, tmp);
-
-  drive::util::Log("%s succeeded.", name().c_str());
-  return true;
-}
-
-SearchDriveFunction::SearchDriveFunction() {
-}
-
-SearchDriveFunction::~SearchDriveFunction() {
-}
-
-bool SearchDriveFunction::RunImpl() {
-  DictionaryValue* search_params;
-  if (!args_->GetDictionary(0, &search_params))
-    return false;
-
-  std::string query;
-  if (!search_params->GetString("query", &query))
-    return false;
-
-  std::string next_feed;
-  if (!search_params->GetString("nextFeed", &next_feed))
-    return false;
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled.
-  if (!integration_service || !integration_service->file_system())
-    return false;
-
-  integration_service->file_system()->Search(
-      query, GURL(next_feed),
-      base::Bind(&SearchDriveFunction::OnSearch, this));
-  return true;
-}
-
-void SearchDriveFunction::OnSearch(
-    drive::FileError error,
-    const GURL& next_feed,
-    scoped_ptr<std::vector<drive::SearchResultInfo> > results) {
-  if (error != drive::FILE_ERROR_OK) {
-    SendResponse(false);
-    return;
-  }
-
-  DCHECK(results.get());
-
-  base::ListValue* entries = new ListValue();
-
-  // Convert Drive files to something File API stack can understand.
-  GURL origin_url = source_url_.GetOrigin();
-  fileapi::FileSystemType file_system_type = fileapi::kFileSystemTypeExternal;
-  GURL file_system_root_url =
-      fileapi::GetFileSystemRootURI(origin_url, file_system_type);
-  std::string file_system_name =
-      fileapi::GetFileSystemName(origin_url, file_system_type);
-  for (size_t i = 0; i < results->size(); ++i) {
-    DictionaryValue* entry = new DictionaryValue();
-    entry->SetString("fileSystemName", file_system_name);
-    entry->SetString("fileSystemRoot", file_system_root_url.spec());
-    entry->SetString("fileFullPath", "/" + results->at(i).path.value());
-    entry->SetBoolean("fileIsDirectory",
-                      results->at(i).entry.file_info().is_directory());
-    entries->Append(entry);
-  }
-
-  base::DictionaryValue* result = new DictionaryValue();
-  result->Set("entries", entries);
-  result->SetString("nextFeed", next_feed.spec());
-
-  SetResult(result);
-  SendResponse(true);
-}
-
-SearchDriveMetadataFunction::SearchDriveMetadataFunction() {
-}
-
-SearchDriveMetadataFunction::~SearchDriveMetadataFunction() {
-}
-
-bool SearchDriveMetadataFunction::RunImpl() {
-  DictionaryValue* search_params;
-  if (!args_->GetDictionary(0, &search_params))
-    return false;
-
-  std::string query;
-  if (!search_params->GetString("query", &query))
-    return false;
-
-  std::string types;
-  if (!search_params->GetString("types", &types))
-    return false;
-
-  int max_results = 0;
-  if (!search_params->GetInteger("maxResults", &max_results))
-    return false;
-
-  drive::util::Log("%s[%d] called. (types: '%s', maxResults: '%d')",
-                   name().c_str(),
-                   request_id(),
-                   types.c_str(),
-                   max_results);
-  set_log_on_completion(true);
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled.
-  if (!integration_service || !integration_service->file_system())
-    return false;
-
-  int options = drive::SEARCH_METADATA_ALL;
-  // TODO(hirono): Switch to the JSON scheme compiler. http://crbug.com/241693
-  if (types == "EXCLUDE_DIRECTORIES")
-    options = drive::SEARCH_METADATA_EXCLUDE_DIRECTORIES;
-  else if (types == "SHARED_WITH_ME")
-    options = drive::SEARCH_METADATA_SHARED_WITH_ME;
-  else if (types == "OFFLINE")
-    options = drive::SEARCH_METADATA_OFFLINE;
-  else
-    DCHECK_EQ("ALL", types);
-
-  integration_service->file_system()->SearchMetadata(
-      query,
-      options,
-      max_results,
-      base::Bind(&SearchDriveMetadataFunction::OnSearchMetadata, this));
-  return true;
-}
-
-void SearchDriveMetadataFunction::OnSearchMetadata(
-    drive::FileError error,
-    scoped_ptr<drive::MetadataSearchResultVector> results) {
-  if (error != drive::FILE_ERROR_OK) {
-    SendResponse(false);
-    return;
-  }
-
-  DCHECK(results.get());
-
-  base::ListValue* results_list = new ListValue();
-
-  // Convert Drive files to something File API stack can understand.  See
-  // file_browser_handler_custom_bindings.cc and
-  // file_browser_private_custom_bindings.js for how this is magically
-  // converted to a FileEntry.
-  GURL origin_url = source_url_.GetOrigin();
-  fileapi::FileSystemType file_system_type = fileapi::kFileSystemTypeExternal;
-  GURL file_system_root_url =
-      fileapi::GetFileSystemRootURI(origin_url, file_system_type);
-  std::string file_system_name =
-      fileapi::GetFileSystemName(origin_url, file_system_type);
-  for (size_t i = 0; i < results->size(); ++i) {
-    DictionaryValue* result_dict = new DictionaryValue();
-
-    // FileEntry fields.
-    DictionaryValue* entry = new DictionaryValue();
-    entry->SetString("fileSystemName", file_system_name);
-    entry->SetString("fileSystemRoot", file_system_root_url.spec());
-    entry->SetString("fileFullPath", "/" + results->at(i).path.value());
-    entry->SetBoolean("fileIsDirectory",
-                      results->at(i).entry.file_info().is_directory());
-
-    result_dict->Set("entry", entry);
-    result_dict->SetString("highlightedBaseName",
-                           results->at(i).highlighted_base_name);
-    results_list->Append(result_dict);
-  }
-
-  SetResult(results_list);
-  SendResponse(true);
-}
-
-ClearDriveCacheFunction::ClearDriveCacheFunction() {
-}
-
-ClearDriveCacheFunction::~ClearDriveCacheFunction() {
-}
-
-bool ClearDriveCacheFunction::RunImpl() {
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled.
-  if (!integration_service || !integration_service->file_system())
-    return false;
-
-  // TODO(yoshiki): Receive a callback from JS-side and pass it to
-  // ClearCacheAndRemountFileSystem(). http://crbug.com/140511
-  integration_service->ClearCacheAndRemountFileSystem(
-      base::Bind(&DoNothingWithBool));
-
-  SendResponse(true);
-  return true;
-}
-
-GetDriveConnectionStateFunction::GetDriveConnectionStateFunction() {
-}
-
-GetDriveConnectionStateFunction::~GetDriveConnectionStateFunction() {
-}
-
-bool GetDriveConnectionStateFunction::RunImpl() {
-  scoped_ptr<DictionaryValue> value(new DictionaryValue());
-  scoped_ptr<ListValue> reasons(new ListValue());
-
-  std::string type_string;
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-
-  bool ready = integration_service &&
-      integration_service->drive_service()->CanSendRequest();
-  bool is_connection_cellular =
-      net::NetworkChangeNotifier::IsConnectionCellular(
-          net::NetworkChangeNotifier::GetConnectionType());
-
-  if (net::NetworkChangeNotifier::IsOffline() || !ready) {
-    type_string = kDriveConnectionTypeOffline;
-    if (net::NetworkChangeNotifier::IsOffline())
-      reasons->AppendString(kDriveConnectionReasonNoNetwork);
-    if (!ready)
-      reasons->AppendString(kDriveConnectionReasonNotReady);
-    if (!integration_service)
-      reasons->AppendString(kDriveConnectionReasonNoService);
-  } else if (
-      is_connection_cellular &&
-      profile_->GetPrefs()->GetBoolean(prefs::kDisableDriveOverCellular)) {
-    type_string = kDriveConnectionTypeMetered;
-  } else {
-    type_string = kDriveConnectionTypeOnline;
-  }
-
-  value->SetString("type", type_string);
-  value->Set("reasons", reasons.release());
-  SetResult(value.release());
-
-  drive::util::Log("%s succeeded.", name().c_str());
-  return true;
-}
-
-ZipSelectionFunction::ZipSelectionFunction() {
-}
-
-ZipSelectionFunction::~ZipSelectionFunction() {
-}
-
-bool ZipSelectionFunction::RunImpl() {
-  if (args_->GetSize() < 3) {
-    return false;
-  }
-
-  // First param is the source directory URL.
-  std::string dir_url;
-  if (!args_->GetString(0, &dir_url) || dir_url.empty())
-    return false;
-
-  base::FilePath src_dir = GetLocalPathFromURL(
-      render_view_host(), profile(), GURL(dir_url));
-  if (src_dir.empty())
-    return false;
-
-  // Second param is the list of selected file URLs.
-  ListValue* selection_urls = NULL;
-  args_->GetList(1, &selection_urls);
-  if (!selection_urls || !selection_urls->GetSize())
-    return false;
-
-  std::vector<base::FilePath> files;
-  for (size_t i = 0; i < selection_urls->GetSize(); ++i) {
-    std::string file_url;
-    selection_urls->GetString(i, &file_url);
-    base::FilePath path = GetLocalPathFromURL(
-        render_view_host(), profile(), GURL(file_url));
-    if (path.empty())
-      return false;
-    files.push_back(path);
-  }
-
-  // Third param is the name of the output zip file.
-  std::string dest_name;
-  if (!args_->GetString(2, &dest_name) || dest_name.empty())
-    return false;
-
-  // Check if the dir path is under Drive mount point.
-  // TODO(hshi): support create zip file on Drive (crbug.com/158690).
-  if (drive::util::IsUnderDriveMountPoint(src_dir))
-    return false;
-
-  base::FilePath dest_file = src_dir.Append(dest_name);
-  std::vector<base::FilePath> src_relative_paths;
-  for (size_t i = 0; i != files.size(); ++i) {
-    const base::FilePath& file_path = files[i];
-
-    // Obtain the relative path of |file_path| under |src_dir|.
-    base::FilePath relative_path;
-    if (!src_dir.AppendRelativePath(file_path, &relative_path))
-      return false;
-    src_relative_paths.push_back(relative_path);
-  }
-
-  zip_file_creator_ = new ZipFileCreator(this, src_dir, src_relative_paths,
-                                         dest_file);
-
-  // Keep the refcount until the zipping is complete on utility process.
-  AddRef();
-
-  zip_file_creator_->Start();
-  return true;
-}
-
-void ZipSelectionFunction::OnZipDone(bool success) {
-  SetResult(new base::FundamentalValue(success));
-  SendResponse(true);
-  Release();
-}
-
-ValidatePathNameLengthFunction::ValidatePathNameLengthFunction() {
-}
-
-ValidatePathNameLengthFunction::~ValidatePathNameLengthFunction() {
-}
-
-bool ValidatePathNameLengthFunction::RunImpl() {
-  std::string parent_url;
-  if (!args_->GetString(0, &parent_url))
-    return false;
-
-  std::string name;
-  if (!args_->GetString(1, &name))
-    return false;
-
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
-  scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      BrowserContext::GetStoragePartition(profile(), site_instance)->
-          GetFileSystemContext();
-  fileapi::FileSystemURL filesystem_url(
-      file_system_context->CrackURL(GURL(parent_url)));
-  if (!chromeos::FileSystemBackend::CanHandleURL(filesystem_url))
-    return false;
-
-  // No explicit limit on the length of Drive file names.
-  if (filesystem_url.type() == fileapi::kFileSystemTypeDrive) {
-    SetResult(new base::FundamentalValue(true));
-    SendResponse(true);
-    return true;
-  }
-
-  base::PostTaskAndReplyWithResult(
-      BrowserThread::GetBlockingPool(),
-      FROM_HERE,
-      base::Bind(&GetFileNameMaxLengthOnBlockingPool,
-                 filesystem_url.path().AsUTF8Unsafe()),
-      base::Bind(&ValidatePathNameLengthFunction::OnFilePathLimitRetrieved,
-                 this, name.size()));
-  return true;
-}
-
-void ValidatePathNameLengthFunction::OnFilePathLimitRetrieved(
-    size_t current_length,
-    size_t max_length) {
-  SetResult(new base::FundamentalValue(current_length <= max_length));
-  SendResponse(true);
-}
-
-ZoomFunction::ZoomFunction() {
-}
-
-ZoomFunction::~ZoomFunction() {
-}
-
-bool ZoomFunction::RunImpl() {
-  content::RenderViewHost* const view_host = render_view_host();
-  std::string operation;
-  args_->GetString(0, &operation);
-  content::PageZoom zoom_type;
-  if (operation == "in") {
-    zoom_type = content::PAGE_ZOOM_IN;
-  } else if (operation == "out") {
-    zoom_type = content::PAGE_ZOOM_OUT;
-  } else if (operation == "reset") {
-    zoom_type = content::PAGE_ZOOM_RESET;
-  } else {
-    NOTREACHED();
-    return false;
-  }
-  view_host->Zoom(zoom_type);
-  return true;
-}
-
-RequestAccessTokenFunction::RequestAccessTokenFunction() {
-}
-
-RequestAccessTokenFunction::~RequestAccessTokenFunction() {
-}
-
-bool RequestAccessTokenFunction::RunImpl() {
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  bool refresh;
-  args_->GetBoolean(0, &refresh);
-
-  if (!integration_service) {
-    SetResult(new base::StringValue(""));
-    SendResponse(true);
-    return true;
-  }
-
-  // If refreshing is requested, then clear the token to refetch it.
-  if (refresh)
-    integration_service->drive_service()->ClearAccessToken();
-
-  // Retrieve the cached auth token (if available), otherwise the AuthService
-  // instance will try to refetch it.
-  integration_service->drive_service()->RequestAccessToken(
-      base::Bind(&RequestAccessTokenFunction::OnAccessTokenFetched, this));
-  return true;
-}
-
-void RequestAccessTokenFunction::OnAccessTokenFetched(
-    google_apis::GDataErrorCode code, const std::string& access_token) {
-  SetResult(new base::StringValue(access_token));
-  SendResponse(true);
-}
-
-GetShareUrlFunction::GetShareUrlFunction() {
-}
-
-GetShareUrlFunction::~GetShareUrlFunction() {
-}
-
-bool GetShareUrlFunction::RunImpl() {
-  std::string file_url;
-  if (!args_->GetString(0, &file_url))
-    return false;
-
-  const base::FilePath path = GetLocalPathFromURL(
-      render_view_host(), profile(), GURL(file_url));
-  DCHECK(drive::util::IsUnderDriveMountPoint(path));
-
-  base::FilePath drive_path = drive::util::ExtractDrivePath(path);
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled.
-  if (!integration_service)
-    return false;
-
-  integration_service->file_system()->GetShareUrl(
-      drive_path,
-      file_manager_util::GetFileBrowserExtensionUrl(),  // embed origin
-      base::Bind(&GetShareUrlFunction::OnGetShareUrl, this));
-  return true;
-}
-
-
-void GetShareUrlFunction::OnGetShareUrl(drive::FileError error,
-                                        const GURL& share_url) {
-  if (error != drive::FILE_ERROR_OK) {
-    error_ = "Share Url for this item is not available.";
-    SendResponse(false);
-    return;
-  }
-
-  SetResult(new base::StringValue(share_url.spec()));
-  SendResponse(true);
-}
-
 }  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h b/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h
index d2fd6ca..60da290 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h
@@ -5,48 +5,15 @@
 #ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_BROWSER_PRIVATE_API_H_
 #define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_BROWSER_PRIVATE_API_H_
 
-#include <map>
-#include <queue>
-#include <string>
-#include <vector>
-
 #include "base/memory/scoped_ptr.h"
-#include "base/platform_file.h"
-#include "base/prefs/pref_service.h"
-#include "base/time/time.h"
-#include "chrome/browser/chromeos/drive/file_errors.h"
-#include "chrome/browser/chromeos/drive/file_system_interface.h"
-#include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
-#include "chrome/browser/chromeos/extensions/file_manager/zip_file_creator.h"
-#include "chrome/browser/extensions/extension_function.h"
 #include "components/browser_context_keyed_service/browser_context_keyed_service.h"
 
-class FileManagerEventRouter;
-class GURL;
 class Profile;
 
-namespace base {
-class Value;
-}
-
-namespace fileapi {
-class FileSystemContext;
-class FileSystemURL;
-}
-
-namespace drive {
-class FileCacheEntry;
-class DriveAppRegistry;
-struct DriveAppInfo;
-struct SearchResultInfo;
-}
-
-namespace ui {
-struct SelectedFileInfo;
-}
-
 namespace file_manager {
 
+class FileManagerEventRouter;
+
 // Manages and registers the fileBrowserPrivate API with the extension system.
 class FileBrowserPrivateAPI : public BrowserContextKeyedService {
  public:
@@ -67,722 +34,6 @@
   scoped_ptr<FileManagerEventRouter> event_router_;
 };
 
-// Select a single file.  Closes the dialog window.
-// Implements the chrome.fileBrowserPrivate.logoutUser method.
-class LogoutUserFunction : public SyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.logoutUser",
-                             FILEBROWSERPRIVATE_LOGOUTUSER)
-
-  LogoutUserFunction();
-
- protected:
-  virtual ~LogoutUserFunction();
-
-  // SyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Implements the chrome.fileBrowserPrivate.requestFileSystem method.
-class RequestFileSystemFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.requestFileSystem",
-                             FILEBROWSERPRIVATE_REQUESTFILESYSTEM)
-
-  RequestFileSystemFunction();
-
- protected:
-  virtual ~RequestFileSystemFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  void RespondSuccessOnUIThread(const std::string& name,
-                                const GURL& root_path);
-  void RespondFailedOnUIThread(base::PlatformFileError error_code);
-
-  // Called when FileSystemContext::OpenFileSystem() is done.
-  void DidOpenFileSystem(
-      scoped_refptr<fileapi::FileSystemContext> file_system_context,
-      base::PlatformFileError result,
-      const std::string& name,
-      const GURL& root_path);
-
-  // Called when something goes wrong. Records the error to |error_| per the
-  // error code and reports that the private API function failed.
-  void DidFail(base::PlatformFileError error_code);
-
-  // Sets up file system access permissions to the extension identified by
-  // |child_id|.
-  bool SetupFileSystemAccessPermissions(
-      scoped_refptr<fileapi::FileSystemContext> file_system_context,
-      int child_id,
-      scoped_refptr<const extensions::Extension> extension);
-};
-
-// Implements the chrome.fileBrowserPrivate.addFileWatch method.
-class FileWatchBrowserFunctionBase : public LoggedAsyncExtensionFunction {
- public:
-  FileWatchBrowserFunctionBase();
-
- protected:
-  virtual ~FileWatchBrowserFunctionBase();
-
-  // Performs a file watch operation (ex. adds or removes a file watch).
-  virtual void PerformFileWatchOperation(
-      const base::FilePath& local_path,
-      const base::FilePath& virtual_path,
-      const std::string& extension_id) = 0;
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
-  // Calls SendResponse() with |success| converted to base::Value.
-  void Respond(bool success);
-};
-
-// Implements the chrome.fileBrowserPrivate.addFileWatch method.
-class AddFileWatchBrowserFunction : public FileWatchBrowserFunctionBase {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.addFileWatch",
-                             FILEBROWSERPRIVATE_ADDFILEWATCH)
-
-  AddFileWatchBrowserFunction();
-
- protected:
-  virtual ~AddFileWatchBrowserFunction();
-
-  // FileWatchBrowserFunctionBase override.
-  virtual void PerformFileWatchOperation(
-      const base::FilePath& local_path,
-      const base::FilePath& virtual_path,
-      const std::string& extension_id) OVERRIDE;
-};
-
-
-// Implements the chrome.fileBrowserPrivate.removeFileWatch method.
-class RemoveFileWatchBrowserFunction : public FileWatchBrowserFunctionBase {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.removeFileWatch",
-                             FILEBROWSERPRIVATE_REMOVEFILEWATCH)
-
-  RemoveFileWatchBrowserFunction();
-
- protected:
-  virtual ~RemoveFileWatchBrowserFunction();
-
-  // FileWatchBrowserFunctionBase override.
-  virtual void PerformFileWatchOperation(
-      const base::FilePath& local_path,
-      const base::FilePath& virtual_path,
-      const std::string& extension_id) OVERRIDE;
-};
-
-// Implements the chrome.fileBrowserPrivate.getFileTasks method.
-class GetFileTasksFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getFileTasks",
-                             FILEBROWSERPRIVATE_GETFILETASKS)
-
-  GetFileTasksFunction();
-
- protected:
-  virtual ~GetFileTasksFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  struct FileInfo;
-  typedef std::vector<FileInfo> FileInfoList;
-
-  // Holds fields to build a task result.
-  struct TaskInfo;
-
-  // Map from a task id to TaskInfo.
-  typedef std::map<std::string, TaskInfo> TaskInfoMap;
-
-  // Looks up available apps for each file in |file_info_list| in the
-  // |registry|, and returns the intersection of all available apps as a
-  // map from task id to TaskInfo.
-  static void GetAvailableDriveTasks(drive::DriveAppRegistry* registry,
-                                     const FileInfoList& file_info_list,
-                                     TaskInfoMap* task_info_map);
-
-  // Looks in the preferences and finds any of the available apps that are
-  // also listed as default apps for any of the files in the info list.
-  void FindDefaultDriveTasks(const FileInfoList& file_info_list,
-                             const TaskInfoMap& task_info_map,
-                             std::set<std::string>* default_tasks);
-
-  // Creates a list of each task in |task_info_map| and stores the result into
-  // |result_list|. If a default task is set in the result list,
-  // |default_already_set| is set to true.
-  static void CreateDriveTasks(const TaskInfoMap& task_info_map,
-                               const std::set<std::string>& default_tasks,
-                               ListValue* result_list,
-                               bool* default_already_set);
-
-  // Find the list of drive apps that can be used with the given file types. If
-  // a default task is set in the result list, then |default_already_set| is set
-  // to true.
-  bool FindDriveAppTasks(const FileInfoList& file_info_list,
-                         ListValue* result_list,
-                         bool* default_already_set);
-
-  // Find the list of app file handlers that can be used with the given file
-  // types, appending them to the |result_list|. If a default task is set in the
-  // result list, then |default_already_set| is set to true.
-  bool FindAppTasks(const std::vector<base::FilePath>& file_paths,
-                    ListValue* result_list,
-                    bool* default_already_set);
-};
-
-// Implements the chrome.fileBrowserPrivate.executeTask method.
-class ExecuteTasksFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.executeTask",
-                             FILEBROWSERPRIVATE_EXECUTETASK)
-
-  ExecuteTasksFunction();
-
- protected:
-  virtual ~ExecuteTasksFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
-  void OnTaskExecuted(bool success);
-};
-
-// Implements the chrome.fileBrowserPrivate.setDefaultTask method.
-class SetDefaultTaskFunction : public SyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.setDefaultTask",
-                             FILEBROWSERPRIVATE_SETDEFAULTTASK)
-
-  SetDefaultTaskFunction();
-
- protected:
-  virtual ~SetDefaultTaskFunction();
-
-  // SyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-class SelectFileFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.selectFile",
-                             FILEBROWSERPRIVATE_SELECTFILE)
-
-  SelectFileFunction();
-
- protected:
-  virtual ~SelectFileFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  // A callback method to handle the result of GetSelectedFileInfo.
-  void GetSelectedFileInfoResponse(
-      const std::vector<ui::SelectedFileInfo>& files);
-};
-
-// View multiple selected files.  Window stays open.
-class ViewFilesFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.viewFiles",
-                             FILEBROWSERPRIVATE_VIEWFILES)
-
-  ViewFilesFunction();
-
- protected:
-  virtual ~ViewFilesFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Select multiple files.  Closes the dialog window.
-class SelectFilesFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.selectFiles",
-                             FILEBROWSERPRIVATE_SELECTFILES)
-
-  SelectFilesFunction();
-
- protected:
-  virtual ~SelectFilesFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  // A callback method to handle the result of GetSelectedFileInfo.
-  void GetSelectedFileInfoResponse(
-      const std::vector<ui::SelectedFileInfo>& files);
-};
-
-// Cancel file selection Dialog.  Closes the dialog window.
-class CancelFileDialogFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.cancelDialog",
-                             FILEBROWSERPRIVATE_CANCELDIALOG)
-
-  CancelFileDialogFunction();
-
- protected:
-  virtual ~CancelFileDialogFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Mount a device or a file.
-class AddMountFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.addMount",
-                             FILEBROWSERPRIVATE_ADDMOUNT)
-
-  AddMountFunction();
-
- protected:
-  virtual ~AddMountFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  // A callback method to handle the result of MarkCacheAsMounted.
-  void OnMountedStateSet(const std::string& mount_type,
-                         const base::FilePath::StringType& file_name,
-                         drive::FileError error,
-                         const base::FilePath& file_path);
-};
-
-// Unmounts selected device. Expects mount point path as an argument.
-class RemoveMountFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.removeMount",
-                             FILEBROWSERPRIVATE_REMOVEMOUNT)
-
-  RemoveMountFunction();
-
- protected:
-  virtual ~RemoveMountFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  // A callback method to handle the result of GetSelectedFileInfo.
-  void GetSelectedFileInfoResponse(
-      const std::vector<ui::SelectedFileInfo>& files);
-};
-
-class GetMountPointsFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getMountPoints",
-                             FILEBROWSERPRIVATE_GETMOUNTPOINTS)
-
-  GetMountPointsFunction();
-
- protected:
-  virtual ~GetMountPointsFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Formats Device given its mount path.
-class FormatDeviceFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.formatDevice",
-                             FILEBROWSERPRIVATE_FORMATDEVICE)
-
-  FormatDeviceFunction();
-
- protected:
-  virtual ~FormatDeviceFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Sets last modified date in seconds of local file
-class SetLastModifiedFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.setLastModified",
-                             FILEBROWSERPRIVATE_SETLASTMODIFIED)
-
-  SetLastModifiedFunction();
-
- protected:
-  virtual ~SetLastModifiedFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-class GetSizeStatsFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getSizeStats",
-                             FILEBROWSERPRIVATE_GETSIZESTATS)
-
-  GetSizeStatsFunction();
-
- protected:
-  virtual ~GetSizeStatsFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  void GetDriveAvailableSpaceCallback(drive::FileError error,
-                                      int64 bytes_total,
-                                      int64 bytes_used);
-
-  void GetSizeStatsCallback(const size_t* total_size_kb,
-                            const size_t* remaining_size_kb);
-};
-
-// Retrieves devices meta-data. Expects volume's device path as an argument.
-class GetVolumeMetadataFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getVolumeMetadata",
-                             FILEBROWSERPRIVATE_GETVOLUMEMETADATA)
-
-  GetVolumeMetadataFunction();
-
- protected:
-  virtual ~GetVolumeMetadataFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// File Dialog Strings.
-class FileDialogStringsFunction : public SyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getStrings",
-                             FILEBROWSERPRIVATE_GETSTRINGS)
-
-  FileDialogStringsFunction();
-
- protected:
-  virtual ~FileDialogStringsFunction();
-
-  // SyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Retrieves property information for an entry and returns it as a dictionary.
-// On error, returns a dictionary with the key "error" set to the error number
-// (drive::FileError).
-class GetDriveEntryPropertiesFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getDriveEntryProperties",
-                             FILEBROWSERPRIVATE_GETDRIVEFILEPROPERTIES)
-
-  GetDriveEntryPropertiesFunction();
-
- protected:
-  virtual ~GetDriveEntryPropertiesFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  void OnGetFileInfo(drive::FileError error,
-                     scoped_ptr<drive::ResourceEntry> entry);
-
-  void CacheStateReceived(bool success,
-                          const drive::FileCacheEntry& cache_entry);
-
-  void CompleteGetFileProperties(drive::FileError error);
-
-  base::FilePath file_path_;
-  scoped_ptr<base::DictionaryValue> properties_;
-};
-
-// Implements the chrome.fileBrowserPrivate.pinDriveFile method.
-class PinDriveFileFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.pinDriveFile",
-                             FILEBROWSERPRIVATE_PINDRIVEFILE)
-
-  PinDriveFileFunction();
-
- protected:
-  virtual ~PinDriveFileFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  // Callback for RunImpl().
-  void OnPinStateSet(drive::FileError error);
-};
-
-// Get drive files for the given list of file URLs. Initiate downloading of
-// drive files if these are not cached. Return a list of local file names.
-// This function puts empty strings instead of local paths for files could
-// not be obtained. For instance, this can happen if the user specifies a new
-// file name to save a file on drive. There may be other reasons to fail. The
-// file manager should check if the local paths returned from getDriveFiles()
-// contain empty paths.
-// TODO(satorux): Should we propagate error types to the JavaScript layer?
-class GetDriveFilesFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getDriveFiles",
-                             FILEBROWSERPRIVATE_GETDRIVEFILES)
-
-  GetDriveFilesFunction();
-
- protected:
-  virtual ~GetDriveFilesFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  // Gets the file on the top of the |remaining_drive_paths_| or sends the
-  // response if the queue is empty.
-  void GetFileOrSendResponse();
-
-  // Called by FileSystem::GetFile(). Pops the file from
-  // |remaining_drive_paths_|, and calls GetFileOrSendResponse().
-  void OnFileReady(drive::FileError error,
-                   const base::FilePath& local_path,
-                   scoped_ptr<drive::ResourceEntry> entry);
-
-  std::queue<base::FilePath> remaining_drive_paths_;
-  ListValue* local_paths_;
-};
-
-// Implements the chrome.fileBrowserPrivate.cancelFileTransfers method.
-class CancelFileTransfersFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.cancelFileTransfers",
-                             FILEBROWSERPRIVATE_CANCELFILETRANSFERS)
-
-  CancelFileTransfersFunction();
-
- protected:
-  virtual ~CancelFileTransfersFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Implements the chrome.fileBrowserPrivate.transferFile method.
-class TransferFileFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.transferFile",
-                             FILEBROWSERPRIVATE_TRANSFERFILE)
-
-  TransferFileFunction();
-
- protected:
-  virtual ~TransferFileFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  // Helper callback for handling response from FileSystem::TransferFile().
-  void OnTransferCompleted(drive::FileError error);
-};
-
-// Read setting value.
-class GetPreferencesFunction : public SyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getPreferences",
-                             FILEBROWSERPRIVATE_GETPREFERENCES)
-
-  GetPreferencesFunction();
-
- protected:
-  virtual ~GetPreferencesFunction();
-
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Write setting value.
-class SetPreferencesFunction : public SyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.setPreferences",
-                             FILEBROWSERPRIVATE_SETPREFERENCES)
-
-  SetPreferencesFunction();
-
- protected:
-  virtual ~SetPreferencesFunction();
-
-  virtual bool RunImpl() OVERRIDE;
-};
-
-class SearchDriveFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.searchDrive",
-                             FILEBROWSERPRIVATE_SEARCHDRIVE)
-
-  SearchDriveFunction();
-
- protected:
-  virtual ~SearchDriveFunction();
-
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  // Callback for Search().
-  void OnSearch(drive::FileError error,
-                const GURL& next_feed,
-                scoped_ptr<std::vector<drive::SearchResultInfo> > result_paths);
-};
-
-// Similar to SearchDriveFunction but this one is used for searching drive
-// metadata which is stored locally.
-class SearchDriveMetadataFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.searchDriveMetadata",
-                             FILEBROWSERPRIVATE_SEARCHDRIVEMETADATA)
-
-  SearchDriveMetadataFunction();
-
- protected:
-  virtual ~SearchDriveMetadataFunction();
-
-  virtual bool RunImpl() OVERRIDE;
-
- private:
-  // Callback for SearchMetadata();
-  void OnSearchMetadata(drive::FileError error,
-                        scoped_ptr<drive::MetadataSearchResultVector> results);
-};
-
-class ClearDriveCacheFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.clearDriveCache",
-                             FILEBROWSERPRIVATE_CLEARDRIVECACHE)
-
-  ClearDriveCacheFunction();
-
- protected:
-  virtual ~ClearDriveCacheFunction();
-
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Implements the chrome.fileBrowserPrivate.getDriveConnectionState method.
-class GetDriveConnectionStateFunction : public SyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION(
-      "fileBrowserPrivate.getDriveConnectionState",
-      FILEBROWSERPRIVATE_GETDRIVECONNECTIONSTATE);
-
-  GetDriveConnectionStateFunction();
-
- protected:
-  virtual ~GetDriveConnectionStateFunction();
-
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Create a zip file for the selected files.
-class ZipSelectionFunction : public LoggedAsyncExtensionFunction,
-                             public extensions::ZipFileCreator::Observer {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.zipSelection",
-                             FILEBROWSERPRIVATE_ZIPSELECTION)
-
-  ZipSelectionFunction();
-
- protected:
-  virtual ~ZipSelectionFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
-  // extensions::ZipFileCreator::Delegate overrides.
-  virtual void OnZipDone(bool success) OVERRIDE;
-
- private:
-  scoped_refptr<extensions::ZipFileCreator> zip_file_creator_;
-};
-
-// Implements the chrome.fileBrowserPrivate.validatePathNameLength method.
-class ValidatePathNameLengthFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.validatePathNameLength",
-                             FILEBROWSERPRIVATE_VALIDATEPATHNAMELENGTH)
-
-  ValidatePathNameLengthFunction();
-
- protected:
-  virtual ~ValidatePathNameLengthFunction();
-
-  void OnFilePathLimitRetrieved(size_t current_length, size_t max_length);
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Changes the zoom level of the file manager by internally calling
-// RenderViewHost::Zoom(). TODO(hirono): Remove this function once the zoom
-// level change is supported for all apps. crbug.com/227175.
-class ZoomFunction : public SyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.zoom",
-                             FILEBROWSERPRIVATE_ZOOM);
-
-  ZoomFunction();
-
- protected:
-  virtual ~ZoomFunction();
-  virtual bool RunImpl() OVERRIDE;
-};
-
-// Implements the chrome.fileBrowserPrivate.requestAccessToken method.
-class RequestAccessTokenFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.requestAccessToken",
-                             FILEBROWSERPRIVATE_REQUESTACCESSTOKEN)
-
-  RequestAccessTokenFunction();
-
- protected:
-  virtual ~RequestAccessTokenFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
-  // Callback with a cached auth token (if available) or a fetched one.
-  void OnAccessTokenFetched(google_apis::GDataErrorCode code,
-                            const std::string& access_token);
-};
-
-// Implements the chrome.fileBrowserPrivate.getShareUrl method.
-class GetShareUrlFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getShareUrl",
-                             FILEBROWSERPRIVATE_GETSHAREURL)
-
-  GetShareUrlFunction();
-
- protected:
-  virtual ~GetShareUrlFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-
-  // Callback with an url to the sharing dialog as |share_url|, called by
-  // FileSystem::GetShareUrl.
-  void OnGetShareUrl(drive::FileError error, const GURL& share_url);
-};
-
 }  // namespace file_manager
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_BROWSER_PRIVATE_API_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_handler_util.h b/chrome/browser/chromeos/extensions/file_manager/file_handler_util.h
deleted file mode 100644
index 9cb5b67..0000000
--- a/chrome/browser/chromeos/extensions/file_manager/file_handler_util.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_HANDLER_UTIL_H_
-#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_HANDLER_UTIL_H_
-
-#include <string>
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/platform_file.h"
-#include "chrome/common/extensions/extension.h"
-
-class Browser;
-class FileBrowserHandler;
-class GURL;
-class Profile;
-
-namespace fileapi {
-class FileSystemURL;
-}
-
-namespace file_handler_util {
-
-// Tasks are stored as a vector in order of priorities.
-typedef std::vector<const FileBrowserHandler*> FileBrowserHandlerList;
-
-// Specifies the task type for a task id that represents some file action, Drive
-// action, or Web Intent action.
-extern const char kTaskFile[];
-extern const char kTaskDrive[];
-extern const char kTaskApp[];
-
-void UpdateFileHandlerUsageStats(Profile* profile, const std::string& task_id);
-
-// Returns true if the task should be used as a fallback. Such tasks are
-// Files.app's internal handlers as well as quick office extensions.
-bool IsFallbackTask(const FileBrowserHandler* task);
-
-// Update the default file handler for the given sets of suffixes and MIME
-// types.
-void UpdateDefaultTask(Profile* profile,
-                       const std::string& task_id,
-                       const std::set<std::string>& suffixes,
-                       const std::set<std::string>& mime_types);
-
-// Returns the task ID of the default task for the given |mime_type|/|suffix|
-// combination. If it finds a MIME type match, then it prefers that over a
-// suffix match. If it a default can't be found, then it returns the empty
-// string.
-std::string GetDefaultTaskIdFromPrefs(Profile* profile,
-                                      const std::string& mime_type,
-                                      const std::string& suffix);
-
-// Generates task id for the action specified by the extension. The |task_type|
-// must be one of kTaskFile, kTaskDrive or kTaskApp.
-std::string MakeTaskID(const std::string& extension_id,
-                       const std::string& task_type,
-                       const std::string& action_id);
-
-// Extracts action, type and extension id bound to the file task. Either
-// |target_extension_id| or |action_id| are allowed to be NULL if caller isn't
-// interested in those values.  Returns false on failure to parse.
-bool CrackTaskID(const std::string& task_id,
-                 std::string* target_extension_id,
-                 std::string* task_type,
-                 std::string* action_id);
-
-// This generates a list of default tasks (tasks set as default by the user in
-// prefs) from the |common_tasks|.
-void FindDefaultTasks(Profile* profile,
-                      const std::vector<base::FilePath>& files_list,
-                      const FileBrowserHandlerList& common_tasks,
-                      FileBrowserHandlerList* default_tasks);
-
-// This generates list of tasks common for all files in |file_list|.
-bool FindCommonTasks(Profile* profile,
-                     const std::vector<GURL>& files_list,
-                     FileBrowserHandlerList* common_tasks);
-
-// Finds a task for a file whose URL is |url| and whose path is |path|.
-// Returns default task if one is defined (The default task is the task that is
-// assigned to file browser task button by default). If default task is not
-// found, tries to match the url with one of the builtin tasks.
-bool GetTaskForURLAndPath(Profile* profile,
-                          const GURL& url,
-                          const base::FilePath& path,
-                          const FileBrowserHandler** handler);
-
-// Used for returning success or failure from task executions.
-typedef base::Callback<void(bool)> FileTaskFinishedCallback;
-
-// Executes file handler task for each element of |file_urls|.
-// Returns |false| if the execution cannot be initiated. Otherwise returns
-// |true| and then eventually calls |done| when all the files have been handled.
-// |done| can be a null callback.
-bool ExecuteFileTask(Profile* profile,
-                     const GURL& source_url,
-                     const std::string& file_browser_id,
-                     int32 tab_id,
-                     const std::string& extension_id,
-                     const std::string& task_type,
-                     const std::string& action_id,
-                     const std::vector<fileapi::FileSystemURL>& file_urls,
-                     const FileTaskFinishedCallback& done);
-
-}  // namespace file_handler_util
-
-#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_HANDLER_UTIL_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_browsertest.cc
index 64ec69b..246bcf2 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_browsertest.cc
@@ -36,6 +36,7 @@
 #include "content/public/test/test_utils.h"
 #include "webkit/browser/fileapi/external_mount_points.h"
 
+namespace file_manager {
 namespace {
 
 enum EntryType {
@@ -377,7 +378,7 @@
     for (size_t i = 0; i < arraysize(kTestEntrySetDriveOnly); ++i)
       drive_volume_->CreateEntry(kTestEntrySetDriveOnly[i]);
 
-    drive_test_util::WaitUntilDriveMountPointIsAdded(browser()->profile());
+    test_util::WaitUntilDriveMountPointIsAdded(browser()->profile());
   }
 }
 
@@ -454,9 +455,8 @@
                                     "galleryOpenDownloads"),
                       TestParameter(NOT_IN_GUEST_MODE, "galleryOpenDrive")));
 
-// TODO(mtomasz): Fix this test. crbug.com/252561
 INSTANTIATE_TEST_CASE_P(
-    DISABLED_KeyboardOperations,
+    KeyboardOperations,
     FileManagerBrowserTest,
     ::testing::Values(TestParameter(IN_GUEST_MODE, "keyboardDeleteDownloads"),
                       TestParameter(NOT_IN_GUEST_MODE,
@@ -508,4 +508,6 @@
                       TestParameter(NOT_IN_GUEST_MODE, "restoreSortColumn"),
                       TestParameter(IN_GUEST_MODE, "restoreCurrentView"),
                       TestParameter(NOT_IN_GUEST_MODE, "restoreCurrentView")));
+
 }  // namespace
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.cc
index 487ea2d..8e93964 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.cc
@@ -40,6 +40,7 @@
 using drive::DriveIntegrationService;
 using drive::DriveIntegrationServiceFactory;
 
+namespace file_manager {
 namespace {
 
 const char kPathChanged[] = "changed";
@@ -164,7 +165,7 @@
   DCHECK(IsActiveFileTransferJobInfo(job_info));
 
   scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
-  GURL url = file_manager_util::ConvertRelativePathToFileSystemUrl(
+  GURL url = util::ConvertRelativePathToFileSystemUrl(
       job_info.file_path, extension_id);
   result->SetString("fileUrl", url.spec());
   result->SetString("transferState", job_status);
@@ -708,7 +709,7 @@
       mount_info.mount_condition) {
     // Convert mount point path to relative path with the external file system
     // exposed within File API.
-    if (file_manager_util::ConvertFileToRelativeFileSystemPath(
+    if (util::ConvertFileToRelativeFileSystemPath(
             profile_,
             kFileBrowserDomain,
             base::FilePath(mount_info.mount_path),
@@ -746,8 +747,8 @@
       dcim_path,
       IsGooglePhotosInstalled(profile_) ?
         base::Bind(&base::DoNothing) :
-        base::Bind(&file_manager_util::ViewRemovableDrive, mount_path),
-      base::Bind(&file_manager_util::ViewRemovableDrive, mount_path));
+        base::Bind(&util::ViewRemovableDrive, mount_path),
+      base::Bind(&util::ViewRemovableDrive, mount_path));
 }
 
 void FileManagerEventRouter::OnDiskAdded(
@@ -870,3 +871,5 @@
                                      device_path);
   }
 }
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.h b/chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.h
index 7fff315..ace1195 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.h
@@ -18,11 +18,14 @@
 #include "chrome/browser/drive/drive_service_interface.h"
 #include "chromeos/disks/disk_mount_manager.h"
 
-class FileManagerNotifications;
-class MountedDiskMonitor;
 class PrefChangeRegistrar;
 class Profile;
 
+namespace file_manager {
+
+class FileManagerNotifications;
+class MountedDiskMonitor;
+
 // Monitors changes in disk mounts, network connection state and preferences
 // affecting File Manager. Dispatches appropriate File Browser events.
 class FileManagerEventRouter
@@ -166,4 +169,6 @@
   DISALLOW_COPY_AND_ASSIGN(FileManagerEventRouter);
 };
 
+}  // namespace file_manager
+
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_MANAGER_EVENT_ROUTER_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications.cc
index 89cdd77..64236e4 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications.cc
@@ -16,6 +16,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 
+namespace file_manager {
 namespace {
 
 struct NotificationTypeInfo {
@@ -169,7 +170,7 @@
     // TODO(mukai): refactor here to invoke NotificationUIManager directly.
     const string16 replace_id = UTF8ToUTF16(notification_id);
     DesktopNotificationService::AddIconNotification(
-        file_manager_util::GetFileBrowserExtensionUrl(), GetTitle(type),
+        util::GetFileBrowserExtensionUrl(), GetTitle(type),
         message, icon, replace_id,
         new Delegate(host->AsWeakPtr(), notification_id), profile);
   }
@@ -383,3 +384,5 @@
     return string16();
   return it->second->message();
 }
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications.h b/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications.h
index 559e9cc..d41fec4 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications.h
@@ -16,6 +16,8 @@
 
 class Profile;
 
+namespace file_manager {
+
 class FileManagerNotifications
     : public base::SupportsWeakPtr<FileManagerNotifications> {
  public:
@@ -94,4 +96,6 @@
   DISALLOW_COPY_AND_ASSIGN(FileManagerNotifications);
 };
 
+}  // namespace file_manager
+
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_MANAGER_NOTIFICATIONS_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications_browsertest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications_browsertest.cc
index a590aa1..64c0dbc 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications_browsertest.cc
@@ -16,7 +16,7 @@
 #include "grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
-namespace chromeos {
+namespace file_manager {
 
 class FileManagerNotificationsTest : public InProcessBrowserTest {
  public:
@@ -176,4 +176,4 @@
   notifications_->UnregisterDevice("path");
 }
 
-}  // namespace chromeos.
+}  // namespace file_manager.
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications_unittest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications_unittest.cc
index 7a22c3b..00316d3 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications_unittest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_notifications_unittest.cc
@@ -17,7 +17,7 @@
 using ::testing::InSequence;
 using ::testing::StrEq;
 
-namespace chromeos {
+namespace file_manager {
 
 namespace {
 
@@ -241,4 +241,4 @@
       device_label, false, false, false);
 }
 
-}  // namespace chromeos.
+}  // namespace file_manager.
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_util.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_util.cc
index c698bfc..4579b77 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_util.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_util.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/chromeos/drive/file_system.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_handler_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
 #include "chrome/browser/chromeos/media/media_player.h"
 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
 #include "chrome/browser/extensions/crx_installer.h"
@@ -82,7 +82,8 @@
 
 const char kVideoPlayerAppName[] = "videoplayer";
 
-namespace file_manager_util {
+namespace file_manager {
+namespace util {
 namespace {
 
 const char kCRXExtension[] = ".crx";
@@ -179,6 +180,10 @@
       type_str = "folder";
       break;
 
+    case ui::SelectFileDialog::SELECT_UPLOAD_FOLDER:
+      type_str = "upload-folder";
+      break;
+
     case ui::SelectFileDialog::SELECT_SAVEAS_FILE:
       type_str = "saveas-file";
       break;
@@ -287,7 +292,7 @@
   std::vector<FileSystemURL> urls;
   urls.push_back(file_system_context->CrackURL(url));
 
-  file_handler_util::ExecuteFileTask(
+  file_tasks::ExecuteFileTask(
       profile,
       source_url,
       kFileBrowserDomain,
@@ -296,7 +301,7 @@
       task_type,
       action_id,
       urls,
-      file_handler_util::FileTaskFinishedCallback());
+      file_tasks::FileTaskFinishedCallback());
 }
 
 void OpenFileBrowserImpl(const base::FilePath& path,
@@ -311,7 +316,7 @@
   // Some values of |action_id| are not listed in the manifest and are used
   // to parametrize the behavior when opening the Files app window.
   ExecuteHandler(profile, kFileBrowserDomain, action_id, url,
-                 file_handler_util::kTaskFile);
+                 file_tasks::kTaskFile);
 }
 
 Browser* GetBrowserForUrl(GURL target_url) {
@@ -320,7 +325,7 @@
     TabStripModel* tab_strip = browser->tab_strip_model();
     for (int idx = 0; idx < tab_strip->count(); idx++) {
       content::WebContents* web_contents = tab_strip->GetWebContentsAt(idx);
-      const GURL& url = web_contents->GetURL();
+      const GURL& url = web_contents->GetLastCommittedURL();
       if (url == target_url)
         return browser;
     }
@@ -365,11 +370,11 @@
     for (FileHandlerList::iterator i = file_handlers.begin();
          i != file_handlers.end(); ++i) {
       const extensions::FileHandlerInfo* handler = *i;
-      std::string task_id = file_handler_util::MakeTaskID(extension->id(),
-          file_handler_util::kTaskApp, handler->id);
+      std::string task_id = file_tasks::MakeTaskID(extension->id(),
+          file_tasks::kTaskApp, handler->id);
       if (task_id == default_task_id) {
         ExecuteHandler(profile, extension->id(), handler->id, url,
-                       file_handler_util::kTaskApp);
+                       file_tasks::kTaskApp);
         return true;
 
       } else if (!first_handler) {
@@ -380,7 +385,7 @@
   }
   if (first_handler) {
     ExecuteHandler(profile, extension_for_first_handler->id(),
-                   first_handler->id, url, file_handler_util::kTaskApp);
+                   first_handler->id, url, file_tasks::kTaskApp);
     return true;
   }
   return false;
@@ -406,14 +411,14 @@
         action_id == kFileBrowserPlayTaskId ||
         action_id == kFileBrowserWatchTaskId) {
       ExecuteHandler(profile, extension_id, action_id, url,
-                     file_handler_util::kTaskFile);
+                     file_tasks::kTaskFile);
       return true;
     }
     return ExecuteBuiltinHandler(browser, path, action_id);
   }
 
   ExecuteHandler(profile, extension_id, action_id, url,
-                 file_handler_util::kTaskFile);
+                 file_tasks::kTaskFile);
   return true;
 }
 
@@ -423,7 +428,7 @@
     return false;
 
   std::string mime_type = GetMimeTypeForPath(path);
-  std::string default_task_id = file_handler_util::GetDefaultTaskIdFromPrefs(
+  std::string default_task_id = file_tasks::GetDefaultTaskIdFromPrefs(
       profile, mime_type, path.Extension());
   const FileBrowserHandler* handler;
 
@@ -435,15 +440,15 @@
   // 4. non-default app
   // 5. non-default extension
   // Note that there can be at most one of default extension and default app.
-  if (!file_handler_util::GetTaskForURLAndPath(profile, url, path, &handler)) {
+  if (!file_tasks::GetTaskForURLAndPath(profile, url, path, &handler)) {
     return ExecuteDefaultAppHandler(
         profile, path, url, mime_type, default_task_id);
   }
 
-  std::string handler_task_id = file_handler_util::MakeTaskID(
-        handler->extension_id(), file_handler_util::kTaskFile, handler->id());
+  std::string handler_task_id = file_tasks::MakeTaskID(
+        handler->extension_id(), file_tasks::kTaskFile, handler->id());
   if (handler_task_id != default_task_id &&
-      !file_handler_util::IsFallbackTask(handler) &&
+      !file_tasks::IsFallbackTask(handler) &&
       ExecuteDefaultAppHandler(
           profile, path, url, mime_type, default_task_id)) {
     return true;
@@ -652,6 +657,11 @@
           IDS_FILE_BROWSER_SELECT_FOLDER_TITLE);
       break;
 
+    case ui::SelectFileDialog::SELECT_UPLOAD_FOLDER:
+      title = l10n_util::GetStringUTF16(
+          IDS_FILE_BROWSER_SELECT_UPLOAD_FOLDER_TITLE);
+      break;
+
     case ui::SelectFileDialog::SELECT_SAVEAS_FILE:
       title = l10n_util::GetStringUTF16(
           IDS_FILE_BROWSER_SELECT_SAVEAS_FILE_TITLE);
@@ -861,4 +871,5 @@
   }
 }
 
-}  // namespace file_manager_util
+}  // namespace util
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_util.h b/chrome/browser/chromeos/extensions/file_manager/file_manager_util.h
index e5549f8..dbf68d7 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_util.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_util.h
@@ -19,7 +19,8 @@
 extern const char kFileBrowserWatchTaskId[];
 
 // File manager helper methods.
-namespace file_manager_util {
+namespace file_manager {
+namespace util {
 
 // Gets base file browser url.
 GURL GetFileBrowserExtensionUrl();
@@ -96,6 +97,7 @@
 // Returns the MIME type of |file_path|.
 std::string GetMimeTypeForPath(const base::FilePath& file_path);
 
-}  // namespace file_manager_util
+}  // namespace util
+}  // namespace file_manager
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_MANAGER_UTIL_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_handler_util.cc b/chrome/browser/chromeos/extensions/file_manager/file_tasks.cc
similarity index 98%
rename from chrome/browser/chromeos/extensions/file_manager/file_handler_util.cc
rename to chrome/browser/chromeos/extensions/file_manager/file_tasks.cc
index ca94b06..64a1214 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_handler_util.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_tasks.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/extensions/file_manager/file_handler_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
 
 #include "base/bind.h"
 #include "base/file_util.h"
@@ -49,7 +49,8 @@
 using extensions::Extension;
 using fileapi::FileSystemURL;
 
-namespace file_handler_util {
+namespace file_manager {
+namespace file_tasks {
 
 const char kTaskFile[] = "file";
 const char kTaskDrive[] = "drive";
@@ -255,9 +256,6 @@
                             action_id.c_str());
 }
 
-// Breaks down task_id that is used between getFileTasks() and executeTask() on
-// its building blocks. task_id field the following structure:
-//     <extension-id>|<task-type>|<task-action-id>
 bool CrackTaskID(const std::string& task_id,
                  std::string* extension_id,
                  std::string* task_type,
@@ -333,7 +331,7 @@
   std::set<std::string> default_ids;
   for (std::vector<base::FilePath>::const_iterator it = files_list.begin();
        it != files_list.end(); ++it) {
-    std::string task_id = file_handler_util::GetDefaultTaskIdFromPrefs(
+    std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs(
         profile, "", it->Extension());
     if (!task_id.empty())
       default_ids.insert(task_id);
@@ -810,4 +808,5 @@
   }
 }
 
-} // namespace file_handler_util
+}  // namespace file_tasks
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_tasks.h b/chrome/browser/chromeos/extensions/file_manager/file_tasks.h
new file mode 100644
index 0000000..9c05fa5
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/file_tasks.h
@@ -0,0 +1,219 @@
+// Copyright (c) 2012 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.
+//
+// This file provides utility functions for "file tasks".
+//
+// WHAT ARE FILE TASKS?
+//
+// File tasks are representatiosn of actions that can be performed over the
+// currently selected files from Files.app. A task can be either of:
+//
+// 1) Chrome extension or app, registered via "file_handlers" or
+// "file_browser_handlers" in manifest.json (ex. Text.app). This information
+// comes from FileBrowserHandler::GetHandlers()
+//
+// See also:
+// https://developer.chrome.com/extensions/manifest.html#file_handlers
+// https://developer.chrome.com/extensions/fileBrowserHandler.html
+//
+// 2) Built-in handlers provided from Files.app. Files.app provides lots of
+// file_browser_handlers, such as "play", "watch", "mount-archive".  These
+// built-in handlers are often handled in special manners inside Files.app.
+// This information also comes from FileBrowserHandler::GetHandlers().
+//
+// See also:
+// chrome/browser/resources/file_manager/manifest.json
+//
+// 3) Drive app, which is a hosted app (i.e. just web site), that can work
+// with Drive (ex. Pixlr Editor). This information comes from
+// drive::DriveAppRegistry.
+//
+// See also:
+// https://chrome.google.com/webstore/category/collection/drive_apps
+//
+// For example, if the user is now selecting a JPEG file, Files.app will
+// receive file tasks represented as a JSON object via
+// chrome.fileBrowserPrivate.getFileTasks() API, which look like:
+//
+// [
+//   {
+//     "driveApp": true,
+//     "iconUrl": "<app_icon_url>",
+//     "isDefault": false,
+//     "taskId": "<drive_app_id>|drive|open-with",
+//     "title": "Drive App Name (ex. Pixlr Editor)"
+//   },
+//   {
+//     "driveApp": false,
+//     "iconUrl": "chrome://extension-icon/hhaomjibdihmijegdhdafkllkbggdgoj/16/1",
+//     "isDefault": true,
+//     "taskId": "hhaomjibdihmijegdhdafkllkbggdgoj|file|gallery",
+//     "title": "__MSG_OPEN_ACTION__"
+//   }
+// ]
+//
+// The first file task is a Drive app. The second file task is a built-in
+// handler from Files.app.
+//
+// WHAT ARE TASK IDS?
+//
+// You may have noticed that "taskId" fields in the above example look
+// awakard. Apparently "taskId" encodes three types of information delimited
+// by "|". This is a weird format for something called as an ID.
+//
+// 1) Why are the three types information encoded in this way?
+//
+// It's just a historical reason. The reason is that a simple string can be
+// easily stored in user's preferences. We should stop doing this, by storing
+// this information in chrome.storage instead. crbug.com/267359.
+//
+// 2) OK, then what are the three types of information encoded here?
+//
+// The task ID encodes the folloing structure:
+//
+//     <app-id>|<task-type>|<task-action-id>
+//
+// <app-id> is either of Chrome Extension/App ID or Drive App ID. For some
+// reason, Chrome Extension/App IDs and Drive App IDs look differently. As of
+// writing, the fomer looks like "hhaomjibdihmijegdhdafkllkbggdgoj"
+// (Files.app) and the latter looks like "419782477519" (Pixlr Editor).
+//
+// <task-type> is either of
+// - "file" - Files.app built-in handler
+// - "drive" - Drive App
+// - "app" - Regular Chrome Extension/App ID
+//
+// <task-action-id> is an ID string used for identifying actions provided
+// from a single Chrome Extension/App. In other words, a single
+// Chrome/Extension can provide multiple file handlers hence each of them
+// needs to have a unique action ID.  For Drive apps, <task-action-id> is
+// always "open-with".
+//
+// HOW TASKS ARE EXECUTED?
+//
+// chrome.fileBrowserPrivate.viewFiles() is used to open a file in a browser,
+// without any handler. Browser will take care of handling the file (ex. PDF).
+//
+// chrome.fileBrowserPrivate.executeTasks() is used to open a file with a
+// handler (Chrome Extension/App or Drive App).
+//
+// Some built-in handlers such as "play" and "watch" are handled internally
+// in Files.app. "mount-archive" is handled very differently. The task
+// execution business should be simplified: crbug.com/267313
+//
+// See also:
+// chrome/browser/resources/file_manager/js/file_tasks.js
+//
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_TASKS_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_TASKS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/platform_file.h"
+#include "chrome/common/extensions/extension.h"
+
+class Browser;
+class FileBrowserHandler;
+class GURL;
+class Profile;
+
+namespace fileapi {
+class FileSystemURL;
+}
+
+namespace file_manager {
+namespace file_tasks {
+
+// Tasks are stored as a vector in order of priorities.
+typedef std::vector<const FileBrowserHandler*> FileBrowserHandlerList;
+
+// Specifies the task type for a task id that represents some file action, Drive
+// action, or Web Intent action.
+extern const char kTaskFile[];
+extern const char kTaskDrive[];
+extern const char kTaskApp[];
+
+void UpdateFileHandlerUsageStats(Profile* profile, const std::string& task_id);
+
+// Returns true if the task should be used as a fallback. Such tasks are
+// Files.app's internal handlers as well as quick office extensions.
+bool IsFallbackTask(const FileBrowserHandler* task);
+
+// Update the default file handler for the given sets of suffixes and MIME
+// types.
+void UpdateDefaultTask(Profile* profile,
+                       const std::string& task_id,
+                       const std::set<std::string>& suffixes,
+                       const std::set<std::string>& mime_types);
+
+// Returns the task ID of the default task for the given |mime_type|/|suffix|
+// combination. If it finds a MIME type match, then it prefers that over a
+// suffix match. If it a default can't be found, then it returns the empty
+// string.
+std::string GetDefaultTaskIdFromPrefs(Profile* profile,
+                                      const std::string& mime_type,
+                                      const std::string& suffix);
+
+// Generates task id for the action specified by the extension. The |task_type|
+// must be one of kTaskFile, kTaskDrive or kTaskApp.
+std::string MakeTaskID(const std::string& extension_id,
+                       const std::string& task_type,
+                       const std::string& action_id);
+
+// Extracts action, type and extension id bound to the file task ID. Either
+// |target_extension_id| or |action_id| are allowed to be NULL if caller isn't
+// interested in those values.  Returns false on failure to parse.
+//
+// See also the comment at the beginning of the file for details for how
+// "task_id" looks like.
+bool CrackTaskID(const std::string& task_id,
+                 std::string* target_extension_id,
+                 std::string* task_type,
+                 std::string* action_id);
+
+// This generates a list of default tasks (tasks set as default by the user in
+// prefs) from the |common_tasks|.
+void FindDefaultTasks(Profile* profile,
+                      const std::vector<base::FilePath>& files_list,
+                      const FileBrowserHandlerList& common_tasks,
+                      FileBrowserHandlerList* default_tasks);
+
+// This generates list of tasks common for all files in |file_list|.
+bool FindCommonTasks(Profile* profile,
+                     const std::vector<GURL>& files_list,
+                     FileBrowserHandlerList* common_tasks);
+
+// Finds a task for a file whose URL is |url| and whose path is |path|.
+// Returns default task if one is defined (The default task is the task that is
+// assigned to file browser task button by default). If default task is not
+// found, tries to match the url with one of the builtin tasks.
+bool GetTaskForURLAndPath(Profile* profile,
+                          const GURL& url,
+                          const base::FilePath& path,
+                          const FileBrowserHandler** handler);
+
+// Used for returning success or failure from task executions.
+typedef base::Callback<void(bool)> FileTaskFinishedCallback;
+
+// Executes file handler task for each element of |file_urls|.
+// Returns |false| if the execution cannot be initiated. Otherwise returns
+// |true| and then eventually calls |done| when all the files have been handled.
+// |done| can be a null callback.
+bool ExecuteFileTask(Profile* profile,
+                     const GURL& source_url,
+                     const std::string& file_browser_id,
+                     int32 tab_id,
+                     const std::string& extension_id,
+                     const std::string& task_type,
+                     const std::string& action_id,
+                     const std::vector<fileapi::FileSystemURL>& file_urls,
+                     const FileTaskFinishedCallback& done);
+
+}  // namespace file_tasks
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_TASKS_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_watcher_extensions.cc b/chrome/browser/chromeos/extensions/file_manager/file_watcher_extensions.cc
index 7dfd98e..706129e 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_watcher_extensions.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_watcher_extensions.cc
@@ -11,6 +11,7 @@
 
 using content::BrowserThread;
 
+namespace file_manager {
 namespace {
 
 // Creates a base::FilePathWatcher and starts watching at |watch_path| with
@@ -115,3 +116,5 @@
     callback.Run(false);
   }
 }
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_watcher_extensions.h b/chrome/browser/chromeos/extensions/file_manager/file_watcher_extensions.h
index 69774af..9b0ee79 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_watcher_extensions.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_watcher_extensions.h
@@ -13,6 +13,8 @@
 #include "base/files/file_path_watcher.h"
 #include "base/memory/weak_ptr.h"
 
+namespace file_manager {
+
 // This class is used to remember what extensions are watching |virtual_path|.
 class FileWatcherExtensions {
  public:
@@ -62,4 +64,6 @@
   base::WeakPtrFactory<FileWatcherExtensions> weak_ptr_factory_;
 };
 
+}  // namespace file_manager
+
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_WATCHER_EXTENSIONS_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/mounted_disk_monitor.cc b/chrome/browser/chromeos/extensions/file_manager/mounted_disk_monitor.cc
index 4628179..d59ef49 100644
--- a/chrome/browser/chromeos/extensions/file_manager/mounted_disk_monitor.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/mounted_disk_monitor.cc
@@ -12,6 +12,7 @@
 using chromeos::DBusThreadManager;
 using chromeos::disks::DiskMountManager;
 
+namespace file_manager {
 namespace {
 
 // Time span of the resuming process. All unmount events sent during this
@@ -112,3 +113,5 @@
   unmounted_while_resuming_.clear();
   is_resuming_ = false;
 }
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/mounted_disk_monitor.h b/chrome/browser/chromeos/extensions/file_manager/mounted_disk_monitor.h
index 98d7658..d4c0730 100644
--- a/chrome/browser/chromeos/extensions/file_manager/mounted_disk_monitor.h
+++ b/chrome/browser/chromeos/extensions/file_manager/mounted_disk_monitor.h
@@ -14,6 +14,8 @@
 #include "chromeos/dbus/power_manager_client.h"
 #include "chromeos/disks/disk_mount_manager.h"
 
+namespace file_manager {
+
 // Observes PowerManager and updates its state when the system suspends and
 // resumes. After the system resumes it will stay in "is_resuming" state for
 // couple of seconds. This is to give DiskManager time to process device
@@ -68,4 +70,6 @@
   DISALLOW_COPY_AND_ASSIGN(MountedDiskMonitor);
 };
 
+}  // namespace file_manager
+
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_MOUNTED_DISK_MONITOR_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
index f9db372..d0779f9 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
@@ -25,13 +25,15 @@
 void LoggedAsyncExtensionFunction::SendResponse(bool success) {
   int64 elapsed = (base::Time::Now() - start_time_).InMilliseconds();
   if (log_on_completion_) {
-    drive::util::Log("%s[%d] %s. (elapsed time: %sms)",
+    drive::util::Log(logging::LOG_INFO,
+                     "%s[%d] %s. (elapsed time: %sms)",
                      name().c_str(),
                      request_id(),
                      success ? "succeeded" : "failed",
                      base::Int64ToString(elapsed).c_str());
   } else if (elapsed >= kSlowOperationThresholdMs) {
     drive::util::Log(
+        logging::LOG_WARNING,
         "PEFORMANCE WARNING: %s[%d] was slow. (elapsed time: %sms)",
         name().c_str(),
         request_id(),
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
new file mode 100644
index 0000000..b5370ae
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
@@ -0,0 +1,110 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_dialog.h"
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
+#include "chrome/browser/ui/views/select_file_dialog_extension.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/shell_dialogs/selected_file_info.h"
+
+using content::BrowserThread;
+
+namespace file_manager {
+
+CancelFileDialogFunction::CancelFileDialogFunction() {
+}
+
+CancelFileDialogFunction::~CancelFileDialogFunction() {
+}
+
+bool CancelFileDialogFunction::RunImpl() {
+  int32 tab_id = util::GetTabId(dispatcher());
+  SelectFileDialogExtension::OnFileSelectionCanceled(tab_id);
+  SendResponse(true);
+  return true;
+}
+
+SelectFileFunction::SelectFileFunction() {
+}
+
+SelectFileFunction::~SelectFileFunction() {
+}
+
+bool SelectFileFunction::RunImpl() {
+  if (args_->GetSize() != 4) {
+    return false;
+  }
+  std::string file_url;
+  args_->GetString(0, &file_url);
+  std::vector<GURL> file_paths;
+  file_paths.push_back(GURL(file_url));
+  bool for_opening = false;
+  args_->GetBoolean(2, &for_opening);
+
+  util::GetSelectedFileInfo(
+      render_view_host(),
+      profile(),
+      file_paths,
+      for_opening,
+      base::Bind(&SelectFileFunction::GetSelectedFileInfoResponse, this));
+  return true;
+}
+
+void SelectFileFunction::GetSelectedFileInfoResponse(
+    const std::vector<ui::SelectedFileInfo>& files) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (files.size() != 1) {
+    SendResponse(false);
+    return;
+  }
+  int index;
+  args_->GetInteger(1, &index);
+  int32 tab_id = util::GetTabId(dispatcher());
+  SelectFileDialogExtension::OnFileSelected(tab_id, files[0], index);
+  SendResponse(true);
+}
+
+SelectFilesFunction::SelectFilesFunction() {
+}
+
+SelectFilesFunction::~SelectFilesFunction() {
+}
+
+bool SelectFilesFunction::RunImpl() {
+  if (args_->GetSize() != 2) {
+    return false;
+  }
+
+  ListValue* path_list = NULL;
+  args_->GetList(0, &path_list);
+  DCHECK(path_list);
+
+  std::string virtual_path;
+  size_t len = path_list->GetSize();
+  std::vector<GURL> file_urls;
+  file_urls.reserve(len);
+  for (size_t i = 0; i < len; ++i) {
+    path_list->GetString(i, &virtual_path);
+    file_urls.push_back(GURL(virtual_path));
+  }
+
+  util::GetSelectedFileInfo(
+      render_view_host(),
+      profile(),
+      file_urls,
+      true,  // for_opening
+      base::Bind(&SelectFilesFunction::GetSelectedFileInfoResponse, this));
+  return true;
+}
+
+void SelectFilesFunction::GetSelectedFileInfoResponse(
+    const std::vector<ui::SelectedFileInfo>& files) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  int32 tab_id = util::GetTabId(dispatcher());
+  SelectFileDialogExtension::OnMultiFilesSelected(tab_id, files);
+  SendResponse(true);
+}
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.h b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.h
new file mode 100644
index 0000000..ba8cdf1
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.h
@@ -0,0 +1,76 @@
+// Copyright 2013 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.
+//
+// This file provides API functions for the file manager to act as the file
+// dialog for opening and saving files.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DIALOG_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DIALOG_H_
+
+#include <vector>
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
+
+namespace ui {
+struct SelectedFileInfo;
+}
+
+namespace file_manager {
+
+// Cancel file selection Dialog.  Closes the dialog window.
+class CancelFileDialogFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.cancelDialog",
+                             FILEBROWSERPRIVATE_CANCELDIALOG)
+
+  CancelFileDialogFunction();
+
+ protected:
+  virtual ~CancelFileDialogFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+class SelectFileFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.selectFile",
+                             FILEBROWSERPRIVATE_SELECTFILE)
+
+  SelectFileFunction();
+
+ protected:
+  virtual ~SelectFileFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  // A callback method to handle the result of GetSelectedFileInfo.
+  void GetSelectedFileInfoResponse(
+      const std::vector<ui::SelectedFileInfo>& files);
+};
+
+// Select multiple files.  Closes the dialog window.
+class SelectFilesFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.selectFiles",
+                             FILEBROWSERPRIVATE_SELECTFILES)
+
+  SelectFilesFunction();
+
+ protected:
+  virtual ~SelectFilesFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  // A callback method to handle the result of GetSelectedFileInfo.
+  void GetSelectedFileInfoResponse(
+      const std::vector<ui::SelectedFileInfo>& files);
+};
+
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DIALOG_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
new file mode 100644
index 0000000..c963b42
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -0,0 +1,763 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_drive.h"
+
+#include "base/prefs/pref_service.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/chromeos/drive/drive_app_registry.h"
+#include "chrome/browser/chromeos/drive/drive_integration_service.h"
+#include "chrome/browser/chromeos/drive/logging.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
+#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
+#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_view_host.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+using content::BrowserThread;
+
+namespace file_manager {
+namespace {
+
+
+// List of connection types of drive.
+// Keep this in sync with the DriveConnectionType in volume_manager.js.
+const char kDriveConnectionTypeOffline[] = "offline";
+const char kDriveConnectionTypeMetered[] = "metered";
+const char kDriveConnectionTypeOnline[] = "online";
+
+
+// List of reasons of kDriveConnectionType*.
+// Keep this in sync with the DriveConnectionReason in volume_manager.js.
+const char kDriveConnectionReasonNotReady[] = "not_ready";
+const char kDriveConnectionReasonNoNetwork[] = "no_network";
+const char kDriveConnectionReasonNoService[] = "no_service";
+
+// Does nothing with a bool parameter. Used as a placeholder for calling
+// ClearCacheAndRemountFileSystem(). TODO(yoshiki): Handle an error from
+// ClearCacheAndRemountFileSystem() properly: http://crbug.com/140511.
+void DoNothingWithBool(bool /* success */) {
+}
+
+// Copies properties from |entry_proto| to |property_dict|.
+void FillDriveEntryPropertiesValue(
+    const drive::ResourceEntry& entry_proto,
+    DictionaryValue* property_dict) {
+  property_dict->SetBoolean("sharedWithMe", entry_proto.shared_with_me());
+
+  if (!entry_proto.has_file_specific_info())
+    return;
+
+  const drive::FileSpecificInfo& file_specific_info =
+      entry_proto.file_specific_info();
+
+  property_dict->SetString("thumbnailUrl", file_specific_info.thumbnail_url());
+  property_dict->SetBoolean("isHosted",
+                            file_specific_info.is_hosted_document());
+  property_dict->SetString("contentMimeType",
+                           file_specific_info.content_mime_type());
+}
+
+}  // namespace
+
+GetDriveEntryPropertiesFunction::GetDriveEntryPropertiesFunction() {
+}
+
+GetDriveEntryPropertiesFunction::~GetDriveEntryPropertiesFunction() {
+}
+
+bool GetDriveEntryPropertiesFunction::RunImpl() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  std::string file_url_str;
+  if (args_->GetSize() != 1 || !args_->GetString(0, &file_url_str))
+    return false;
+
+  GURL file_url = GURL(file_url_str);
+  file_path_ = drive::util::ExtractDrivePath(
+      util::GetLocalPathFromURL(render_view_host(), profile(), file_url));
+
+  properties_.reset(new base::DictionaryValue);
+  properties_->SetString("fileUrl", file_url.spec());
+
+  // Start getting the file info.
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  // |integration_service| is NULL if Drive is disabled.
+  if (!integration_service) {
+    CompleteGetFileProperties(drive::FILE_ERROR_FAILED);
+    return true;
+  }
+
+  integration_service->file_system()->GetResourceEntryByPath(
+      file_path_,
+      base::Bind(&GetDriveEntryPropertiesFunction::OnGetFileInfo, this));
+  return true;
+}
+
+void GetDriveEntryPropertiesFunction::OnGetFileInfo(
+    drive::FileError error,
+    scoped_ptr<drive::ResourceEntry> entry) {
+  DCHECK(properties_);
+
+  if (error != drive::FILE_ERROR_OK) {
+    CompleteGetFileProperties(error);
+    return;
+  }
+  DCHECK(entry);
+
+  FillDriveEntryPropertiesValue(*entry, properties_.get());
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  // |integration_service| is NULL if Drive is disabled.
+  if (!integration_service) {
+    CompleteGetFileProperties(drive::FILE_ERROR_FAILED);
+    return;
+  }
+
+  // The properties meaningful for directories are already filled in
+  // FillDriveEntryPropertiesValue().
+  if (entry.get() && !entry->has_file_specific_info()) {
+    CompleteGetFileProperties(error);
+    return;
+  }
+
+  const drive::FileSpecificInfo& file_specific_info =
+      entry->file_specific_info();
+
+  // Get drive WebApps that can accept this file.
+  ScopedVector<drive::DriveAppInfo> drive_apps;
+  integration_service->drive_app_registry()->GetAppsForFile(
+      file_path_, file_specific_info.content_mime_type(), &drive_apps);
+  if (!drive_apps.empty()) {
+    std::string default_task_id = file_tasks::GetDefaultTaskIdFromPrefs(
+        profile_,
+        file_specific_info.content_mime_type(),
+        file_path_.Extension());
+    std::string default_app_id;
+    file_tasks::CrackTaskID(
+        default_task_id, &default_app_id, NULL, NULL);
+
+    ListValue* apps = new ListValue();
+    properties_->Set("driveApps", apps);
+    for (ScopedVector<drive::DriveAppInfo>::const_iterator it =
+             drive_apps.begin();
+         it != drive_apps.end(); ++it) {
+      const drive::DriveAppInfo* app_info = *it;
+      DictionaryValue* app = new DictionaryValue();
+      app->SetString("appId", app_info->app_id);
+      app->SetString("appName", app_info->app_name);
+      GURL app_icon = util::FindPreferredIcon(app_info->app_icons,
+                                              util::kPreferredIconSize);
+      if (!app_icon.is_empty())
+        app->SetString("appIcon", app_icon.spec());
+      GURL doc_icon = util::FindPreferredIcon(app_info->document_icons,
+                                              util::kPreferredIconSize);
+      if (!doc_icon.is_empty())
+        app->SetString("docIcon", doc_icon.spec());
+      app->SetString("objectType", app_info->object_type);
+      app->SetBoolean("isPrimary", default_app_id == app_info->app_id);
+      apps->Append(app);
+    }
+  }
+
+  integration_service->file_system()->GetCacheEntryByResourceId(
+      entry->resource_id(),
+      base::Bind(&GetDriveEntryPropertiesFunction::CacheStateReceived, this));
+}
+
+void GetDriveEntryPropertiesFunction::CacheStateReceived(
+    bool /* success */,
+    const drive::FileCacheEntry& cache_entry) {
+  // In case of an error (i.e. success is false), cache_entry.is_*() all
+  // returns false.
+  properties_->SetBoolean("isPinned", cache_entry.is_pinned());
+  properties_->SetBoolean("isPresent", cache_entry.is_present());
+  properties_->SetBoolean("isDirty", cache_entry.is_dirty());
+
+  CompleteGetFileProperties(drive::FILE_ERROR_OK);
+}
+
+void GetDriveEntryPropertiesFunction::CompleteGetFileProperties(
+    drive::FileError error) {
+  if (error != drive::FILE_ERROR_OK)
+    properties_->SetInteger("errorCode", error);
+  SetResult(properties_.release());
+  SendResponse(true);
+}
+
+PinDriveFileFunction::PinDriveFileFunction() {
+}
+
+PinDriveFileFunction::~PinDriveFileFunction() {
+}
+
+bool PinDriveFileFunction::RunImpl() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  std::string url;
+  bool set_pin = false;
+  if (args_->GetSize() != 2 ||
+      !args_->GetString(0, &url) ||
+      !args_->GetBoolean(1, &set_pin))
+    return false;
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  drive::FileSystemInterface* file_system =
+      integration_service ? integration_service->file_system() : NULL;
+  if (!file_system)  // |file_system| is NULL if Drive is disabled.
+    return false;
+
+  base::FilePath drive_path =
+      drive::util::ExtractDrivePath(
+          util::GetLocalPathFromURL(render_view_host(), profile(), GURL(url)));
+  if (set_pin) {
+    file_system->Pin(drive_path,
+                     base::Bind(&PinDriveFileFunction::OnPinStateSet, this));
+  } else {
+    file_system->Unpin(drive_path,
+                       base::Bind(&PinDriveFileFunction::OnPinStateSet, this));
+  }
+  return true;
+}
+
+void PinDriveFileFunction::OnPinStateSet(drive::FileError error) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (error == drive::FILE_ERROR_OK) {
+    SendResponse(true);
+  } else {
+    error_ = drive::FileErrorToString(error);
+    SendResponse(false);
+  }
+}
+
+GetDriveFilesFunction::GetDriveFilesFunction()
+    : local_paths_(NULL) {
+}
+
+GetDriveFilesFunction::~GetDriveFilesFunction() {
+}
+
+bool GetDriveFilesFunction::RunImpl() {
+  ListValue* file_urls_as_strings = NULL;
+  if (!args_->GetList(0, &file_urls_as_strings))
+    return false;
+
+  // Convert the list of strings to a list of GURLs.
+  for (size_t i = 0; i < file_urls_as_strings->GetSize(); ++i) {
+    std::string file_url_as_string;
+    if (!file_urls_as_strings->GetString(i, &file_url_as_string))
+      return false;
+    const base::FilePath path = util::GetLocalPathFromURL(
+        render_view_host(), profile(), GURL(file_url_as_string));
+    DCHECK(drive::util::IsUnderDriveMountPoint(path));
+    base::FilePath drive_path = drive::util::ExtractDrivePath(path);
+    remaining_drive_paths_.push(drive_path);
+  }
+
+  local_paths_ = new ListValue;
+  GetFileOrSendResponse();
+  return true;
+}
+
+void GetDriveFilesFunction::GetFileOrSendResponse() {
+  // Send the response if all files are obtained.
+  if (remaining_drive_paths_.empty()) {
+    SetResult(local_paths_);
+    SendResponse(true);
+    return;
+  }
+
+  // Get the file on the top of the queue.
+  base::FilePath drive_path = remaining_drive_paths_.front();
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  // |integration_service| is NULL if Drive is disabled.
+  if (!integration_service) {
+    OnFileReady(drive::FILE_ERROR_FAILED, drive_path,
+                scoped_ptr<drive::ResourceEntry>());
+    return;
+  }
+
+  integration_service->file_system()->GetFileByPath(
+      drive_path,
+      base::Bind(&GetDriveFilesFunction::OnFileReady, this));
+}
+
+
+void GetDriveFilesFunction::OnFileReady(
+    drive::FileError error,
+    const base::FilePath& local_path,
+    scoped_ptr<drive::ResourceEntry> entry) {
+  base::FilePath drive_path = remaining_drive_paths_.front();
+
+  if (error == drive::FILE_ERROR_OK) {
+    local_paths_->Append(new base::StringValue(local_path.value()));
+    DVLOG(1) << "Got " << drive_path.value() << " as " << local_path.value();
+
+    // TODO(benchan): If the file is a hosted document, a temporary JSON file
+    // is created to represent the document. The JSON file is not cached and
+    // should be deleted after use. We need to somehow communicate with
+    // file_manager.js to manage the lifetime of the temporary file.
+    // See crosbug.com/28058.
+  } else {
+    local_paths_->Append(new base::StringValue(""));
+    DVLOG(1) << "Failed to get " << drive_path.value()
+             << " with error code: " << error;
+  }
+
+  remaining_drive_paths_.pop();
+
+  // Start getting the next file.
+  GetFileOrSendResponse();
+}
+
+CancelFileTransfersFunction::CancelFileTransfersFunction() {
+}
+
+CancelFileTransfersFunction::~CancelFileTransfersFunction() {
+}
+
+bool CancelFileTransfersFunction::RunImpl() {
+  ListValue* url_list = NULL;
+  if (!args_->GetList(0, &url_list))
+    return false;
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  // |integration_service| is NULL if Drive is disabled.
+  if (!integration_service)
+    return false;
+
+  // Create the mapping from file path to job ID.
+  drive::JobListInterface* job_list = integration_service->job_list();
+  DCHECK(job_list);
+  std::vector<drive::JobInfo> jobs = job_list->GetJobInfoList();
+
+  typedef std::map<base::FilePath, std::vector<drive::JobID> > PathToIdMap;
+  PathToIdMap path_to_id_map;
+  for (size_t i = 0; i < jobs.size(); ++i) {
+    if (drive::IsActiveFileTransferJobInfo(jobs[i]))
+      path_to_id_map[jobs[i].file_path].push_back(jobs[i].job_id);
+  }
+
+  // Cancel by Job ID.
+  scoped_ptr<ListValue> responses(new ListValue());
+  for (size_t i = 0; i < url_list->GetSize(); ++i) {
+    std::string url_as_string;
+    url_list->GetString(i, &url_as_string);
+
+    base::FilePath file_path = util::GetLocalPathFromURL(
+        render_view_host(), profile(), GURL(url_as_string));
+    if (file_path.empty())
+      continue;
+
+    DCHECK(drive::util::IsUnderDriveMountPoint(file_path));
+    file_path = drive::util::ExtractDrivePath(file_path);
+    scoped_ptr<DictionaryValue> result(new DictionaryValue());
+
+    // Cancel all the jobs for the file.
+    PathToIdMap::iterator it = path_to_id_map.find(file_path);
+    if (it != path_to_id_map.end()) {
+      for (size_t i = 0; i < it->second.size(); ++i)
+        job_list->CancelJob(it->second[i]);
+    }
+    result->SetBoolean("canceled", it != path_to_id_map.end());
+    // TODO(kinaba): simplify cancelFileTransfer() to take single URL each time,
+    // and eliminate this field; it is just returning a copy of the argument.
+    result->SetString("fileUrl", url_as_string);
+    responses->Append(result.release());
+  }
+  SetResult(responses.release());
+  SendResponse(true);
+  return true;
+}
+
+TransferFileFunction::TransferFileFunction() {
+}
+
+TransferFileFunction::~TransferFileFunction() {
+}
+
+bool TransferFileFunction::RunImpl() {
+  std::string source_file_url;
+  std::string destination_file_url;
+  if (!args_->GetString(0, &source_file_url) ||
+      !args_->GetString(1, &destination_file_url)) {
+    return false;
+  }
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  // |integration_service| is NULL if Drive is disabled.
+  if (!integration_service)
+    return false;
+
+  base::FilePath source_file = util::GetLocalPathFromURL(
+      render_view_host(), profile(), GURL(source_file_url));
+  base::FilePath destination_file = util::GetLocalPathFromURL(
+      render_view_host(), profile(), GURL(destination_file_url));
+  if (source_file.empty() || destination_file.empty())
+    return false;
+
+  bool source_file_under_drive =
+      drive::util::IsUnderDriveMountPoint(source_file);
+  bool destination_file_under_drive =
+      drive::util::IsUnderDriveMountPoint(destination_file);
+
+  if (source_file_under_drive && !destination_file_under_drive) {
+    // Transfer a file from drive to local file system.
+    source_file = drive::util::ExtractDrivePath(source_file);
+    integration_service->file_system()->TransferFileFromRemoteToLocal(
+        source_file,
+        destination_file,
+        base::Bind(&TransferFileFunction::OnTransferCompleted, this));
+  } else if (!source_file_under_drive && destination_file_under_drive) {
+    // Transfer a file from local to Drive file system
+    destination_file = drive::util::ExtractDrivePath(destination_file);
+    integration_service->file_system()->TransferFileFromLocalToRemote(
+        source_file,
+        destination_file,
+        base::Bind(&TransferFileFunction::OnTransferCompleted, this));
+  } else {
+    // Local-to-local or Drive-to-Drive file transfers should be done via
+    // FileEntry.copyTo in the File API and are thus not supported here.
+    NOTREACHED();
+    SendResponse(false);
+  }
+  return true;
+}
+
+void TransferFileFunction::OnTransferCompleted(drive::FileError error) {
+  if (error == drive::FILE_ERROR_OK) {
+    SendResponse(true);
+  } else {
+    error_ = base::StringPrintf("%d", static_cast<int>(
+        fileapi::PlatformFileErrorToWebFileError(
+            drive::FileErrorToPlatformError(error))));
+    SendResponse(false);
+  }
+}
+
+SearchDriveFunction::SearchDriveFunction() {
+}
+
+SearchDriveFunction::~SearchDriveFunction() {
+}
+
+bool SearchDriveFunction::RunImpl() {
+  DictionaryValue* search_params;
+  if (!args_->GetDictionary(0, &search_params))
+    return false;
+
+  std::string query;
+  if (!search_params->GetString("query", &query))
+    return false;
+
+  std::string next_feed;
+  if (!search_params->GetString("nextFeed", &next_feed))
+    return false;
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  // |integration_service| is NULL if Drive is disabled.
+  if (!integration_service || !integration_service->file_system())
+    return false;
+
+  integration_service->file_system()->Search(
+      query, GURL(next_feed),
+      base::Bind(&SearchDriveFunction::OnSearch, this));
+  return true;
+}
+
+void SearchDriveFunction::OnSearch(
+    drive::FileError error,
+    const GURL& next_feed,
+    scoped_ptr<std::vector<drive::SearchResultInfo> > results) {
+  if (error != drive::FILE_ERROR_OK) {
+    SendResponse(false);
+    return;
+  }
+
+  DCHECK(results.get());
+
+  base::ListValue* entries = new ListValue();
+
+  // Convert Drive files to something File API stack can understand.
+  GURL origin_url = source_url_.GetOrigin();
+  fileapi::FileSystemType file_system_type = fileapi::kFileSystemTypeExternal;
+  GURL file_system_root_url =
+      fileapi::GetFileSystemRootURI(origin_url, file_system_type);
+  std::string file_system_name =
+      fileapi::GetFileSystemName(origin_url, file_system_type);
+  for (size_t i = 0; i < results->size(); ++i) {
+    DictionaryValue* entry = new DictionaryValue();
+    entry->SetString("fileSystemName", file_system_name);
+    entry->SetString("fileSystemRoot", file_system_root_url.spec());
+    entry->SetString("fileFullPath", "/" + results->at(i).path.value());
+    entry->SetBoolean("fileIsDirectory",
+                      results->at(i).entry.file_info().is_directory());
+    entries->Append(entry);
+  }
+
+  base::DictionaryValue* result = new DictionaryValue();
+  result->Set("entries", entries);
+  result->SetString("nextFeed", next_feed.spec());
+
+  SetResult(result);
+  SendResponse(true);
+}
+
+SearchDriveMetadataFunction::SearchDriveMetadataFunction() {
+}
+
+SearchDriveMetadataFunction::~SearchDriveMetadataFunction() {
+}
+
+bool SearchDriveMetadataFunction::RunImpl() {
+  DictionaryValue* search_params;
+  if (!args_->GetDictionary(0, &search_params))
+    return false;
+
+  std::string query;
+  if (!search_params->GetString("query", &query))
+    return false;
+
+  std::string types;
+  if (!search_params->GetString("types", &types))
+    return false;
+
+  int max_results = 0;
+  if (!search_params->GetInteger("maxResults", &max_results))
+    return false;
+
+  drive::util::Log(logging::LOG_INFO,
+                   "%s[%d] called. (types: '%s', maxResults: '%d')",
+                   name().c_str(),
+                   request_id(),
+                   types.c_str(),
+                   max_results);
+  set_log_on_completion(true);
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  // |integration_service| is NULL if Drive is disabled.
+  if (!integration_service || !integration_service->file_system())
+    return false;
+
+  int options = drive::SEARCH_METADATA_ALL;
+  // TODO(hirono): Switch to the JSON scheme compiler. http://crbug.com/241693
+  if (types == "EXCLUDE_DIRECTORIES")
+    options = drive::SEARCH_METADATA_EXCLUDE_DIRECTORIES;
+  else if (types == "SHARED_WITH_ME")
+    options = drive::SEARCH_METADATA_SHARED_WITH_ME;
+  else if (types == "OFFLINE")
+    options = drive::SEARCH_METADATA_OFFLINE;
+  else
+    DCHECK_EQ("ALL", types);
+
+  integration_service->file_system()->SearchMetadata(
+      query,
+      options,
+      max_results,
+      base::Bind(&SearchDriveMetadataFunction::OnSearchMetadata, this));
+  return true;
+}
+
+void SearchDriveMetadataFunction::OnSearchMetadata(
+    drive::FileError error,
+    scoped_ptr<drive::MetadataSearchResultVector> results) {
+  if (error != drive::FILE_ERROR_OK) {
+    SendResponse(false);
+    return;
+  }
+
+  DCHECK(results.get());
+
+  base::ListValue* results_list = new ListValue();
+
+  // Convert Drive files to something File API stack can understand.  See
+  // file_browser_handler_custom_bindings.cc and
+  // file_browser_private_custom_bindings.js for how this is magically
+  // converted to a FileEntry.
+  GURL origin_url = source_url_.GetOrigin();
+  fileapi::FileSystemType file_system_type = fileapi::kFileSystemTypeExternal;
+  GURL file_system_root_url =
+      fileapi::GetFileSystemRootURI(origin_url, file_system_type);
+  std::string file_system_name =
+      fileapi::GetFileSystemName(origin_url, file_system_type);
+  for (size_t i = 0; i < results->size(); ++i) {
+    DictionaryValue* result_dict = new DictionaryValue();
+
+    // FileEntry fields.
+    DictionaryValue* entry = new DictionaryValue();
+    entry->SetString("fileSystemName", file_system_name);
+    entry->SetString("fileSystemRoot", file_system_root_url.spec());
+    entry->SetString("fileFullPath", "/" + results->at(i).path.value());
+    entry->SetBoolean("fileIsDirectory",
+                      results->at(i).entry.file_info().is_directory());
+
+    result_dict->Set("entry", entry);
+    result_dict->SetString("highlightedBaseName",
+                           results->at(i).highlighted_base_name);
+    results_list->Append(result_dict);
+  }
+
+  SetResult(results_list);
+  SendResponse(true);
+}
+
+ClearDriveCacheFunction::ClearDriveCacheFunction() {
+}
+
+ClearDriveCacheFunction::~ClearDriveCacheFunction() {
+}
+
+bool ClearDriveCacheFunction::RunImpl() {
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  // |integration_service| is NULL if Drive is disabled.
+  if (!integration_service || !integration_service->file_system())
+    return false;
+
+  // TODO(yoshiki): Receive a callback from JS-side and pass it to
+  // ClearCacheAndRemountFileSystem(). http://crbug.com/140511
+  integration_service->ClearCacheAndRemountFileSystem(
+      base::Bind(&DoNothingWithBool));
+
+  SendResponse(true);
+  return true;
+}
+
+GetDriveConnectionStateFunction::GetDriveConnectionStateFunction() {
+}
+
+GetDriveConnectionStateFunction::~GetDriveConnectionStateFunction() {
+}
+
+bool GetDriveConnectionStateFunction::RunImpl() {
+  scoped_ptr<DictionaryValue> value(new DictionaryValue());
+  scoped_ptr<ListValue> reasons(new ListValue());
+
+  std::string type_string;
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+
+  bool ready = integration_service &&
+      integration_service->drive_service()->CanSendRequest();
+  bool is_connection_cellular =
+      net::NetworkChangeNotifier::IsConnectionCellular(
+          net::NetworkChangeNotifier::GetConnectionType());
+
+  if (net::NetworkChangeNotifier::IsOffline() || !ready) {
+    type_string = kDriveConnectionTypeOffline;
+    if (net::NetworkChangeNotifier::IsOffline())
+      reasons->AppendString(kDriveConnectionReasonNoNetwork);
+    if (!ready)
+      reasons->AppendString(kDriveConnectionReasonNotReady);
+    if (!integration_service)
+      reasons->AppendString(kDriveConnectionReasonNoService);
+  } else if (
+      is_connection_cellular &&
+      profile_->GetPrefs()->GetBoolean(prefs::kDisableDriveOverCellular)) {
+    type_string = kDriveConnectionTypeMetered;
+  } else {
+    type_string = kDriveConnectionTypeOnline;
+  }
+
+  value->SetString("type", type_string);
+  value->Set("reasons", reasons.release());
+  SetResult(value.release());
+
+  drive::util::Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
+  return true;
+}
+
+RequestAccessTokenFunction::RequestAccessTokenFunction() {
+}
+
+RequestAccessTokenFunction::~RequestAccessTokenFunction() {
+}
+
+bool RequestAccessTokenFunction::RunImpl() {
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  bool refresh;
+  args_->GetBoolean(0, &refresh);
+
+  if (!integration_service) {
+    SetResult(new base::StringValue(""));
+    SendResponse(true);
+    return true;
+  }
+
+  // If refreshing is requested, then clear the token to refetch it.
+  if (refresh)
+    integration_service->drive_service()->ClearAccessToken();
+
+  // Retrieve the cached auth token (if available), otherwise the AuthService
+  // instance will try to refetch it.
+  integration_service->drive_service()->RequestAccessToken(
+      base::Bind(&RequestAccessTokenFunction::OnAccessTokenFetched, this));
+  return true;
+}
+
+void RequestAccessTokenFunction::OnAccessTokenFetched(
+    google_apis::GDataErrorCode code, const std::string& access_token) {
+  SetResult(new base::StringValue(access_token));
+  SendResponse(true);
+}
+
+GetShareUrlFunction::GetShareUrlFunction() {
+}
+
+GetShareUrlFunction::~GetShareUrlFunction() {
+}
+
+bool GetShareUrlFunction::RunImpl() {
+  std::string file_url;
+  if (!args_->GetString(0, &file_url))
+    return false;
+
+  const base::FilePath path = util::GetLocalPathFromURL(
+      render_view_host(), profile(), GURL(file_url));
+  DCHECK(drive::util::IsUnderDriveMountPoint(path));
+
+  base::FilePath drive_path = drive::util::ExtractDrivePath(path);
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  // |integration_service| is NULL if Drive is disabled.
+  if (!integration_service)
+    return false;
+
+  integration_service->file_system()->GetShareUrl(
+      drive_path,
+      util::GetFileBrowserExtensionUrl(),  // embed origin
+      base::Bind(&GetShareUrlFunction::OnGetShareUrl, this));
+  return true;
+}
+
+
+void GetShareUrlFunction::OnGetShareUrl(drive::FileError error,
+                                        const GURL& share_url) {
+  if (error != drive::FILE_ERROR_OK) {
+    error_ = "Share Url for this item is not available.";
+    SendResponse(false);
+    return;
+  }
+
+  SetResult(new base::StringValue(share_url.spec()));
+  SendResponse(true);
+}
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
new file mode 100644
index 0000000..cf79de3
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
@@ -0,0 +1,248 @@
+// Copyright 2013 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.
+//
+// This file provides Drive specific API functions.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DRIVE_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DRIVE_H_
+
+#include "chrome/browser/chromeos/drive/file_errors.h"
+#include "chrome/browser/chromeos/drive/file_system_interface.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
+
+namespace drive {
+class FileCacheEntry;
+class ResourceEntry;
+struct DriveAppInfo;
+struct SearchResultInfo;
+}
+
+namespace file_manager {
+
+// Retrieves property information for an entry and returns it as a dictionary.
+// On error, returns a dictionary with the key "error" set to the error number
+// (drive::FileError).
+class GetDriveEntryPropertiesFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getDriveEntryProperties",
+                             FILEBROWSERPRIVATE_GETDRIVEFILEPROPERTIES)
+
+  GetDriveEntryPropertiesFunction();
+
+ protected:
+  virtual ~GetDriveEntryPropertiesFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  void OnGetFileInfo(drive::FileError error,
+                     scoped_ptr<drive::ResourceEntry> entry);
+
+  void CacheStateReceived(bool success,
+                          const drive::FileCacheEntry& cache_entry);
+
+  void CompleteGetFileProperties(drive::FileError error);
+
+  base::FilePath file_path_;
+  scoped_ptr<base::DictionaryValue> properties_;
+};
+
+// Implements the chrome.fileBrowserPrivate.pinDriveFile method.
+class PinDriveFileFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.pinDriveFile",
+                             FILEBROWSERPRIVATE_PINDRIVEFILE)
+
+  PinDriveFileFunction();
+
+ protected:
+  virtual ~PinDriveFileFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  // Callback for RunImpl().
+  void OnPinStateSet(drive::FileError error);
+};
+
+// Get drive files for the given list of file URLs. Initiate downloading of
+// drive files if these are not cached. Return a list of local file names.
+// This function puts empty strings instead of local paths for files could
+// not be obtained. For instance, this can happen if the user specifies a new
+// file name to save a file on drive. There may be other reasons to fail. The
+// file manager should check if the local paths returned from getDriveFiles()
+// contain empty paths.
+// TODO(satorux): Should we propagate error types to the JavaScript layer?
+class GetDriveFilesFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getDriveFiles",
+                             FILEBROWSERPRIVATE_GETDRIVEFILES)
+
+  GetDriveFilesFunction();
+
+ protected:
+  virtual ~GetDriveFilesFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  // Gets the file on the top of the |remaining_drive_paths_| or sends the
+  // response if the queue is empty.
+  void GetFileOrSendResponse();
+
+  // Called by FileSystem::GetFile(). Pops the file from
+  // |remaining_drive_paths_|, and calls GetFileOrSendResponse().
+  void OnFileReady(drive::FileError error,
+                   const base::FilePath& local_path,
+                   scoped_ptr<drive::ResourceEntry> entry);
+
+  std::queue<base::FilePath> remaining_drive_paths_;
+  ListValue* local_paths_;
+};
+
+// Implements the chrome.fileBrowserPrivate.cancelFileTransfers method.
+class CancelFileTransfersFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.cancelFileTransfers",
+                             FILEBROWSERPRIVATE_CANCELFILETRANSFERS)
+
+  CancelFileTransfersFunction();
+
+ protected:
+  virtual ~CancelFileTransfersFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.transferFile method.
+class TransferFileFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.transferFile",
+                             FILEBROWSERPRIVATE_TRANSFERFILE)
+
+  TransferFileFunction();
+
+ protected:
+  virtual ~TransferFileFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  // Helper callback for handling response from FileSystem::TransferFile().
+  void OnTransferCompleted(drive::FileError error);
+};
+
+class SearchDriveFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.searchDrive",
+                             FILEBROWSERPRIVATE_SEARCHDRIVE)
+
+  SearchDriveFunction();
+
+ protected:
+  virtual ~SearchDriveFunction();
+
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  // Callback for Search().
+  void OnSearch(drive::FileError error,
+                const GURL& next_feed,
+                scoped_ptr<std::vector<drive::SearchResultInfo> > result_paths);
+};
+
+// Similar to SearchDriveFunction but this one is used for searching drive
+// metadata which is stored locally.
+class SearchDriveMetadataFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.searchDriveMetadata",
+                             FILEBROWSERPRIVATE_SEARCHDRIVEMETADATA)
+
+  SearchDriveMetadataFunction();
+
+ protected:
+  virtual ~SearchDriveMetadataFunction();
+
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  // Callback for SearchMetadata();
+  void OnSearchMetadata(drive::FileError error,
+                        scoped_ptr<drive::MetadataSearchResultVector> results);
+};
+
+class ClearDriveCacheFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.clearDriveCache",
+                             FILEBROWSERPRIVATE_CLEARDRIVECACHE)
+
+  ClearDriveCacheFunction();
+
+ protected:
+  virtual ~ClearDriveCacheFunction();
+
+  virtual bool RunImpl() OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.getDriveConnectionState method.
+class GetDriveConnectionStateFunction : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION(
+      "fileBrowserPrivate.getDriveConnectionState",
+      FILEBROWSERPRIVATE_GETDRIVECONNECTIONSTATE);
+
+  GetDriveConnectionStateFunction();
+
+ protected:
+  virtual ~GetDriveConnectionStateFunction();
+
+  virtual bool RunImpl() OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.requestAccessToken method.
+class RequestAccessTokenFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.requestAccessToken",
+                             FILEBROWSERPRIVATE_REQUESTACCESSTOKEN)
+
+  RequestAccessTokenFunction();
+
+ protected:
+  virtual ~RequestAccessTokenFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+  // Callback with a cached auth token (if available) or a fetched one.
+  void OnAccessTokenFetched(google_apis::GDataErrorCode code,
+                            const std::string& access_token);
+};
+
+// Implements the chrome.fileBrowserPrivate.getShareUrl method.
+class GetShareUrlFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getShareUrl",
+                             FILEBROWSERPRIVATE_GETSHAREURL)
+
+  GetShareUrlFunction();
+
+ protected:
+  virtual ~GetShareUrlFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+  // Callback with an url to the sharing dialog as |share_url|, called by
+  // FileSystem::GetShareUrl.
+  void OnGetShareUrl(drive::FileError error, const GURL& share_url);
+};
+
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DRIVE_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
new file mode 100644
index 0000000..61bf6ac
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
@@ -0,0 +1,601 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h"
+
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <utime.h>
+
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
+#include "base/task_runner_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/chromeos/drive/drive.pb.h"
+#include "chrome/browser/chromeos/drive/drive_integration_service.h"
+#include "chrome/browser/chromeos/drive/file_system_interface.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
+#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/disks/disk_mount_manager.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_file_util.h"
+#include "webkit/browser/fileapi/file_system_operation_context.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+#include "webkit/common/fileapi/file_system_types.h"
+#include "webkit/common/fileapi/file_system_util.h"
+
+using chromeos::disks::DiskMountManager;
+using content::BrowserContext;
+using content::BrowserThread;
+using content::ChildProcessSecurityPolicy;
+using content::WebContents;
+using fileapi::FileSystemURL;
+
+namespace file_manager {
+namespace {
+
+// Error messages.
+const char kFileError[] = "File error %d";
+
+const DiskMountManager::Disk* GetVolumeAsDisk(const std::string& mount_path) {
+  DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
+
+  DiskMountManager::MountPointMap::const_iterator mount_point_it =
+      disk_mount_manager->mount_points().find(mount_path);
+  if (mount_point_it == disk_mount_manager->mount_points().end())
+    return NULL;
+
+  const DiskMountManager::Disk* disk = disk_mount_manager->FindDiskBySourcePath(
+      mount_point_it->second.source_path);
+
+  return (disk && disk->is_hidden()) ? NULL : disk;
+}
+
+base::DictionaryValue* CreateValueFromDisk(
+    Profile* profile,
+    const std::string& extension_id,
+    const DiskMountManager::Disk* volume) {
+  base::DictionaryValue* volume_info = new base::DictionaryValue();
+
+  std::string mount_path;
+  if (!volume->mount_path().empty()) {
+    base::FilePath relative_mount_path;
+    util::ConvertFileToRelativeFileSystemPath(
+        profile, extension_id, base::FilePath(volume->mount_path()),
+        &relative_mount_path);
+    mount_path = relative_mount_path.value();
+  }
+
+  volume_info->SetString("devicePath", volume->device_path());
+  volume_info->SetString("mountPath", mount_path);
+  volume_info->SetString("systemPath", volume->system_path());
+  volume_info->SetString("filePath", volume->file_path());
+  volume_info->SetString("deviceLabel", volume->device_label());
+  volume_info->SetString("driveLabel", volume->drive_label());
+  volume_info->SetString(
+      "deviceType",
+      DiskMountManager::DeviceTypeToString(volume->device_type()));
+  volume_info->SetDouble("totalSize",
+                         static_cast<double>(volume->total_size_in_bytes()));
+  volume_info->SetBoolean("isParent", volume->is_parent());
+  volume_info->SetBoolean("isReadOnly", volume->is_read_only());
+  volume_info->SetBoolean("hasMedia", volume->has_media());
+  volume_info->SetBoolean("isOnBootDevice", volume->on_boot_device());
+
+  return volume_info;
+}
+
+// Sets permissions for the Drive mount point so Files.app can access files
+// in the mount point directory. It's safe to call this function even if
+// Drive is disabled by the setting (i.e. prefs::kDisableDrive is true).
+void SetDriveMountPointPermissions(
+    Profile* profile,
+    const std::string& extension_id,
+    content::RenderViewHost* render_view_host) {
+  if (!render_view_host ||
+      !render_view_host->GetSiteInstance() || !render_view_host->GetProcess()) {
+    return;
+  }
+
+  content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
+  fileapi::ExternalFileSystemBackend* backend =
+      BrowserContext::GetStoragePartition(profile, site_instance)->
+      GetFileSystemContext()->external_backend();
+  if (!backend)
+    return;
+
+  const base::FilePath mount_point = drive::util::GetDriveMountPointPath();
+  // Grant R/W permissions to drive 'folder'. File API layer still
+  // expects this to be satisfied.
+  ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile(
+      render_view_host->GetProcess()->GetID(), mount_point);
+
+  base::FilePath mount_point_virtual;
+  if (backend->GetVirtualPath(mount_point, &mount_point_virtual))
+    backend->GrantFileAccessToExtension(extension_id, mount_point_virtual);
+}
+
+// Retrieves total and remaining available size on |mount_path|.
+void GetSizeStatsOnBlockingPool(const std::string& mount_path,
+                                uint64* total_size,
+                                uint64* remaining_size) {
+  struct statvfs stat = {};  // Zero-clear
+  if (HANDLE_EINTR(statvfs(mount_path.c_str(), &stat)) == 0) {
+    *total_size =
+        static_cast<uint64>(stat.f_blocks) * stat.f_frsize;
+    *remaining_size =
+        static_cast<uint64>(stat.f_bfree) * stat.f_frsize;
+  }
+}
+
+// Retrieves the maximum file name length of the file system of |path|.
+// Returns 0 if it could not be queried.
+size_t GetFileNameMaxLengthOnBlockingPool(const std::string& path) {
+  struct statvfs stat = {};
+  if (statvfs(path.c_str(), &stat) != 0) {
+    // The filesystem seems not supporting statvfs(). Assume it to be a commonly
+    // used bound 255, and log the failure.
+    LOG(ERROR) << "Cannot statvfs() the name length limit for: " << path;
+    return 255;
+  }
+  return stat.f_namemax;
+}
+
+// Sets last modified date.
+bool SetLastModifiedOnBlockingPool(const base::FilePath& local_path,
+                                   time_t timestamp) {
+  if (local_path.empty())
+    return false;
+
+  struct stat stat_buffer;
+  if (stat(local_path.value().c_str(), &stat_buffer) != 0)
+    return false;
+
+  struct utimbuf times;
+  times.actime = stat_buffer.st_atime;
+  times.modtime = timestamp;
+  return utime(local_path.value().c_str(), &times) == 0;
+}
+
+}  // namespace
+
+RequestFileSystemFunction::RequestFileSystemFunction() {
+}
+
+RequestFileSystemFunction::~RequestFileSystemFunction() {
+}
+
+void RequestFileSystemFunction::DidOpenFileSystem(
+    scoped_refptr<fileapi::FileSystemContext> file_system_context,
+    base::PlatformFileError result,
+    const std::string& name,
+    const GURL& root_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (result != base::PLATFORM_FILE_OK) {
+    DidFail(result);
+    return;
+  }
+
+  // RenderViewHost may have gone while the task is posted asynchronously.
+  if (!render_view_host()) {
+    DidFail(base::PLATFORM_FILE_ERROR_FAILED);
+    return;
+  }
+
+  // Set up file permission access.
+  const int child_id = render_view_host()->GetProcess()->GetID();
+  if (!SetupFileSystemAccessPermissions(file_system_context,
+                                        child_id,
+                                        GetExtension())) {
+    DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
+    return;
+  }
+
+  // Set permissions for the Drive mount point immediately when we kick of
+  // first instance of file manager. The actual mount event will be sent to
+  // UI only when we perform proper authentication.
+  //
+  // Note that we call this function even when Drive is disabled by the
+  // setting. Otherwise, we need to call this when the setting is changed at
+  // a later time, which complicates the code.
+  SetDriveMountPointPermissions(profile_, extension_id(), render_view_host());
+
+  DictionaryValue* dict = new DictionaryValue();
+  SetResult(dict);
+  dict->SetString("name", name);
+  dict->SetString("path", root_path.spec());
+  dict->SetInteger("error", drive::FILE_ERROR_OK);
+  SendResponse(true);
+}
+
+void RequestFileSystemFunction::DidFail(
+    base::PlatformFileError error_code) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  error_ = base::StringPrintf(kFileError, static_cast<int>(error_code));
+  SendResponse(false);
+}
+
+bool RequestFileSystemFunction::SetupFileSystemAccessPermissions(
+    scoped_refptr<fileapi::FileSystemContext> file_system_context,
+    int child_id,
+    scoped_refptr<const extensions::Extension> extension) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (!extension.get())
+    return false;
+
+  // Make sure that only component extension can access the entire
+  // local file system.
+  if (extension_->location() != extensions::Manifest::COMPONENT) {
+    NOTREACHED() << "Private method access by non-component extension "
+                 << extension->id();
+    return false;
+  }
+
+  fileapi::ExternalFileSystemBackend* backend =
+      file_system_context->external_backend();
+  if (!backend)
+    return false;
+
+  // Grant full access to File API from this component extension.
+  backend->GrantFullAccessToExtension(extension_->id());
+
+  // Grant R/W file permissions to the renderer hosting component
+  // extension for all paths exposed by our local file system backend.
+  std::vector<base::FilePath> root_dirs = backend->GetRootDirectories();
+  for (size_t i = 0; i < root_dirs.size(); ++i) {
+    ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile(
+        child_id, root_dirs[i]);
+  }
+  return true;
+}
+
+bool RequestFileSystemFunction::RunImpl() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (!dispatcher() || !render_view_host() || !render_view_host()->GetProcess())
+    return false;
+
+  set_log_on_completion(true);
+
+  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
+  scoped_refptr<fileapi::FileSystemContext> file_system_context =
+      BrowserContext::GetStoragePartition(profile_, site_instance)->
+      GetFileSystemContext();
+
+  const GURL origin_url = source_url_.GetOrigin();
+  file_system_context->OpenFileSystem(
+      origin_url,
+      fileapi::kFileSystemTypeExternal,
+      fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
+      base::Bind(&RequestFileSystemFunction::DidOpenFileSystem,
+                 this,
+                 file_system_context));
+  return true;
+}
+
+FileWatchFunctionBase::FileWatchFunctionBase() {
+}
+
+FileWatchFunctionBase::~FileWatchFunctionBase() {
+}
+
+void FileWatchFunctionBase::Respond(bool success) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  SetResult(Value::CreateBooleanValue(success));
+  SendResponse(success);
+}
+
+bool FileWatchFunctionBase::RunImpl() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (!render_view_host() || !render_view_host()->GetProcess())
+    return false;
+
+  // First param is url of a file to watch.
+  std::string url;
+  if (!args_->GetString(0, &url) || url.empty())
+    return false;
+
+  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
+  scoped_refptr<fileapi::FileSystemContext> file_system_context =
+      BrowserContext::GetStoragePartition(profile(), site_instance)->
+      GetFileSystemContext();
+
+  FileSystemURL file_watch_url = file_system_context->CrackURL(GURL(url));
+  base::FilePath local_path = file_watch_url.path();
+  base::FilePath virtual_path = file_watch_url.virtual_path();
+  if (local_path.empty()) {
+    Respond(false);
+    return true;
+  }
+  PerformFileWatchOperation(local_path, virtual_path, extension_id());
+
+  return true;
+}
+
+AddFileWatchFunction::AddFileWatchFunction() {
+}
+
+AddFileWatchFunction::~AddFileWatchFunction() {
+}
+
+void AddFileWatchFunction::PerformFileWatchOperation(
+    const base::FilePath& local_path,
+    const base::FilePath& virtual_path,
+    const std::string& extension_id) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  FileManagerEventRouter* event_router =
+      FileBrowserPrivateAPI::Get(profile_)->event_router();
+  event_router->AddFileWatch(
+      local_path,
+      virtual_path,
+      extension_id,
+      base::Bind(&AddFileWatchFunction::Respond, this));
+}
+
+RemoveFileWatchFunction::RemoveFileWatchFunction() {
+}
+
+RemoveFileWatchFunction::~RemoveFileWatchFunction() {
+}
+
+void RemoveFileWatchFunction::PerformFileWatchOperation(
+    const base::FilePath& local_path,
+    const base::FilePath& unused,
+    const std::string& extension_id) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  FileManagerEventRouter* event_router =
+      FileBrowserPrivateAPI::Get(profile_)->event_router();
+  event_router->RemoveFileWatch(local_path, extension_id);
+  Respond(true);
+}
+
+SetLastModifiedFunction::SetLastModifiedFunction() {
+}
+
+SetLastModifiedFunction::~SetLastModifiedFunction() {
+}
+
+bool SetLastModifiedFunction::RunImpl() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (args_->GetSize() != 2) {
+    return false;
+  }
+
+  std::string file_url;
+  if (!args_->GetString(0, &file_url))
+    return false;
+
+  std::string timestamp;
+  if (!args_->GetString(1, &timestamp))
+    return false;
+
+  base::FilePath local_path = util::GetLocalPathFromURL(
+      render_view_host(), profile(), GURL(file_url));
+
+  base::PostTaskAndReplyWithResult(
+      BrowserThread::GetBlockingPool(),
+      FROM_HERE,
+      base::Bind(&SetLastModifiedOnBlockingPool,
+                 local_path,
+                 strtoul(timestamp.c_str(), NULL, 0)),
+      base::Bind(&SetLastModifiedFunction::SendResponse,
+                 this));
+  return true;
+}
+
+GetSizeStatsFunction::GetSizeStatsFunction() {
+}
+
+GetSizeStatsFunction::~GetSizeStatsFunction() {
+}
+
+bool GetSizeStatsFunction::RunImpl() {
+  if (args_->GetSize() != 1) {
+    return false;
+  }
+
+  std::string mount_url;
+  if (!args_->GetString(0, &mount_url))
+    return false;
+
+  base::FilePath file_path = util::GetLocalPathFromURL(
+      render_view_host(), profile(), GURL(mount_url));
+  if (file_path.empty())
+    return false;
+
+  if (file_path == drive::util::GetDriveMountPointPath()) {
+    drive::DriveIntegrationService* integration_service =
+        drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+    // |integration_service| is NULL if Drive is disabled.
+    if (!integration_service) {
+      // If stats couldn't be gotten for drive, result should be left
+      // undefined. See comments in GetDriveAvailableSpaceCallback().
+      SendResponse(true);
+      return true;
+    }
+
+    drive::FileSystemInterface* file_system =
+        integration_service->file_system();
+
+    file_system->GetAvailableSpace(
+        base::Bind(&GetSizeStatsFunction::GetDriveAvailableSpaceCallback,
+                   this));
+
+  } else {
+    uint64* total_size = new uint64(0);
+    uint64* remaining_size = new uint64(0);
+    BrowserThread::PostBlockingPoolTaskAndReply(
+        FROM_HERE,
+        base::Bind(&GetSizeStatsOnBlockingPool,
+                   file_path.value(),
+                   total_size,
+                   remaining_size),
+        base::Bind(&GetSizeStatsFunction::GetSizeStatsCallback,
+                   this,
+                   base::Owned(total_size),
+                   base::Owned(remaining_size)));
+  }
+  return true;
+}
+
+void GetSizeStatsFunction::GetDriveAvailableSpaceCallback(
+    drive::FileError error,
+    int64 bytes_total,
+    int64 bytes_used) {
+  if (error == drive::FILE_ERROR_OK) {
+    const uint64 bytes_total_unsigned = bytes_total;
+    const uint64 bytes_remaining_unsigned = bytes_total - bytes_used;
+    GetSizeStatsCallback(&bytes_total_unsigned,
+                         &bytes_remaining_unsigned);
+  } else {
+    // If stats couldn't be gotten for drive, result should be left undefined.
+    SendResponse(true);
+  }
+}
+
+void GetSizeStatsFunction::GetSizeStatsCallback(
+    const uint64* total_size,
+    const uint64* remaining_size) {
+  base::DictionaryValue* sizes = new base::DictionaryValue();
+  SetResult(sizes);
+
+  sizes->SetDouble("totalSize", static_cast<double>(*total_size));
+  sizes->SetDouble("remainingSize", static_cast<double>(*remaining_size));
+
+  SendResponse(true);
+}
+
+GetVolumeMetadataFunction::GetVolumeMetadataFunction() {
+}
+
+GetVolumeMetadataFunction::~GetVolumeMetadataFunction() {
+}
+
+bool GetVolumeMetadataFunction::RunImpl() {
+  if (args_->GetSize() != 1) {
+    error_ = "Invalid argument count";
+    return false;
+  }
+
+  std::string volume_mount_url;
+  if (!args_->GetString(0, &volume_mount_url)) {
+    NOTREACHED();
+    return false;
+  }
+
+  base::FilePath file_path = util::GetLocalPathFromURL(
+      render_view_host(), profile(), GURL(volume_mount_url));
+  if (file_path.empty()) {
+    error_ = "Invalid mount path.";
+    return false;
+  }
+
+  results_.reset();
+
+  const DiskMountManager::Disk* volume = GetVolumeAsDisk(file_path.value());
+  if (volume) {
+    DictionaryValue* volume_info =
+        CreateValueFromDisk(profile_, extension_->id(), volume);
+    SetResult(volume_info);
+  }
+
+  SendResponse(true);
+  return true;
+}
+
+ValidatePathNameLengthFunction::ValidatePathNameLengthFunction() {
+}
+
+ValidatePathNameLengthFunction::~ValidatePathNameLengthFunction() {
+}
+
+bool ValidatePathNameLengthFunction::RunImpl() {
+  std::string parent_url;
+  if (!args_->GetString(0, &parent_url))
+    return false;
+
+  std::string name;
+  if (!args_->GetString(1, &name))
+    return false;
+
+  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
+  scoped_refptr<fileapi::FileSystemContext> file_system_context =
+      BrowserContext::GetStoragePartition(profile(), site_instance)->
+      GetFileSystemContext();
+  fileapi::FileSystemURL filesystem_url(
+      file_system_context->CrackURL(GURL(parent_url)));
+  if (!chromeos::FileSystemBackend::CanHandleURL(filesystem_url))
+    return false;
+
+  // No explicit limit on the length of Drive file names.
+  if (filesystem_url.type() == fileapi::kFileSystemTypeDrive) {
+    SetResult(new base::FundamentalValue(true));
+    SendResponse(true);
+    return true;
+  }
+
+  base::PostTaskAndReplyWithResult(
+      BrowserThread::GetBlockingPool(),
+      FROM_HERE,
+      base::Bind(&GetFileNameMaxLengthOnBlockingPool,
+                 filesystem_url.path().AsUTF8Unsafe()),
+      base::Bind(&ValidatePathNameLengthFunction::OnFilePathLimitRetrieved,
+                 this, name.size()));
+  return true;
+}
+
+void ValidatePathNameLengthFunction::OnFilePathLimitRetrieved(
+    size_t current_length,
+    size_t max_length) {
+  SetResult(new base::FundamentalValue(current_length <= max_length));
+  SendResponse(true);
+}
+
+FormatDeviceFunction::FormatDeviceFunction() {
+}
+
+FormatDeviceFunction::~FormatDeviceFunction() {
+}
+
+bool FormatDeviceFunction::RunImpl() {
+  if (args_->GetSize() != 1) {
+    return false;
+  }
+
+  std::string volume_file_url;
+  if (!args_->GetString(0, &volume_file_url)) {
+    NOTREACHED();
+    return false;
+  }
+
+  base::FilePath file_path = util::GetLocalPathFromURL(
+      render_view_host(), profile(), GURL(volume_file_url));
+  if (file_path.empty())
+    return false;
+
+  DiskMountManager::GetInstance()->FormatMountedDevice(file_path.value());
+  SendResponse(true);
+  return true;
+}
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
new file mode 100644
index 0000000..d4e4f13
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
@@ -0,0 +1,218 @@
+// Copyright 2013 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.
+//
+// This file provides file system related API functions.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_FILE_SYSTEM_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_FILE_SYSTEM_H_
+
+#include <string>
+
+#include "base/platform_file.h"
+#include "chrome/browser/chromeos/drive/file_errors.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
+
+class GURL;
+
+namespace base {
+class FilePath;
+}
+
+namespace fileapi {
+class FileSystemContext;
+}
+
+namespace file_manager {
+
+// Implements the chrome.fileBrowserPrivate.requestFileSystem method.
+class RequestFileSystemFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.requestFileSystem",
+                             FILEBROWSERPRIVATE_REQUESTFILESYSTEM)
+
+  RequestFileSystemFunction();
+
+ protected:
+  virtual ~RequestFileSystemFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  void RespondSuccessOnUIThread(const std::string& name,
+                                const GURL& root_path);
+  void RespondFailedOnUIThread(base::PlatformFileError error_code);
+
+  // Called when FileSystemContext::OpenFileSystem() is done.
+  void DidOpenFileSystem(
+      scoped_refptr<fileapi::FileSystemContext> file_system_context,
+      base::PlatformFileError result,
+      const std::string& name,
+      const GURL& root_path);
+
+  // Called when something goes wrong. Records the error to |error_| per the
+  // error code and reports that the private API function failed.
+  void DidFail(base::PlatformFileError error_code);
+
+  // Sets up file system access permissions to the extension identified by
+  // |child_id|.
+  bool SetupFileSystemAccessPermissions(
+      scoped_refptr<fileapi::FileSystemContext> file_system_context,
+      int child_id,
+      scoped_refptr<const extensions::Extension> extension);
+};
+
+// Base class for AddFileWatchFunction and RemoveFileWatchFunction. Although
+// it's called "FileWatch", the class and its sub classes are used only for
+// watching changes in directories.
+class FileWatchFunctionBase : public LoggedAsyncExtensionFunction {
+ public:
+  FileWatchFunctionBase();
+
+ protected:
+  virtual ~FileWatchFunctionBase();
+
+  // Performs a file watch operation (ex. adds or removes a file watch).
+  virtual void PerformFileWatchOperation(
+      const base::FilePath& local_path,
+      const base::FilePath& virtual_path,
+      const std::string& extension_id) = 0;
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+  // Calls SendResponse() with |success| converted to base::Value.
+  void Respond(bool success);
+};
+
+// Implements the chrome.fileBrowserPrivate.addFileWatch method.
+// Starts watching changes in directories.
+class AddFileWatchFunction : public FileWatchFunctionBase {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.addFileWatch",
+                             FILEBROWSERPRIVATE_ADDFILEWATCH)
+
+  AddFileWatchFunction();
+
+ protected:
+  virtual ~AddFileWatchFunction();
+
+  // FileWatchFunctionBase override.
+  virtual void PerformFileWatchOperation(
+      const base::FilePath& local_path,
+      const base::FilePath& virtual_path,
+      const std::string& extension_id) OVERRIDE;
+};
+
+
+// Implements the chrome.fileBrowserPrivate.removeFileWatch method.
+// Stops watching changes in directories.
+class RemoveFileWatchFunction : public FileWatchFunctionBase {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.removeFileWatch",
+                             FILEBROWSERPRIVATE_REMOVEFILEWATCH)
+
+  RemoveFileWatchFunction();
+
+ protected:
+  virtual ~RemoveFileWatchFunction();
+
+  // FileWatchFunctionBase override.
+  virtual void PerformFileWatchOperation(
+      const base::FilePath& local_path,
+      const base::FilePath& virtual_path,
+      const std::string& extension_id) OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.setLastModified method.
+// Sets last modified date in seconds of local file
+class SetLastModifiedFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.setLastModified",
+                             FILEBROWSERPRIVATE_SETLASTMODIFIED)
+
+  SetLastModifiedFunction();
+
+ protected:
+  virtual ~SetLastModifiedFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.getSizeStats method.
+class GetSizeStatsFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getSizeStats",
+                             FILEBROWSERPRIVATE_GETSIZESTATS)
+
+  GetSizeStatsFunction();
+
+ protected:
+  virtual ~GetSizeStatsFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  void GetDriveAvailableSpaceCallback(drive::FileError error,
+                                      int64 bytes_total,
+                                      int64 bytes_used);
+
+  void GetSizeStatsCallback(const uint64* total_size,
+                            const uint64* remaining_size);
+};
+
+// Implements the chrome.fileBrowserPrivate.getVolumeMetadata method.
+// Retrieves devices meta-data. Expects volume's device path as an argument.
+class GetVolumeMetadataFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getVolumeMetadata",
+                             FILEBROWSERPRIVATE_GETVOLUMEMETADATA)
+
+  GetVolumeMetadataFunction();
+
+ protected:
+  virtual ~GetVolumeMetadataFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.validatePathNameLength method.
+class ValidatePathNameLengthFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.validatePathNameLength",
+                             FILEBROWSERPRIVATE_VALIDATEPATHNAMELENGTH)
+
+  ValidatePathNameLengthFunction();
+
+ protected:
+  virtual ~ValidatePathNameLengthFunction();
+
+  void OnFilePathLimitRetrieved(size_t current_length, size_t max_length);
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.formatDevice method.
+// Formats Device given its mount path.
+class FormatDeviceFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.formatDevice",
+                             FILEBROWSERPRIVATE_FORMATDEVICE)
+
+  FormatDeviceFunction();
+
+ protected:
+  virtual ~FormatDeviceFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_FILE_SYSTEM_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
new file mode 100644
index 0000000..115b18c
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -0,0 +1,204 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_misc.h"
+
+#include "base/files/file_path.h"
+#include "base/prefs/pref_service.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/drive/drive_integration_service.h"
+#include "chrome/browser/chromeos/drive/logging.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/common/page_zoom.h"
+#include "url/gurl.h"
+
+namespace file_manager {
+
+LogoutUserFunction::LogoutUserFunction() {
+}
+
+LogoutUserFunction::~LogoutUserFunction() {
+}
+
+bool LogoutUserFunction::RunImpl() {
+  chrome::AttemptUserExit();
+  return true;
+}
+
+GetPreferencesFunction::GetPreferencesFunction() {
+}
+
+GetPreferencesFunction::~GetPreferencesFunction() {
+}
+
+bool GetPreferencesFunction::RunImpl() {
+  scoped_ptr<DictionaryValue> value(new DictionaryValue());
+
+  const PrefService* service = profile_->GetPrefs();
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  bool drive_enabled = (integration_service != NULL);
+
+  value->SetBoolean("driveEnabled", drive_enabled);
+
+  value->SetBoolean("cellularDisabled",
+                    service->GetBoolean(prefs::kDisableDriveOverCellular));
+
+  value->SetBoolean("hostedFilesDisabled",
+                    service->GetBoolean(prefs::kDisableDriveHostedFiles));
+
+  value->SetBoolean("use24hourClock",
+                    service->GetBoolean(prefs::kUse24HourClock));
+
+  {
+    bool allow = true;
+    if (!chromeos::CrosSettings::Get()->GetBoolean(
+            chromeos::kAllowRedeemChromeOsRegistrationOffers, &allow)) {
+      allow = true;
+    }
+    value->SetBoolean("allowRedeemOffers", allow);
+  }
+
+  SetResult(value.release());
+
+  drive::util::Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
+  return true;
+}
+
+SetPreferencesFunction::SetPreferencesFunction() {
+}
+
+SetPreferencesFunction::~SetPreferencesFunction() {
+}
+
+bool SetPreferencesFunction::RunImpl() {
+  base::DictionaryValue* value = NULL;
+
+  if (!args_->GetDictionary(0, &value) || !value)
+    return false;
+
+  PrefService* service = profile_->GetPrefs();
+
+  bool tmp;
+
+  if (value->GetBoolean("cellularDisabled", &tmp))
+    service->SetBoolean(prefs::kDisableDriveOverCellular, tmp);
+
+  if (value->GetBoolean("hostedFilesDisabled", &tmp))
+    service->SetBoolean(prefs::kDisableDriveHostedFiles, tmp);
+
+  drive::util::Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
+  return true;
+}
+
+ZipSelectionFunction::ZipSelectionFunction() {
+}
+
+ZipSelectionFunction::~ZipSelectionFunction() {
+}
+
+bool ZipSelectionFunction::RunImpl() {
+  if (args_->GetSize() < 3) {
+    return false;
+  }
+
+  // First param is the source directory URL.
+  std::string dir_url;
+  if (!args_->GetString(0, &dir_url) || dir_url.empty())
+    return false;
+
+  base::FilePath src_dir = util::GetLocalPathFromURL(
+      render_view_host(), profile(), GURL(dir_url));
+  if (src_dir.empty())
+    return false;
+
+  // Second param is the list of selected file URLs.
+  ListValue* selection_urls = NULL;
+  args_->GetList(1, &selection_urls);
+  if (!selection_urls || !selection_urls->GetSize())
+    return false;
+
+  std::vector<base::FilePath> files;
+  for (size_t i = 0; i < selection_urls->GetSize(); ++i) {
+    std::string file_url;
+    selection_urls->GetString(i, &file_url);
+    base::FilePath path = util::GetLocalPathFromURL(
+        render_view_host(), profile(), GURL(file_url));
+    if (path.empty())
+      return false;
+    files.push_back(path);
+  }
+
+  // Third param is the name of the output zip file.
+  std::string dest_name;
+  if (!args_->GetString(2, &dest_name) || dest_name.empty())
+    return false;
+
+  // Check if the dir path is under Drive mount point.
+  // TODO(hshi): support create zip file on Drive (crbug.com/158690).
+  if (drive::util::IsUnderDriveMountPoint(src_dir))
+    return false;
+
+  base::FilePath dest_file = src_dir.Append(dest_name);
+  std::vector<base::FilePath> src_relative_paths;
+  for (size_t i = 0; i != files.size(); ++i) {
+    const base::FilePath& file_path = files[i];
+
+    // Obtain the relative path of |file_path| under |src_dir|.
+    base::FilePath relative_path;
+    if (!src_dir.AppendRelativePath(file_path, &relative_path))
+      return false;
+    src_relative_paths.push_back(relative_path);
+  }
+
+  zip_file_creator_ = new ZipFileCreator(this,
+                                         src_dir,
+                                         src_relative_paths,
+                                         dest_file);
+
+  // Keep the refcount until the zipping is complete on utility process.
+  AddRef();
+
+  zip_file_creator_->Start();
+  return true;
+}
+
+void ZipSelectionFunction::OnZipDone(bool success) {
+  SetResult(new base::FundamentalValue(success));
+  SendResponse(true);
+  Release();
+}
+
+ZoomFunction::ZoomFunction() {
+}
+
+ZoomFunction::~ZoomFunction() {
+}
+
+bool ZoomFunction::RunImpl() {
+  content::RenderViewHost* const view_host = render_view_host();
+  std::string operation;
+  args_->GetString(0, &operation);
+  content::PageZoom zoom_type;
+  if (operation == "in") {
+    zoom_type = content::PAGE_ZOOM_IN;
+  } else if (operation == "out") {
+    zoom_type = content::PAGE_ZOOM_OUT;
+  } else if (operation == "reset") {
+    zoom_type = content::PAGE_ZOOM_RESET;
+  } else {
+    NOTREACHED();
+    return false;
+  }
+  view_host->Zoom(zoom_type);
+  return true;
+}
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
new file mode 100644
index 0000000..922864d
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
@@ -0,0 +1,102 @@
+// Copyright 2013 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.
+//
+// This file provides miscellaneous API functions, which don't belong to
+// other files.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_MISC_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_MISC_H_
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
+#include "chrome/browser/chromeos/extensions/file_manager/zip_file_creator.h"
+
+namespace file_manager {
+
+// Implements the chrome.fileBrowserPrivate.logoutUser method.
+class LogoutUserFunction : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.logoutUser",
+                             FILEBROWSERPRIVATE_LOGOUTUSER)
+
+  LogoutUserFunction();
+
+ protected:
+  virtual ~LogoutUserFunction();
+
+  // SyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.getPreferences method.
+// Gets settings for Files.app.
+class GetPreferencesFunction : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getPreferences",
+                             FILEBROWSERPRIVATE_GETPREFERENCES)
+
+  GetPreferencesFunction();
+
+ protected:
+  virtual ~GetPreferencesFunction();
+
+  virtual bool RunImpl() OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.setPreferences method.
+// Sets settings for Files.app.
+class SetPreferencesFunction : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.setPreferences",
+                             FILEBROWSERPRIVATE_SETPREFERENCES)
+
+  SetPreferencesFunction();
+
+ protected:
+  virtual ~SetPreferencesFunction();
+
+  virtual bool RunImpl() OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.zipSelection method.
+// Creates a zip file for the selected files.
+class ZipSelectionFunction : public LoggedAsyncExtensionFunction,
+                             public ZipFileCreator::Observer {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.zipSelection",
+                             FILEBROWSERPRIVATE_ZIPSELECTION)
+
+  ZipSelectionFunction();
+
+ protected:
+  virtual ~ZipSelectionFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+  // extensions::ZipFileCreator::Delegate overrides.
+  virtual void OnZipDone(bool success) OVERRIDE;
+
+ private:
+  scoped_refptr<ZipFileCreator> zip_file_creator_;
+};
+
+// Implements the chrome.fileBrowserPrivate.zoom method.
+// Changes the zoom level of the file manager by internally calling
+// RenderViewHost::Zoom(). TODO(hirono): Remove this function once the zoom
+// level change is supported for all apps. crbug.com/227175.
+class ZoomFunction : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.zoom",
+                             FILEBROWSERPRIVATE_ZOOM);
+
+  ZoomFunction();
+
+ protected:
+  virtual ~ZoomFunction();
+  virtual bool RunImpl() OVERRIDE;
+};
+
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_MISC_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
new file mode 100644
index 0000000..806be96
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
@@ -0,0 +1,270 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_mount.h"
+
+#include "base/format_macros.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/drive/drive_integration_service.h"
+#include "chrome/browser/chromeos/drive/file_system_interface.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/chromeos/drive/logging.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/disks/disk_mount_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/shell_dialogs/selected_file_info.h"
+
+using chromeos::disks::DiskMountManager;
+using content::BrowserThread;
+
+namespace file_manager {
+namespace {
+
+// Creates a dictionary describing the mount point of |mount_point_info|.
+base::DictionaryValue* CreateValueFromMountPoint(
+    Profile* profile,
+    const DiskMountManager::MountPointInfo& mount_point_info,
+    const std::string& extension_id) {
+
+  base::DictionaryValue *mount_info = new base::DictionaryValue();
+
+  mount_info->SetString("mountType",
+                        DiskMountManager::MountTypeToString(
+                            mount_point_info.mount_type));
+  mount_info->SetString("sourcePath", mount_point_info.source_path);
+
+  base::FilePath relative_mount_path;
+  // Convert mount point path to relative path with the external file system
+  // exposed within File API.
+  if (util::ConvertFileToRelativeFileSystemPath(
+          profile,
+          extension_id,
+          base::FilePath(mount_point_info.mount_path),
+          &relative_mount_path)) {
+    mount_info->SetString("mountPath", relative_mount_path.value());
+  }
+
+  mount_info->SetString("mountCondition",
+                        DiskMountManager::MountConditionToString(
+                            mount_point_info.mount_condition));
+
+  return mount_info;
+}
+
+}  // namespace
+
+AddMountFunction::AddMountFunction() {
+}
+
+AddMountFunction::~AddMountFunction() {
+}
+
+bool AddMountFunction::RunImpl() {
+  // The third argument is simply ignored.
+  if (args_->GetSize() != 2 && args_->GetSize() != 3) {
+    error_ = "Invalid argument count";
+    return false;
+  }
+
+  std::string file_url;
+  if (!args_->GetString(0, &file_url)) {
+    return false;
+  }
+
+  std::string mount_type_str;
+  if (!args_->GetString(1, &mount_type_str)) {
+    return false;
+  }
+
+  drive::util::Log(logging::LOG_INFO,
+                   "%s[%d] called. (source: '%s', type:'%s')",
+                   name().c_str(),
+                   request_id(),
+                   file_url.empty() ? "(none)" : file_url.c_str(),
+                   mount_type_str.c_str());
+  set_log_on_completion(true);
+
+  // Set default return source path to the empty string.
+  SetResult(new base::StringValue(""));
+
+  chromeos::MountType mount_type =
+      DiskMountManager::MountTypeFromString(mount_type_str);
+  switch (mount_type) {
+    case chromeos::MOUNT_TYPE_INVALID: {
+      error_ = "Invalid mount type";
+      SendResponse(false);
+      break;
+    }
+    case chromeos::MOUNT_TYPE_GOOGLE_DRIVE: {
+      // Dispatch fake 'mounted' event because JS code depends on it.
+      // TODO(hashimoto): Remove this redanduncy.
+      FileBrowserPrivateAPI::Get(profile_)->event_router()->
+          OnFileSystemMounted();
+
+      // Pass back the drive mount point path as source path.
+      const std::string& drive_path =
+          drive::util::GetDriveMountPointPathAsString();
+      SetResult(new base::StringValue(drive_path));
+      SendResponse(true);
+      break;
+    }
+    default: {
+      const base::FilePath path = util::GetLocalPathFromURL(
+          render_view_host(), profile(), GURL(file_url));
+
+      if (path.empty()) {
+        SendResponse(false);
+        break;
+      }
+
+      const base::FilePath::StringType display_name = path.BaseName().value();
+
+      // Check if the source path is under Drive cache directory.
+      if (drive::util::IsUnderDriveMountPoint(path)) {
+        drive::DriveIntegrationService* integration_service =
+            drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+        drive::FileSystemInterface* file_system =
+            integration_service ? integration_service->file_system() : NULL;
+        if (!file_system) {
+          SendResponse(false);
+          break;
+        }
+        file_system->MarkCacheFileAsMounted(
+            drive::util::ExtractDrivePath(path),
+            base::Bind(&AddMountFunction::OnMountedStateSet,
+                       this, mount_type_str, display_name));
+      } else {
+        OnMountedStateSet(mount_type_str, display_name,
+                          drive::FILE_ERROR_OK, path);
+      }
+      break;
+    }
+  }
+
+  return true;
+}
+
+void AddMountFunction::OnMountedStateSet(
+    const std::string& mount_type,
+    const base::FilePath::StringType& file_name,
+    drive::FileError error,
+    const base::FilePath& file_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (error != drive::FILE_ERROR_OK) {
+    SendResponse(false);
+    return;
+  }
+
+  DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
+  // Pass back the actual source path of the mount point.
+  SetResult(new base::StringValue(file_path.value()));
+  SendResponse(true);
+  // MountPath() takes a std::string.
+  disk_mount_manager->MountPath(
+      file_path.AsUTF8Unsafe(), base::FilePath(file_name).Extension(),
+      file_name, DiskMountManager::MountTypeFromString(mount_type));
+}
+
+RemoveMountFunction::RemoveMountFunction() {
+}
+
+RemoveMountFunction::~RemoveMountFunction() {
+}
+
+bool RemoveMountFunction::RunImpl() {
+  if (args_->GetSize() != 1) {
+    return false;
+  }
+
+  std::string mount_path;
+  if (!args_->GetString(0, &mount_path)) {
+    return false;
+  }
+
+  drive::util::Log(logging::LOG_INFO,
+                   "%s[%d] called. (mount_path: '%s')",
+                   name().c_str(),
+                   request_id(),
+                   mount_path.c_str());
+  set_log_on_completion(true);
+
+  std::vector<GURL> file_paths;
+  file_paths.push_back(GURL(mount_path));
+  util::GetSelectedFileInfo(
+      render_view_host(),
+      profile(),
+      file_paths,
+      true,  // for_opening
+      base::Bind(&RemoveMountFunction::GetSelectedFileInfoResponse, this));
+  return true;
+}
+
+void RemoveMountFunction::GetSelectedFileInfoResponse(
+    const std::vector<ui::SelectedFileInfo>& files) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (files.size() != 1) {
+    SendResponse(false);
+    return;
+  }
+
+  // TODO(tbarzic): Send response when callback is received, it would make more
+  // sense than remembering issued unmount requests in file manager and showing
+  // errors for them when MountCompleted event is received.
+  DiskMountManager::GetInstance()->UnmountPath(
+      files[0].local_path.value(),
+      chromeos::UNMOUNT_OPTIONS_NONE,
+      DiskMountManager::UnmountPathCallback());
+  SendResponse(true);
+}
+
+GetMountPointsFunction::GetMountPointsFunction() {
+}
+
+GetMountPointsFunction::~GetMountPointsFunction() {
+}
+
+bool GetMountPointsFunction::RunImpl() {
+  if (args_->GetSize())
+    return false;
+
+  base::ListValue *mounts = new base::ListValue();
+  SetResult(mounts);
+
+  DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
+  DiskMountManager::MountPointMap mount_points =
+      disk_mount_manager->mount_points();
+
+  std::string log_string = "[";
+  const char *separator = "";
+  for (DiskMountManager::MountPointMap::const_iterator it =
+           mount_points.begin();
+       it != mount_points.end();
+       ++it) {
+    mounts->Append(CreateValueFromMountPoint(profile_,
+                                             it->second,
+                                             extension_->id()));
+    log_string += separator + it->first;
+    separator = ", ";
+  }
+
+  log_string += "]";
+
+  drive::util::Log(logging::LOG_INFO,
+                   "%s[%d] succeeded. (results: '%s', %" PRIuS " mount points)",
+                   name().c_str(),
+                   request_id(),
+                   log_string.c_str(),
+                   mount_points.size());
+
+  SendResponse(true);
+  return true;
+}
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h
new file mode 100644
index 0000000..a3913f7
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h
@@ -0,0 +1,84 @@
+// Copyright 2013 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.
+//
+// This file provides task related API functions.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_MOUNT_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_MOUNT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "chrome/browser/chromeos/drive/file_errors.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
+
+namespace ui {
+struct SelectedFileInfo;
+}
+
+namespace file_manager {
+
+// Implements chrome.fileBrowserPrivate.addMount method.
+// Mounts a device or a file.
+class AddMountFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.addMount",
+                             FILEBROWSERPRIVATE_ADDMOUNT)
+
+  AddMountFunction();
+
+ protected:
+  virtual ~AddMountFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  // A callback method to handle the result of MarkCacheAsMounted.
+  void OnMountedStateSet(const std::string& mount_type,
+                         const base::FilePath::StringType& file_name,
+                         drive::FileError error,
+                         const base::FilePath& file_path);
+};
+
+// Implements chrome.fileBrowserPrivate.removeMount method.
+// Unmounts selected device. Expects mount point path as an argument.
+class RemoveMountFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.removeMount",
+                             FILEBROWSERPRIVATE_REMOVEMOUNT)
+
+  RemoveMountFunction();
+
+ protected:
+  virtual ~RemoveMountFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  // A callback method to handle the result of GetSelectedFileInfo.
+  void GetSelectedFileInfoResponse(
+      const std::vector<ui::SelectedFileInfo>& files);
+};
+
+// Implements chrome.fileBrowserPrivate.getMountPoints method.
+class GetMountPointsFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getMountPoints",
+                             FILEBROWSERPRIVATE_GETMOUNTPOINTS)
+
+  GetMountPointsFunction();
+
+ protected:
+  virtual ~GetMountPointsFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_MOUNT_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
new file mode 100644
index 0000000..6d6505e
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
@@ -0,0 +1,521 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_strings.h"
+
+#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
+#include "chrome/browser/chromeos/system/statistics_provider.h"
+#include "grit/app_locale_settings.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/webui/web_ui_util.h"
+
+namespace file_manager {
+
+GetStringsFunction::GetStringsFunction() {
+}
+
+GetStringsFunction::~GetStringsFunction() {
+}
+
+bool GetStringsFunction::RunImpl() {
+  DictionaryValue* dict = new DictionaryValue();
+  SetResult(dict);
+
+#define SET_STRING(id, idr) \
+  dict->SetString(id, l10n_util::GetStringUTF16(idr))
+
+  SET_STRING("WEB_FONT_FAMILY", IDS_WEB_FONT_FAMILY);
+  SET_STRING("WEB_FONT_SIZE", IDS_WEB_FONT_SIZE);
+
+  SET_STRING("ROOT_DIRECTORY_LABEL", IDS_FILE_BROWSER_ROOT_DIRECTORY_LABEL);
+  SET_STRING("ARCHIVE_DIRECTORY_LABEL",
+             IDS_FILE_BROWSER_ARCHIVE_DIRECTORY_LABEL);
+  SET_STRING("REMOVABLE_DIRECTORY_LABEL",
+             IDS_FILE_BROWSER_REMOVABLE_DIRECTORY_LABEL);
+  SET_STRING("DOWNLOADS_DIRECTORY_LABEL",
+             IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_LABEL);
+  SET_STRING("DRIVE_DIRECTORY_LABEL", IDS_FILE_BROWSER_DRIVE_DIRECTORY_LABEL);
+  SET_STRING("DRIVE_MY_DRIVE_LABEL", IDS_FILE_BROWSER_DRIVE_MY_DRIVE_LABEL);
+  SET_STRING("DRIVE_OFFLINE_COLLECTION_LABEL",
+             IDS_FILE_BROWSER_DRIVE_OFFLINE_COLLECTION_LABEL);
+  SET_STRING("DRIVE_SHARED_WITH_ME_COLLECTION_LABEL",
+             IDS_FILE_BROWSER_DRIVE_SHARED_WITH_ME_COLLECTION_LABEL);
+  SET_STRING("DRIVE_RECENT_COLLECTION_LABEL",
+             IDS_FILE_BROWSER_DRIVE_RECENT_COLLECTION_LABEL);
+  SET_STRING("NAME_COLUMN_LABEL", IDS_FILE_BROWSER_NAME_COLUMN_LABEL);
+  SET_STRING("SIZE_COLUMN_LABEL", IDS_FILE_BROWSER_SIZE_COLUMN_LABEL);
+  SET_STRING("SIZE_BYTES", IDS_FILE_BROWSER_SIZE_BYTES);
+  SET_STRING("SIZE_KB", IDS_FILE_BROWSER_SIZE_KB);
+  SET_STRING("SIZE_MB", IDS_FILE_BROWSER_SIZE_MB);
+  SET_STRING("SIZE_GB", IDS_FILE_BROWSER_SIZE_GB);
+  SET_STRING("SIZE_TB", IDS_FILE_BROWSER_SIZE_TB);
+  SET_STRING("SIZE_PB", IDS_FILE_BROWSER_SIZE_PB);
+
+  SET_STRING("SHORTCUT_CTRL", IDS_FILE_BROWSER_SHORTCUT_CTRL);
+  SET_STRING("SHORTCUT_ALT", IDS_FILE_BROWSER_SHORTCUT_ALT);
+  SET_STRING("SHORTCUT_SHIFT", IDS_FILE_BROWSER_SHORTCUT_SHIFT);
+  SET_STRING("SHORTCUT_META", IDS_FILE_BROWSER_SHORTCUT_META);
+  SET_STRING("SHORTCUT_SPACE", IDS_FILE_BROWSER_SHORTCUT_SPACE);
+  SET_STRING("SHORTCUT_ENTER", IDS_FILE_BROWSER_SHORTCUT_ENTER);
+
+  SET_STRING("TYPE_COLUMN_LABEL", IDS_FILE_BROWSER_TYPE_COLUMN_LABEL);
+  SET_STRING("DATE_COLUMN_LABEL", IDS_FILE_BROWSER_DATE_COLUMN_LABEL);
+  SET_STRING("PREVIEW_COLUMN_LABEL", IDS_FILE_BROWSER_PREVIEW_COLUMN_LABEL);
+  SET_STRING("OFFLINE_COLUMN_LABEL", IDS_FILE_BROWSER_OFFLINE_COLUMN_LABEL);
+
+  SET_STRING("DOWNLOADS_DIRECTORY_WARNING",
+             IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_WARNING);
+
+  SET_STRING("ERROR_CREATING_FOLDER", IDS_FILE_BROWSER_ERROR_CREATING_FOLDER);
+  SET_STRING("ERROR_INVALID_CHARACTER",
+             IDS_FILE_BROWSER_ERROR_INVALID_CHARACTER);
+  SET_STRING("ERROR_RESERVED_NAME", IDS_FILE_BROWSER_ERROR_RESERVED_NAME);
+  SET_STRING("ERROR_HIDDEN_NAME", IDS_FILE_BROWSER_ERROR_HIDDEN_NAME);
+  SET_STRING("ERROR_WHITESPACE_NAME", IDS_FILE_BROWSER_ERROR_WHITESPACE_NAME);
+  SET_STRING("ERROR_NEW_FOLDER_EMPTY_NAME",
+             IDS_FILE_BROWSER_ERROR_NEW_FOLDER_EMPTY_NAME);
+  SET_STRING("ERROR_LONG_NAME", IDS_FILE_BROWSER_ERROR_LONG_NAME);
+  SET_STRING("NEW_FOLDER_BUTTON_LABEL",
+             IDS_FILE_BROWSER_NEW_FOLDER_BUTTON_LABEL);
+  SET_STRING("NEW_WINDOW_BUTTON_LABEL",
+             IDS_FILE_BROWSER_NEW_WINDOW_BUTTON_LABEL);
+  SET_STRING("CHANGE_DEFAULT_APP_BUTTON_LABEL",
+             IDS_FILE_BROWSER_CHANGE_DEFAULT_APP_BUTTON_LABEL);
+  SET_STRING("FILENAME_LABEL", IDS_FILE_BROWSER_FILENAME_LABEL);
+  SET_STRING("PREPARING_LABEL", IDS_FILE_BROWSER_PREPARING_LABEL);
+  SET_STRING("DRAGGING_MULTIPLE_ITEMS",
+             IDS_FILE_BROWSER_DRAGGING_MULTIPLE_ITEMS);
+
+  SET_STRING("DIMENSIONS_LABEL", IDS_FILE_BROWSER_DIMENSIONS_LABEL);
+  SET_STRING("DIMENSIONS_FORMAT", IDS_FILE_BROWSER_DIMENSIONS_FORMAT);
+
+  SET_STRING("IMAGE_DIMENSIONS", IDS_FILE_BROWSER_IMAGE_DIMENSIONS);
+  SET_STRING("VOLUME_LABEL", IDS_FILE_BROWSER_VOLUME_LABEL);
+  SET_STRING("READ_ONLY", IDS_FILE_BROWSER_READ_ONLY);
+
+  SET_STRING("ARCHIVE_MOUNT_FAILED", IDS_FILE_BROWSER_ARCHIVE_MOUNT_FAILED);
+  SET_STRING("UNMOUNT_FAILED", IDS_FILE_BROWSER_UNMOUNT_FAILED);
+  SET_STRING("MOUNT_ARCHIVE", IDS_FILE_BROWSER_MOUNT_ARCHIVE);
+  SET_STRING("FORMAT_DEVICE_BUTTON_LABEL",
+             IDS_FILE_BROWSER_FORMAT_DEVICE_BUTTON_LABEL);
+  SET_STRING("UNMOUNT_DEVICE_BUTTON_LABEL",
+             IDS_FILE_BROWSER_UNMOUNT_DEVICE_BUTTON_LABEL);
+  SET_STRING("CLOSE_ARCHIVE_BUTTON_LABEL",
+             IDS_FILE_BROWSER_CLOSE_ARCHIVE_BUTTON_LABEL);
+
+  SET_STRING("SEARCH_TEXT_LABEL", IDS_FILE_BROWSER_SEARCH_TEXT_LABEL);
+
+  SET_STRING("ACTION_VIEW", IDS_FILE_BROWSER_ACTION_VIEW);
+  SET_STRING("ACTION_OPEN", IDS_FILE_BROWSER_ACTION_OPEN);
+  SET_STRING("ACTION_OPEN_GDOC", IDS_FILE_BROWSER_ACTION_OPEN_GDOC);
+  SET_STRING("ACTION_OPEN_GSHEET", IDS_FILE_BROWSER_ACTION_OPEN_GSHEET);
+  SET_STRING("ACTION_OPEN_GSLIDES", IDS_FILE_BROWSER_ACTION_OPEN_GSLIDES);
+  SET_STRING("ACTION_WATCH", IDS_FILE_BROWSER_ACTION_WATCH);
+  SET_STRING("ACTION_LISTEN", IDS_FILE_BROWSER_ACTION_LISTEN);
+  SET_STRING("INSTALL_CRX", IDS_FILE_BROWSER_INSTALL_CRX);
+  SET_STRING("SEND_TO_DRIVE", IDS_FILE_BROWSER_SEND_TO_DRIVE);
+
+  SET_STRING("GALLERY_NO_IMAGES", IDS_FILE_BROWSER_GALLERY_NO_IMAGES);
+  SET_STRING("GALLERY_ITEMS_SELECTED", IDS_FILE_BROWSER_GALLERY_ITEMS_SELECTED);
+  SET_STRING("GALLERY_MOSAIC", IDS_FILE_BROWSER_GALLERY_MOSAIC);
+  SET_STRING("GALLERY_SLIDE", IDS_FILE_BROWSER_GALLERY_SLIDE);
+  SET_STRING("GALLERY_DELETE", IDS_FILE_BROWSER_GALLERY_DELETE);
+  SET_STRING("GALLERY_SLIDESHOW", IDS_FILE_BROWSER_GALLERY_SLIDESHOW);
+
+  SET_STRING("GALLERY_EDIT", IDS_FILE_BROWSER_GALLERY_EDIT);
+  SET_STRING("GALLERY_PRINT", IDS_FILE_BROWSER_GALLERY_PRINT);
+  SET_STRING("GALLERY_SHARE", IDS_FILE_BROWSER_GALLERY_SHARE);
+  SET_STRING("GALLERY_ENTER_WHEN_DONE",
+             IDS_FILE_BROWSER_GALLERY_ENTER_WHEN_DONE);
+  SET_STRING("GALLERY_AUTOFIX", IDS_FILE_BROWSER_GALLERY_AUTOFIX);
+  SET_STRING("GALLERY_FIXED", IDS_FILE_BROWSER_GALLERY_FIXED);
+  SET_STRING("GALLERY_CROP", IDS_FILE_BROWSER_GALLERY_CROP);
+  SET_STRING("GALLERY_EXPOSURE", IDS_FILE_BROWSER_GALLERY_EXPOSURE);
+  SET_STRING("GALLERY_BRIGHTNESS", IDS_FILE_BROWSER_GALLERY_BRIGHTNESS);
+  SET_STRING("GALLERY_CONTRAST", IDS_FILE_BROWSER_GALLERY_CONTRAST);
+  SET_STRING("GALLERY_ROTATE_LEFT", IDS_FILE_BROWSER_GALLERY_ROTATE_LEFT);
+  SET_STRING("GALLERY_ROTATE_RIGHT", IDS_FILE_BROWSER_GALLERY_ROTATE_RIGHT);
+  SET_STRING("GALLERY_UNDO", IDS_FILE_BROWSER_GALLERY_UNDO);
+  SET_STRING("GALLERY_REDO", IDS_FILE_BROWSER_GALLERY_REDO);
+  SET_STRING("GALLERY_FILE_EXISTS", IDS_FILE_BROWSER_GALLERY_FILE_EXISTS);
+  SET_STRING("GALLERY_SAVED", IDS_FILE_BROWSER_GALLERY_SAVED);
+  SET_STRING("GALLERY_OVERWRITE_ORIGINAL",
+             IDS_FILE_BROWSER_GALLERY_OVERWRITE_ORIGINAL);
+  SET_STRING("GALLERY_OVERWRITE_BUBBLE",
+             IDS_FILE_BROWSER_GALLERY_OVERWRITE_BUBBLE);
+  SET_STRING("GALLERY_UNSAVED_CHANGES",
+             IDS_FILE_BROWSER_GALLERY_UNSAVED_CHANGES);
+  SET_STRING("GALLERY_READONLY_WARNING",
+             IDS_FILE_BROWSER_GALLERY_READONLY_WARNING);
+  SET_STRING("GALLERY_IMAGE_ERROR", IDS_FILE_BROWSER_GALLERY_IMAGE_ERROR);
+  SET_STRING("GALLERY_IMAGE_TOO_BIG_ERROR",
+             IDS_FILE_BROWSER_GALLERY_IMAGE_TOO_BIG_ERROR);
+  SET_STRING("GALLERY_VIDEO_ERROR", IDS_FILE_BROWSER_GALLERY_VIDEO_ERROR);
+  SET_STRING("GALLERY_VIDEO_DECODING_ERROR",
+             IDS_FILE_BROWSER_GALLERY_VIDEO_DECODING_ERROR);
+  SET_STRING("GALLERY_VIDEO_LOOPED_MODE",
+             IDS_FILE_BROWSER_GALLERY_VIDEO_LOOPED_MODE);
+  SET_STRING("AUDIO_ERROR", IDS_FILE_BROWSER_AUDIO_ERROR);
+  SET_STRING("GALLERY_IMAGE_OFFLINE", IDS_FILE_BROWSER_GALLERY_IMAGE_OFFLINE);
+  SET_STRING("GALLERY_VIDEO_OFFLINE", IDS_FILE_BROWSER_GALLERY_VIDEO_OFFLINE);
+  SET_STRING("AUDIO_OFFLINE", IDS_FILE_BROWSER_AUDIO_OFFLINE);
+  // Reusing strings, but with alias starting with GALLERY.
+  dict->SetString("GALLERY_FILE_HIDDEN_NAME",
+      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_ERROR_HIDDEN_NAME));
+  dict->SetString("GALLERY_OK_LABEL",
+      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_OK_LABEL));
+  dict->SetString("GALLERY_CANCEL_LABEL",
+      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_CANCEL_LABEL));
+  dict->SetString("GALLERY_CONFIRM_DELETE_ONE",
+      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_CONFIRM_DELETE_ONE));
+  dict->SetString("GALLERY_CONFIRM_DELETE_SOME",
+      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_CONFIRM_DELETE_SOME));
+
+  SET_STRING("ACTION_CHOICE_OPENING_METHOD",
+             IDS_FILE_BROWSER_ACTION_CHOICE_OPENING_METHOD);
+  SET_STRING("ACTION_CHOICE_PHOTOS_DRIVE",
+             IDS_FILE_BROWSER_ACTION_CHOICE_PHOTOS_DRIVE);
+  SET_STRING("ACTION_CHOICE_DRIVE_NOT_REACHED",
+             IDS_FILE_BROWSER_ACTION_CHOICE_DRIVE_NOT_REACHED);
+  SET_STRING("ACTION_CHOICE_VIEW_FILES",
+             IDS_FILE_BROWSER_ACTION_CHOICE_VIEW_FILES);
+  SET_STRING("ACTION_CHOICE_WATCH_SINGLE_VIDEO",
+             IDS_FILE_BROWSER_ACTION_CHOICE_WATCH_SINGLE_VIDEO);
+  SET_STRING("ACTION_CHOICE_ONCE", IDS_FILE_BROWSER_ACTION_CHOICE_ONCE);
+  SET_STRING("ACTION_CHOICE_ALWAYS", IDS_FILE_BROWSER_ACTION_CHOICE_ALWAYS);
+  SET_STRING("ACTION_CHOICE_COUNTER_NO_MEDIA",
+             IDS_FILE_BROWSER_ACTION_CHOICE_COUNTER_NO_MEDIA);
+  SET_STRING("ACTION_CHOICE_COUNTER", IDS_FILE_BROWSER_ACTION_CHOICE_COUNTER);
+  SET_STRING("ACTION_CHOICE_LOADING_USB",
+             IDS_FILE_BROWSER_ACTION_CHOICE_LOADING_USB);
+  SET_STRING("ACTION_CHOICE_LOADING_SD",
+             IDS_FILE_BROWSER_ACTION_CHOICE_LOADING_SD);
+
+  SET_STRING("PHOTO_IMPORT_TITLE", IDS_FILE_BROWSER_PHOTO_IMPORT_TITLE);
+  SET_STRING("PHOTO_IMPORT_IMPORT_BUTTON",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_IMPORT_BUTTON);
+  SET_STRING("PHOTO_IMPORT_CANCEL_BUTTON",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_CANCEL_BUTTON);
+  SET_STRING("PHOTO_IMPORT_DRIVE_ERROR",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_DRIVE_ERROR);
+  SET_STRING("PHOTO_IMPORT_DESTINATION_ERROR",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_DESTINATION_ERROR);
+  SET_STRING("PHOTO_IMPORT_SOURCE_ERROR",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_SOURCE_ERROR);
+  SET_STRING("PHOTO_IMPORT_UNKNOWN_DATE",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_UNKNOWN_DATE);
+  SET_STRING("PHOTO_IMPORT_NEW_ALBUM_NAME",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_NEW_ALBUM_NAME);
+  SET_STRING("PHOTO_IMPORT_SELECT_ALBUM_CAPTION",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_SELECT_ALBUM_CAPTION);
+  SET_STRING("PHOTO_IMPORT_SELECT_ALBUM_CAPTION_PLURAL",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_SELECT_ALBUM_CAPTION_PLURAL);
+  SET_STRING("PHOTO_IMPORT_IMPORTING_ERROR",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_IMPORTING_ERROR);
+  SET_STRING("PHOTO_IMPORT_IMPORTING", IDS_FILE_BROWSER_PHOTO_IMPORT_IMPORTING);
+  SET_STRING("PHOTO_IMPORT_IMPORT_COMPLETE",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_IMPORT_COMPLETE);
+  SET_STRING("PHOTO_IMPORT_CAPTION", IDS_FILE_BROWSER_PHOTO_IMPORT_CAPTION);
+  SET_STRING("PHOTO_IMPORT_ONE_SELECTED",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_ONE_SELECTED);
+  SET_STRING("PHOTO_IMPORT_MANY_SELECTED",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_MANY_SELECTED);
+  SET_STRING("PHOTO_IMPORT_SELECT_ALL",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_SELECT_ALL);
+  SET_STRING("PHOTO_IMPORT_SELECT_NONE",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_SELECT_NONE);
+  SET_STRING("PHOTO_IMPORT_DELETE_AFTER",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_DELETE_AFTER);
+  SET_STRING("PHOTO_IMPORT_MY_PHOTOS_DIRECTORY_NAME",
+             IDS_FILE_BROWSER_PHOTO_IMPORT_MY_PHOTOS_DIRECTORY_NAME);
+
+  SET_STRING("CONFIRM_OVERWRITE_FILE", IDS_FILE_BROWSER_CONFIRM_OVERWRITE_FILE);
+  SET_STRING("FILE_ALREADY_EXISTS", IDS_FILE_BROWSER_FILE_ALREADY_EXISTS);
+  SET_STRING("DIRECTORY_ALREADY_EXISTS",
+             IDS_FILE_BROWSER_DIRECTORY_ALREADY_EXISTS);
+  SET_STRING("ERROR_RENAMING", IDS_FILE_BROWSER_ERROR_RENAMING);
+  SET_STRING("RENAME_PROMPT", IDS_FILE_BROWSER_RENAME_PROMPT);
+  SET_STRING("RENAME_BUTTON_LABEL", IDS_FILE_BROWSER_RENAME_BUTTON_LABEL);
+
+  SET_STRING("ERROR_DELETING", IDS_FILE_BROWSER_ERROR_DELETING);
+  SET_STRING("DELETE_BUTTON_LABEL", IDS_FILE_BROWSER_DELETE_BUTTON_LABEL);
+
+  SET_STRING("PASTE_BUTTON_LABEL", IDS_FILE_BROWSER_PASTE_BUTTON_LABEL);
+
+  SET_STRING("COPY_BUTTON_LABEL", IDS_FILE_BROWSER_COPY_BUTTON_LABEL);
+  SET_STRING("CUT_BUTTON_LABEL", IDS_FILE_BROWSER_CUT_BUTTON_LABEL);
+  SET_STRING("ZIP_SELECTION_BUTTON_LABEL",
+             IDS_FILE_BROWSER_ZIP_SELECTION_BUTTON_LABEL);
+  SET_STRING("CREATE_FOLDER_SHORTCUT_BUTTON_LABEL",
+             IDS_FILE_BROWSER_CREATE_FOLDER_SHORTCUT_BUTTON_LABEL);
+  SET_STRING("REMOVE_FOLDER_SHORTCUT_BUTTON_LABEL",
+             IDS_FILE_BROWSER_REMOVE_FOLDER_SHORTCUT_BUTTON_LABEL);
+  SET_STRING("SHARE_BUTTON_LABEL",
+             IDS_FILE_BROWSER_SHARE_BUTTON_LABEL);
+
+  SET_STRING("OPEN_WITH_BUTTON_LABEL", IDS_FILE_BROWSER_OPEN_WITH_BUTTON_LABEL);
+
+  SET_STRING("TRANSFER_ITEMS_REMAINING",
+             IDS_FILE_BROWSER_TRANSFER_ITEMS_REMAINING);
+  SET_STRING("TRANSFER_CANCELLED", IDS_FILE_BROWSER_TRANSFER_CANCELLED);
+  SET_STRING("TRANSFER_TARGET_EXISTS_ERROR",
+             IDS_FILE_BROWSER_TRANSFER_TARGET_EXISTS_ERROR);
+  SET_STRING("TRANSFER_FILESYSTEM_ERROR",
+             IDS_FILE_BROWSER_TRANSFER_FILESYSTEM_ERROR);
+  SET_STRING("TRANSFER_UNEXPECTED_ERROR",
+             IDS_FILE_BROWSER_TRANSFER_UNEXPECTED_ERROR);
+  SET_STRING("COPY_FILE_NAME", IDS_FILE_BROWSER_COPY_FILE_NAME);
+  SET_STRING("COPY_ITEMS_REMAINING", IDS_FILE_BROWSER_COPY_ITEMS_REMAINING);
+  SET_STRING("COPY_CANCELLED", IDS_FILE_BROWSER_COPY_CANCELLED);
+  SET_STRING("COPY_TARGET_EXISTS_ERROR",
+             IDS_FILE_BROWSER_COPY_TARGET_EXISTS_ERROR);
+  SET_STRING("COPY_FILESYSTEM_ERROR", IDS_FILE_BROWSER_COPY_FILESYSTEM_ERROR);
+  SET_STRING("COPY_UNEXPECTED_ERROR", IDS_FILE_BROWSER_COPY_UNEXPECTED_ERROR);
+  SET_STRING("MOVE_FILE_NAME", IDS_FILE_BROWSER_MOVE_FILE_NAME);
+  SET_STRING("MOVE_ITEMS_REMAINING", IDS_FILE_BROWSER_MOVE_ITEMS_REMAINING);
+  SET_STRING("MOVE_CANCELLED", IDS_FILE_BROWSER_MOVE_CANCELLED);
+  SET_STRING("MOVE_TARGET_EXISTS_ERROR",
+             IDS_FILE_BROWSER_MOVE_TARGET_EXISTS_ERROR);
+  SET_STRING("MOVE_FILESYSTEM_ERROR", IDS_FILE_BROWSER_MOVE_FILESYSTEM_ERROR);
+  SET_STRING("MOVE_UNEXPECTED_ERROR", IDS_FILE_BROWSER_MOVE_UNEXPECTED_ERROR);
+  SET_STRING("ZIP_FILE_NAME", IDS_FILE_BROWSER_ZIP_FILE_NAME);
+  SET_STRING("ZIP_ITEMS_REMAINING", IDS_FILE_BROWSER_ZIP_ITEMS_REMAINING);
+  SET_STRING("ZIP_CANCELLED", IDS_FILE_BROWSER_ZIP_CANCELLED);
+  SET_STRING("ZIP_TARGET_EXISTS_ERROR",
+             IDS_FILE_BROWSER_ZIP_TARGET_EXISTS_ERROR);
+  SET_STRING("ZIP_FILESYSTEM_ERROR", IDS_FILE_BROWSER_ZIP_FILESYSTEM_ERROR);
+  SET_STRING("ZIP_UNEXPECTED_ERROR", IDS_FILE_BROWSER_ZIP_UNEXPECTED_ERROR);
+  SET_STRING("SHARE_ERROR", IDS_FILE_BROWSER_SHARE_ERROR);
+
+  SET_STRING("DELETED_MESSAGE_PLURAL", IDS_FILE_BROWSER_DELETED_MESSAGE_PLURAL);
+  SET_STRING("DELETED_MESSAGE", IDS_FILE_BROWSER_DELETED_MESSAGE);
+  SET_STRING("DELETE_ERROR", IDS_FILE_BROWSER_DELETE_ERROR);
+  SET_STRING("UNDO_DELETE", IDS_FILE_BROWSER_UNDO_DELETE);
+
+  SET_STRING("CANCEL_LABEL", IDS_FILE_BROWSER_CANCEL_LABEL);
+  SET_STRING("OPEN_LABEL", IDS_FILE_BROWSER_OPEN_LABEL);
+  SET_STRING("SAVE_LABEL", IDS_FILE_BROWSER_SAVE_LABEL);
+  SET_STRING("OK_LABEL", IDS_FILE_BROWSER_OK_LABEL);
+  SET_STRING("UPLOAD_LABEL", IDS_FILE_BROWSER_UPLOAD_LABEL);
+  SET_STRING("VIEW_LABEL", IDS_FILE_BROWSER_VIEW_LABEL);
+
+  SET_STRING("DEFAULT_NEW_FOLDER_NAME",
+             IDS_FILE_BROWSER_DEFAULT_NEW_FOLDER_NAME);
+  SET_STRING("MORE_FILES", IDS_FILE_BROWSER_MORE_FILES);
+
+  SET_STRING("CONFIRM_DELETE_ONE", IDS_FILE_BROWSER_CONFIRM_DELETE_ONE);
+  SET_STRING("CONFIRM_DELETE_SOME", IDS_FILE_BROWSER_CONFIRM_DELETE_SOME);
+
+  SET_STRING("UNKNOWN_FILESYSTEM_WARNING",
+             IDS_FILE_BROWSER_UNKNOWN_FILESYSTEM_WARNING);
+  SET_STRING("UNSUPPORTED_FILESYSTEM_WARNING",
+             IDS_FILE_BROWSER_UNSUPPORTED_FILESYSTEM_WARNING);
+  SET_STRING("FORMATTING_WARNING", IDS_FILE_BROWSER_FORMATTING_WARNING);
+
+  SET_STRING("DRIVE_MENU_HELP", IDS_FILE_BROWSER_DRIVE_MENU_HELP);
+  SET_STRING("DRIVE_SHOW_HOSTED_FILES_OPTION",
+             IDS_FILE_BROWSER_DRIVE_SHOW_HOSTED_FILES_OPTION);
+  SET_STRING("DRIVE_MOBILE_CONNECTION_OPTION",
+             IDS_FILE_BROWSER_DRIVE_MOBILE_CONNECTION_OPTION);
+  SET_STRING("DRIVE_CLEAR_LOCAL_CACHE",
+             IDS_FILE_BROWSER_DRIVE_CLEAR_LOCAL_CACHE);
+  SET_STRING("DRIVE_SPACE_AVAILABLE_LONG",
+             IDS_FILE_BROWSER_DRIVE_SPACE_AVAILABLE_LONG);
+  SET_STRING("DRIVE_BUY_MORE_SPACE", IDS_FILE_BROWSER_DRIVE_BUY_MORE_SPACE);
+  SET_STRING("DRIVE_BUY_MORE_SPACE_LINK",
+             IDS_FILE_BROWSER_DRIVE_BUY_MORE_SPACE_LINK);
+  SET_STRING("DRIVE_VISIT_DRIVE_GOOGLE_COM",
+             IDS_FILE_BROWSER_DRIVE_VISIT_DRIVE_GOOGLE_COM);
+
+  SET_STRING("SELECT_FOLDER_TITLE", IDS_FILE_BROWSER_SELECT_FOLDER_TITLE);
+  SET_STRING("SELECT_OPEN_FILE_TITLE", IDS_FILE_BROWSER_SELECT_OPEN_FILE_TITLE);
+  SET_STRING("SELECT_OPEN_MULTI_FILE_TITLE",
+             IDS_FILE_BROWSER_SELECT_OPEN_MULTI_FILE_TITLE);
+  SET_STRING("SELECT_SAVEAS_FILE_TITLE",
+             IDS_FILE_BROWSER_SELECT_SAVEAS_FILE_TITLE);
+
+  SET_STRING("MANY_FILES_SELECTED", IDS_FILE_BROWSER_MANY_FILES_SELECTED);
+  SET_STRING("MANY_DIRECTORIES_SELECTED",
+             IDS_FILE_BROWSER_MANY_DIRECTORIES_SELECTED);
+  SET_STRING("MANY_ENTRIES_SELECTED", IDS_FILE_BROWSER_MANY_ENTRIES_SELECTED);
+  SET_STRING("CALCULATING_SIZE", IDS_FILE_BROWSER_CALCULATING_SIZE);
+
+  SET_STRING("OFFLINE_HEADER", IDS_FILE_BROWSER_OFFLINE_HEADER);
+  SET_STRING("OFFLINE_MESSAGE", IDS_FILE_BROWSER_OFFLINE_MESSAGE);
+  SET_STRING("OFFLINE_MESSAGE_PLURAL", IDS_FILE_BROWSER_OFFLINE_MESSAGE_PLURAL);
+  SET_STRING("HOSTED_OFFLINE_MESSAGE", IDS_FILE_BROWSER_HOSTED_OFFLINE_MESSAGE);
+  SET_STRING("HOSTED_OFFLINE_MESSAGE_PLURAL",
+             IDS_FILE_BROWSER_HOSTED_OFFLINE_MESSAGE_PLURAL);
+  SET_STRING("CONFIRM_MOBILE_DATA_USE",
+             IDS_FILE_BROWSER_CONFIRM_MOBILE_DATA_USE);
+  SET_STRING("CONFIRM_MOBILE_DATA_USE_PLURAL",
+             IDS_FILE_BROWSER_CONFIRM_MOBILE_DATA_USE_PLURAL);
+  SET_STRING("DRIVE_OUT_OF_SPACE_HEADER",
+             IDS_FILE_BROWSER_DRIVE_OUT_OF_SPACE_HEADER);
+  SET_STRING("DRIVE_OUT_OF_SPACE_MESSAGE",
+             IDS_FILE_BROWSER_DRIVE_OUT_OF_SPACE_MESSAGE);
+  SET_STRING("DRIVE_SERVER_OUT_OF_SPACE_HEADER",
+             IDS_FILE_BROWSER_DRIVE_SERVER_OUT_OF_SPACE_HEADER);
+  SET_STRING("DRIVE_SERVER_OUT_OF_SPACE_MESSAGE",
+             IDS_FILE_BROWSER_DRIVE_SERVER_OUT_OF_SPACE_MESSAGE);
+  SET_STRING("DRIVE_WELCOME_TITLE", IDS_FILE_BROWSER_DRIVE_WELCOME_TITLE);
+  SET_STRING("DRIVE_WELCOME_TEXT_SHORT",
+             IDS_FILE_BROWSER_DRIVE_WELCOME_TEXT_SHORT);
+  SET_STRING("DRIVE_WELCOME_TEXT_LONG",
+             IDS_FILE_BROWSER_DRIVE_WELCOME_TEXT_LONG);
+  SET_STRING("DRIVE_WELCOME_DISMISS", IDS_FILE_BROWSER_DRIVE_WELCOME_DISMISS);
+  SET_STRING("DRIVE_WELCOME_TITLE_ALTERNATIVE",
+             IDS_FILE_BROWSER_DRIVE_WELCOME_TITLE_ALTERNATIVE);
+  SET_STRING("DRIVE_WELCOME_TITLE_ALTERNATIVE_1TB",
+             IDS_FILE_BROWSER_DRIVE_WELCOME_TITLE_ALTERNATIVE_1TB);
+  SET_STRING("DRIVE_WELCOME_CHECK_ELIGIBILITY",
+             IDS_FILE_BROWSER_DRIVE_WELCOME_CHECK_ELIGIBILITY);
+  SET_STRING("NO_ACTION_FOR_FILE", IDS_FILE_BROWSER_NO_ACTION_FOR_FILE);
+  SET_STRING("NO_ACTION_FOR_EXECUTABLE",
+             IDS_FILE_BROWSER_NO_ACTION_FOR_EXECUTABLE);
+
+  // MP3 metadata extractor plugin
+  SET_STRING("ID3_ALBUM", IDS_FILE_BROWSER_ID3_ALBUM);                // TALB
+  SET_STRING("ID3_BPM", IDS_FILE_BROWSER_ID3_BPM);                    // TBPM
+  SET_STRING("ID3_COMPOSER", IDS_FILE_BROWSER_ID3_COMPOSER);          // TCOM
+  SET_STRING("ID3_COPYRIGHT_MESSAGE",
+             IDS_FILE_BROWSER_ID3_COPYRIGHT_MESSAGE);                 // TCOP
+  SET_STRING("ID3_DATE", IDS_FILE_BROWSER_ID3_DATE);                  // TDAT
+  SET_STRING("ID3_PLAYLIST_DELAY",
+             IDS_FILE_BROWSER_ID3_PLAYLIST_DELAY);                    // TDLY
+  SET_STRING("ID3_ENCODED_BY", IDS_FILE_BROWSER_ID3_ENCODED_BY);      // TENC
+  SET_STRING("ID3_LYRICIST", IDS_FILE_BROWSER_ID3_LYRICIST);          // TEXT
+  SET_STRING("ID3_FILE_TYPE", IDS_FILE_BROWSER_ID3_FILE_TYPE);        // TFLT
+  SET_STRING("ID3_TIME", IDS_FILE_BROWSER_ID3_TIME);                  // TIME
+  SET_STRING("ID3_TITLE", IDS_FILE_BROWSER_ID3_TITLE);                // TIT2
+  SET_STRING("ID3_LENGTH", IDS_FILE_BROWSER_ID3_LENGTH);              // TLEN
+  SET_STRING("ID3_FILE_OWNER", IDS_FILE_BROWSER_ID3_FILE_OWNER);      // TOWN
+  SET_STRING("ID3_LEAD_PERFORMER",
+             IDS_FILE_BROWSER_ID3_LEAD_PERFORMER);                    // TPE1
+  SET_STRING("ID3_BAND", IDS_FILE_BROWSER_ID3_BAND);                  // TPE2
+  SET_STRING("ID3_TRACK_NUMBER", IDS_FILE_BROWSER_ID3_TRACK_NUMBER);  // TRCK
+  SET_STRING("ID3_YEAR", IDS_FILE_BROWSER_ID3_YEAR);                  // TYER
+  SET_STRING("ID3_COPYRIGHT", IDS_FILE_BROWSER_ID3_COPYRIGHT);        // WCOP
+  SET_STRING("ID3_OFFICIAL_AUDIO_FILE_WEBPAGE",
+             IDS_FILE_BROWSER_ID3_OFFICIAL_AUDIO_FILE_WEBPAGE);       // WOAF
+  SET_STRING("ID3_OFFICIAL_ARTIST",
+             IDS_FILE_BROWSER_ID3_OFFICIAL_ARTIST);                   // WOAR
+  SET_STRING("ID3_OFFICIAL_AUDIO_SOURCE_WEBPAGE",
+             IDS_FILE_BROWSER_ID3_OFFICIAL_AUDIO_SOURCE_WEBPAGE);     // WOAS
+  SET_STRING("ID3_PUBLISHERS_OFFICIAL_WEBPAGE",
+             IDS_FILE_BROWSER_ID3_PUBLISHERS_OFFICIAL_WEBPAGE);       // WPUB
+  SET_STRING("ID3_USER_DEFINED_URL_LINK_FRAME",
+             IDS_FILE_BROWSER_ID3_USER_DEFINED_URL_LINK_FRAME);       // WXXX
+
+  // File types
+  SET_STRING("FOLDER", IDS_FILE_BROWSER_FOLDER);
+  SET_STRING("GENERIC_FILE_TYPE", IDS_FILE_BROWSER_GENERIC_FILE_TYPE);
+  SET_STRING("NO_EXTENSION_FILE_TYPE", IDS_FILE_BROWSER_NO_EXTENSION_FILE_TYPE);
+  SET_STRING("DEVICE", IDS_FILE_BROWSER_DEVICE);
+  SET_STRING("IMAGE_FILE_TYPE", IDS_FILE_BROWSER_IMAGE_FILE_TYPE);
+  SET_STRING("VIDEO_FILE_TYPE", IDS_FILE_BROWSER_VIDEO_FILE_TYPE);
+  SET_STRING("AUDIO_FILE_TYPE", IDS_FILE_BROWSER_AUDIO_FILE_TYPE);
+  SET_STRING("HTML_DOCUMENT_FILE_TYPE",
+             IDS_FILE_BROWSER_HTML_DOCUMENT_FILE_TYPE);
+  SET_STRING("ZIP_ARCHIVE_FILE_TYPE", IDS_FILE_BROWSER_ZIP_ARCHIVE_FILE_TYPE);
+  SET_STRING("RAR_ARCHIVE_FILE_TYPE", IDS_FILE_BROWSER_RAR_ARCHIVE_FILE_TYPE);
+  SET_STRING("TAR_ARCHIVE_FILE_TYPE", IDS_FILE_BROWSER_TAR_ARCHIVE_FILE_TYPE);
+  SET_STRING("TAR_BZIP2_ARCHIVE_FILE_TYPE",
+             IDS_FILE_BROWSER_TAR_BZIP2_ARCHIVE_FILE_TYPE);
+  SET_STRING("TAR_GZIP_ARCHIVE_FILE_TYPE",
+             IDS_FILE_BROWSER_TAR_GZIP_ARCHIVE_FILE_TYPE);
+  SET_STRING("PLAIN_TEXT_FILE_TYPE", IDS_FILE_BROWSER_PLAIN_TEXT_FILE_TYPE);
+  SET_STRING("PDF_DOCUMENT_FILE_TYPE", IDS_FILE_BROWSER_PDF_DOCUMENT_FILE_TYPE);
+  SET_STRING("WORD_DOCUMENT_FILE_TYPE",
+             IDS_FILE_BROWSER_WORD_DOCUMENT_FILE_TYPE);
+  SET_STRING("POWERPOINT_PRESENTATION_FILE_TYPE",
+             IDS_FILE_BROWSER_POWERPOINT_PRESENTATION_FILE_TYPE);
+  SET_STRING("EXCEL_FILE_TYPE", IDS_FILE_BROWSER_EXCEL_FILE_TYPE);
+
+  SET_STRING("GDOC_DOCUMENT_FILE_TYPE",
+             IDS_FILE_BROWSER_GDOC_DOCUMENT_FILE_TYPE);
+  SET_STRING("GSHEET_DOCUMENT_FILE_TYPE",
+             IDS_FILE_BROWSER_GSHEET_DOCUMENT_FILE_TYPE);
+  SET_STRING("GSLIDES_DOCUMENT_FILE_TYPE",
+             IDS_FILE_BROWSER_GSLIDES_DOCUMENT_FILE_TYPE);
+  SET_STRING("GDRAW_DOCUMENT_FILE_TYPE",
+             IDS_FILE_BROWSER_GDRAW_DOCUMENT_FILE_TYPE);
+  SET_STRING("GTABLE_DOCUMENT_FILE_TYPE",
+             IDS_FILE_BROWSER_GTABLE_DOCUMENT_FILE_TYPE);
+  SET_STRING("GLINK_DOCUMENT_FILE_TYPE",
+             IDS_FILE_BROWSER_GLINK_DOCUMENT_FILE_TYPE);
+
+  SET_STRING("DRIVE_LOADING", IDS_FILE_BROWSER_DRIVE_LOADING);
+  SET_STRING("DRIVE_CANNOT_REACH", IDS_FILE_BROWSER_DRIVE_CANNOT_REACH);
+  SET_STRING("DRIVE_LEARN_MORE", IDS_FILE_BROWSER_DRIVE_LEARN_MORE);
+  SET_STRING("DRIVE_RETRY", IDS_FILE_BROWSER_DRIVE_RETRY);
+
+  SET_STRING("AUDIO_PLAYER_TITLE", IDS_FILE_BROWSER_AUDIO_PLAYER_TITLE);
+  SET_STRING("AUDIO_PLAYER_DEFAULT_ARTIST",
+             IDS_FILE_BROWSER_AUDIO_PLAYER_DEFAULT_ARTIST);
+
+  SET_STRING("FILE_ERROR_GENERIC", IDS_FILE_BROWSER_FILE_ERROR_GENERIC);
+  SET_STRING("FILE_ERROR_NOT_FOUND", IDS_FILE_BROWSER_FILE_ERROR_NOT_FOUND);
+  SET_STRING("FILE_ERROR_SECURITY", IDS_FILE_BROWSER_FILE_ERROR_SECURITY);
+  SET_STRING("FILE_ERROR_NOT_READABLE",
+             IDS_FILE_BROWSER_FILE_ERROR_NOT_READABLE);
+  SET_STRING("FILE_ERROR_NO_MODIFICATION_ALLOWED",
+             IDS_FILE_BROWSER_FILE_ERROR_NO_MODIFICATION_ALLOWED);
+  SET_STRING("FILE_ERROR_INVALID_STATE",
+             IDS_FILE_BROWSER_FILE_ERROR_INVALID_STATE);
+  SET_STRING("FILE_ERROR_INVALID_MODIFICATION",
+             IDS_FILE_BROWSER_FILE_ERROR_INVALID_MODIFICATION);
+  SET_STRING("FILE_ERROR_PATH_EXISTS", IDS_FILE_BROWSER_FILE_ERROR_PATH_EXISTS);
+  SET_STRING("FILE_ERROR_QUOTA_EXCEEDED",
+             IDS_FILE_BROWSER_FILE_ERROR_QUOTA_EXCEEDED);
+
+  SET_STRING("SEARCH_DRIVE_HTML", IDS_FILE_BROWSER_SEARCH_DRIVE_HTML);
+  SET_STRING("SEARCH_NO_MATCHING_FILES_HTML",
+             IDS_FILE_BROWSER_SEARCH_NO_MATCHING_FILES_HTML);
+  SET_STRING("SEARCH_EXPAND", IDS_FILE_BROWSER_SEARCH_EXPAND);
+  SET_STRING("SEARCH_SPINNER", IDS_FILE_BROWSER_SEARCH_SPINNER);
+
+  SET_STRING("CHANGE_DEFAULT_MENU_ITEM",
+             IDS_FILE_BROWSER_CHANGE_DEFAULT_MENU_ITEM);
+  SET_STRING("CHANGE_DEFAULT_CAPTION", IDS_FILE_BROWSER_CHANGE_DEFAULT_CAPTION);
+  SET_STRING("DEFAULT_ACTION_LABEL", IDS_FILE_BROWSER_DEFAULT_ACTION_LABEL);
+
+  SET_STRING("DETAIL_VIEW_TOOLTIP", IDS_FILE_BROWSER_DETAIL_VIEW_TOOLTIP);
+  SET_STRING("THUMBNAIL_VIEW_TOOLTIP", IDS_FILE_BROWSER_THUMBNAIL_VIEW_TOOLTIP);
+  SET_STRING("GEAR_BUTTON_TOOLTIP", IDS_FILE_BROWSER_GEAR_BUTTON_TOOLTIP);
+
+  SET_STRING("TIME_TODAY", IDS_FILE_BROWSER_TIME_TODAY);
+  SET_STRING("TIME_YESTERDAY", IDS_FILE_BROWSER_TIME_YESTERDAY);
+
+  SET_STRING("ALL_FILES_FILTER", IDS_FILE_BROWSER_ALL_FILES_FILTER);
+
+  SET_STRING("SPACE_AVAILABLE", IDS_FILE_BROWSER_SPACE_AVAILABLE);
+  SET_STRING("WAITING_FOR_SPACE_INFO", IDS_FILE_BROWSER_WAITING_FOR_SPACE_INFO);
+  SET_STRING("FAILED_SPACE_INFO", IDS_FILE_BROWSER_FAILED_SPACE_INFO);
+
+  SET_STRING("DRIVE_NOT_REACHED", IDS_FILE_BROWSER_DRIVE_NOT_REACHED);
+
+  SET_STRING("HELP_LINK_LABEL", IDS_FILE_BROWSER_HELP_LINK_LABEL);
+#undef SET_STRING
+
+  dict->SetBoolean("PDF_VIEW_ENABLED",
+                   util::ShouldBeOpenedWithPlugin(profile(), ".pdf"));
+  dict->SetBoolean("SWF_VIEW_ENABLED",
+                   util::ShouldBeOpenedWithPlugin(profile(), ".swf"));
+
+  webui::SetFontAndTextDirection(dict);
+
+  std::string board;
+  chromeos::system::StatisticsProvider* provider =
+      chromeos::system::StatisticsProvider::GetInstance();
+  if (!provider->GetMachineStatistic(chromeos::system::kMachineInfoBoard,
+                                     &board)) {
+    board = "unknown";
+  }
+  dict->SetString(chromeos::system::kMachineInfoBoard, board);
+  return true;
+}
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.h b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.h
new file mode 100644
index 0000000..abdb172
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.h
@@ -0,0 +1,32 @@
+// Copyright 2013 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.
+//
+// This file only provides getStrings() as the .cc file for it is big.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_STRINGS_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_STRINGS_H_
+
+#include "chrome/browser/extensions/extension_function.h"
+
+namespace file_manager {
+
+// Implements the chrome.fileBrowserPrivate.getStrings method.
+// Used to get strings for the file manager from JavaScript.
+class GetStringsFunction : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getStrings",
+                             FILEBROWSERPRIVATE_GETSTRINGS)
+
+  GetStringsFunction();
+
+ protected:
+  virtual ~GetStringsFunction();
+
+  // SyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_STRINGS_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
new file mode 100644
index 0000000..cb5380d
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
@@ -0,0 +1,656 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h"
+
+#include "chrome/browser/chromeos/drive/drive_app_registry.h"
+#include "chrome/browser/chromeos/drive/drive_integration_service.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
+#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
+#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
+#include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+
+using content::BrowserContext;
+using extensions::app_file_handler_util::FindFileHandlersForFiles;
+using extensions::app_file_handler_util::PathAndMimeTypeSet;
+using extensions::Extension;
+using fileapi::FileSystemURL;
+
+namespace file_manager {
+namespace {
+
+// Error messages.
+const char kInvalidFileUrl[] = "Invalid file URL";
+
+// Default icon path for drive docs.
+const char kDefaultIcon[] = "images/filetype_generic.png";
+
+// Logs the default task for debugging.
+void LogDefaultTask(const std::set<std::string>& mime_types,
+                    const std::set<std::string>& suffixes,
+                    const std::string& task_id) {
+  if (!mime_types.empty()) {
+    std::string mime_types_str;
+    for (std::set<std::string>::const_iterator iter = mime_types.begin();
+         iter != mime_types.end(); ++iter) {
+      if (iter == mime_types.begin()) {
+        mime_types_str = *iter;
+      } else {
+        mime_types_str += ", " + *iter;
+      }
+    }
+    VLOG(1) << "Associating task " << task_id
+            << " with the following MIME types: ";
+    VLOG(1) << "  " << mime_types_str;
+  }
+
+  if (!suffixes.empty()) {
+    std::string suffixes_str;
+    for (std::set<std::string>::const_iterator iter = suffixes.begin();
+         iter != suffixes.end(); ++iter) {
+      if (iter == suffixes.begin()) {
+        suffixes_str = *iter;
+      } else {
+        suffixes_str += ", " + *iter;
+      }
+    }
+    VLOG(1) << "Associating task " << task_id
+            << " with the following suffixes: ";
+    VLOG(1) << "  " << suffixes_str;
+  }
+}
+
+// Returns a task id for the web app with |app_id|.
+std::string MakeWebAppTaskId(const std::string& app_id) {
+  // TODO(gspencer): For now, the action id is always "open-with", but we
+  // could add any actions that the drive app supports.
+  return file_tasks::MakeTaskID(
+      app_id, file_tasks::kTaskDrive, "open-with");
+}
+
+// Gets the mime types for the given file paths.
+void GetMimeTypesForFileURLs(const std::vector<base::FilePath>& file_paths,
+                             PathAndMimeTypeSet* files) {
+  for (std::vector<base::FilePath>::const_iterator iter = file_paths.begin();
+       iter != file_paths.end(); ++iter) {
+    files->insert(
+        std::make_pair(*iter, util::GetMimeTypeForPath(*iter)));
+  }
+}
+
+// Make a set of unique filename suffixes out of the list of file URLs.
+std::set<std::string> GetUniqueSuffixes(base::ListValue* file_url_list,
+                                        fileapi::FileSystemContext* context) {
+  std::set<std::string> suffixes;
+  for (size_t i = 0; i < file_url_list->GetSize(); ++i) {
+    std::string url_str;
+    if (!file_url_list->GetString(i, &url_str))
+      return std::set<std::string>();
+    FileSystemURL url = context->CrackURL(GURL(url_str));
+    if (!url.is_valid() || url.path().empty())
+      return std::set<std::string>();
+    // We'll skip empty suffixes.
+    if (!url.path().Extension().empty())
+      suffixes.insert(url.path().Extension());
+  }
+  return suffixes;
+}
+
+// Make a set of unique MIME types out of the list of MIME types.
+std::set<std::string> GetUniqueMimeTypes(base::ListValue* mime_type_list) {
+  std::set<std::string> mime_types;
+  for (size_t i = 0; i < mime_type_list->GetSize(); ++i) {
+    std::string mime_type;
+    if (!mime_type_list->GetString(i, &mime_type))
+      return std::set<std::string>();
+    // We'll skip empty MIME types.
+    if (!mime_type.empty())
+      mime_types.insert(mime_type);
+  }
+  return mime_types;
+}
+
+}  // namespace
+
+ExecuteTaskFunction::ExecuteTaskFunction() {
+}
+
+ExecuteTaskFunction::~ExecuteTaskFunction() {
+}
+
+bool ExecuteTaskFunction::RunImpl() {
+  // First param is task id that was to the extension with getFileTasks call.
+  std::string task_id;
+  if (!args_->GetString(0, &task_id) || !task_id.size())
+    return false;
+
+  // TODO(kaznacheev): Crack the task_id here, store it in the Executor
+  // and avoid passing it around.
+
+  // The second param is the list of files that need to be executed with this
+  // task.
+  ListValue* files_list = NULL;
+  if (!args_->GetList(1, &files_list))
+    return false;
+
+  std::string extension_id;
+  std::string task_type;
+  std::string action_id;
+  if (!file_tasks::CrackTaskID(
+          task_id, &extension_id, &task_type, &action_id)) {
+    LOG(WARNING) << "Invalid task " << task_id;
+    return false;
+  }
+
+  if (!files_list->GetSize())
+    return true;
+
+  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
+  scoped_refptr<fileapi::FileSystemContext> file_system_context =
+      BrowserContext::GetStoragePartition(profile(), site_instance)->
+      GetFileSystemContext();
+
+  std::vector<FileSystemURL> file_urls;
+  for (size_t i = 0; i < files_list->GetSize(); i++) {
+    std::string file_url_str;
+    if (!files_list->GetString(i, &file_url_str)) {
+      error_ = kInvalidFileUrl;
+      return false;
+    }
+    FileSystemURL url = file_system_context->CrackURL(GURL(file_url_str));
+    if (!chromeos::FileSystemBackend::CanHandleURL(url)) {
+      error_ = kInvalidFileUrl;
+      return false;
+    }
+    file_urls.push_back(url);
+  }
+
+  int32 tab_id = util::GetTabId(dispatcher());
+  return file_tasks::ExecuteFileTask(
+      profile(),
+      source_url(),
+      extension_->id(),
+      tab_id,
+      extension_id,
+      task_type,
+      action_id,
+      file_urls,
+      base::Bind(&ExecuteTaskFunction::OnTaskExecuted, this));
+}
+
+void ExecuteTaskFunction::OnTaskExecuted(bool success) {
+  SetResult(new base::FundamentalValue(success));
+  SendResponse(true);
+}
+
+struct GetFileTasksFunction::FileInfo {
+  GURL file_url;
+  base::FilePath file_path;
+  std::string mime_type;
+};
+
+struct GetFileTasksFunction::TaskInfo {
+  TaskInfo(const string16& app_name, const GURL& icon_url)
+      : app_name(app_name), icon_url(icon_url) {
+  }
+
+  string16 app_name;
+  GURL icon_url;
+};
+
+GetFileTasksFunction::GetFileTasksFunction() {
+}
+
+GetFileTasksFunction::~GetFileTasksFunction() {
+}
+
+// static
+void GetFileTasksFunction::GetAvailableDriveTasks(
+    drive::DriveAppRegistry* registry,
+    const FileInfoList& file_info_list,
+    TaskInfoMap* task_info_map) {
+  DCHECK(registry);
+  DCHECK(task_info_map);
+  DCHECK(task_info_map->empty());
+
+  bool is_first = true;
+  for (size_t i = 0; i < file_info_list.size(); ++i) {
+    const FileInfo& file_info = file_info_list[i];
+    if (file_info.file_path.empty())
+      continue;
+
+    ScopedVector<drive::DriveAppInfo> app_info_list;
+    registry->GetAppsForFile(
+        file_info.file_path, file_info.mime_type, &app_info_list);
+
+    if (is_first) {
+      // For the first file, we store all the info.
+      for (size_t j = 0; j < app_info_list.size(); ++j) {
+        const drive::DriveAppInfo& app_info = *app_info_list[j];
+        GURL icon_url = util::FindPreferredIcon(app_info.app_icons,
+                                                util::kPreferredIconSize);
+        task_info_map->insert(std::pair<std::string, TaskInfo>(
+            MakeWebAppTaskId(app_info.app_id),
+            TaskInfo(app_info.app_name, icon_url)));
+      }
+    } else {
+      // For remaining files, take the intersection with the current result,
+      // based on the task id.
+      std::set<std::string> task_id_set;
+      for (size_t j = 0; j < app_info_list.size(); ++j) {
+        task_id_set.insert(MakeWebAppTaskId(app_info_list[j]->app_id));
+      }
+      for (TaskInfoMap::iterator iter = task_info_map->begin();
+           iter != task_info_map->end(); ) {
+        if (task_id_set.find(iter->first) == task_id_set.end()) {
+          task_info_map->erase(iter++);
+        } else {
+          ++iter;
+        }
+      }
+    }
+
+    is_first = false;
+  }
+}
+
+void GetFileTasksFunction::FindDefaultDriveTasks(
+    const FileInfoList& file_info_list,
+    const TaskInfoMap& task_info_map,
+    std::set<std::string>* default_tasks) {
+  DCHECK(default_tasks);
+
+  for (size_t i = 0; i < file_info_list.size(); ++i) {
+    const FileInfo& file_info = file_info_list[i];
+    std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs(
+        profile_, file_info.mime_type, file_info.file_path.Extension());
+    if (task_info_map.find(task_id) != task_info_map.end())
+      default_tasks->insert(task_id);
+  }
+}
+
+// static
+void GetFileTasksFunction::CreateDriveTasks(
+    const TaskInfoMap& task_info_map,
+    const std::set<std::string>& default_tasks,
+    ListValue* result_list,
+    bool* default_already_set) {
+  DCHECK(result_list);
+  DCHECK(default_already_set);
+
+  for (TaskInfoMap::const_iterator iter = task_info_map.begin();
+       iter != task_info_map.end(); ++iter) {
+    DictionaryValue* task = new DictionaryValue;
+    task->SetString("taskId", iter->first);
+    task->SetString("title", iter->second.app_name);
+
+    const GURL& icon_url = iter->second.icon_url;
+    if (!icon_url.is_empty())
+      task->SetString("iconUrl", icon_url.spec());
+
+    task->SetBoolean("driveApp", true);
+
+    // Once we set a default app, we don't want to set any more.
+    if (!(*default_already_set) &&
+        default_tasks.find(iter->first) != default_tasks.end()) {
+      task->SetBoolean("isDefault", true);
+      *default_already_set = true;
+    } else {
+      task->SetBoolean("isDefault", false);
+    }
+    result_list->Append(task);
+  }
+}
+
+// Find special tasks here for Drive (Blox) apps. Iterate through matching drive
+// apps and add them, with generated task ids. Extension ids will be the app_ids
+// from drive. We'll know that they are drive apps because the extension id will
+// begin with kDriveTaskExtensionPrefix.
+bool GetFileTasksFunction::FindDriveAppTasks(
+    const FileInfoList& file_info_list,
+    ListValue* result_list,
+    bool* default_already_set) {
+  DCHECK(result_list);
+  DCHECK(default_already_set);
+
+  if (file_info_list.empty())
+    return true;
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
+  // |integration_service| is NULL if Drive is disabled. We return true in this
+  // case because there might be other extension tasks, even if we don't have
+  // any to add.
+  if (!integration_service || !integration_service->drive_app_registry())
+    return true;
+
+  drive::DriveAppRegistry* registry =
+      integration_service->drive_app_registry();
+  DCHECK(registry);
+
+  // Map of task_id to TaskInfo of available tasks.
+  TaskInfoMap task_info_map;
+  GetAvailableDriveTasks(registry, file_info_list, &task_info_map);
+  std::set<std::string> default_tasks;
+  FindDefaultDriveTasks(file_info_list, task_info_map, &default_tasks);
+  CreateDriveTasks(
+      task_info_map, default_tasks, result_list, default_already_set);
+  return true;
+}
+
+bool GetFileTasksFunction::FindAppTasks(
+    const std::vector<base::FilePath>& file_paths,
+    ListValue* result_list,
+    bool* default_already_set) {
+  DCHECK(!file_paths.empty());
+  ExtensionService* service = profile_->GetExtensionService();
+  if (!service)
+    return false;
+
+  PathAndMimeTypeSet files;
+  GetMimeTypesForFileURLs(file_paths, &files);
+  std::set<std::string> default_tasks;
+  for (PathAndMimeTypeSet::iterator it = files.begin(); it != files.end();
+       ++it) {
+    default_tasks.insert(file_tasks::GetDefaultTaskIdFromPrefs(
+        profile_, it->second, it->first.Extension()));
+  }
+
+  for (ExtensionSet::const_iterator iter = service->extensions()->begin();
+       iter != service->extensions()->end();
+       ++iter) {
+    const Extension* extension = iter->get();
+
+    // We don't support using hosted apps to open files.
+    if (!extension->is_platform_app())
+      continue;
+
+    if (profile_->IsOffTheRecord() &&
+        !service->IsIncognitoEnabled(extension->id()))
+      continue;
+
+    typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList;
+    FileHandlerList file_handlers = FindFileHandlersForFiles(*extension, files);
+    if (file_handlers.empty())
+      continue;
+
+    for (FileHandlerList::iterator i = file_handlers.begin();
+         i != file_handlers.end(); ++i) {
+      DictionaryValue* task = new DictionaryValue;
+      std::string task_id = file_tasks::MakeTaskID(
+          extension->id(), file_tasks::kTaskApp, (*i)->id);
+      task->SetString("taskId", task_id);
+      task->SetString("title", (*i)->title);
+      if (!(*default_already_set) && ContainsKey(default_tasks, task_id)) {
+        task->SetBoolean("isDefault", true);
+        *default_already_set = true;
+      } else {
+        task->SetBoolean("isDefault", false);
+      }
+
+      GURL best_icon = extensions::ExtensionIconSource::GetIconURL(
+          extension,
+          util::kPreferredIconSize,
+          ExtensionIconSet::MATCH_BIGGER,
+          false,  // grayscale
+          NULL);  // exists
+      if (!best_icon.is_empty())
+        task->SetString("iconUrl", best_icon.spec());
+      else
+        task->SetString("iconUrl", kDefaultIcon);
+
+      task->SetBoolean("driveApp", false);
+      result_list->Append(task);
+    }
+  }
+
+  return true;
+}
+
+bool GetFileTasksFunction::RunImpl() {
+  // First argument is the list of files to get tasks for.
+  ListValue* files_list = NULL;
+  if (!args_->GetList(0, &files_list))
+    return false;
+
+  if (files_list->GetSize() == 0)
+    return false;
+
+  // Second argument is the list of mime types of each of the files in the list.
+  ListValue* mime_types_list = NULL;
+  if (!args_->GetList(1, &mime_types_list))
+    return false;
+
+  // MIME types can either be empty, or there needs to be one for each file.
+  if (mime_types_list->GetSize() != files_list->GetSize() &&
+      mime_types_list->GetSize() != 0)
+    return false;
+
+  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
+  scoped_refptr<fileapi::FileSystemContext> file_system_context =
+      BrowserContext::GetStoragePartition(profile(), site_instance)->
+      GetFileSystemContext();
+
+  // Collect all the URLs, convert them to GURLs, and crack all the urls into
+  // file paths.
+  FileInfoList info_list;
+  std::vector<GURL> file_urls;
+  std::vector<base::FilePath> file_paths;
+  bool has_google_document = false;
+  for (size_t i = 0; i < files_list->GetSize(); ++i) {
+    FileInfo info;
+    std::string file_url_str;
+    if (!files_list->GetString(i, &file_url_str))
+      return false;
+
+    if (mime_types_list->GetSize() != 0 &&
+        !mime_types_list->GetString(i, &info.mime_type))
+      return false;
+
+    GURL file_url(file_url_str);
+    fileapi::FileSystemURL file_system_url(
+        file_system_context->CrackURL(file_url));
+    if (!chromeos::FileSystemBackend::CanHandleURL(file_system_url))
+      continue;
+
+    file_urls.push_back(file_url);
+    file_paths.push_back(file_system_url.path());
+
+    info.file_url = file_url;
+    info.file_path = file_system_url.path();
+    info_list.push_back(info);
+
+    if (google_apis::ResourceEntry::ClassifyEntryKindByFileExtension(
+            info.file_path) &
+        google_apis::ResourceEntry::KIND_OF_GOOGLE_DOCUMENT) {
+      has_google_document = true;
+    }
+  }
+
+  ListValue* result_list = new ListValue();
+  SetResult(result_list);
+
+  // Find the Drive apps first, because we want them to take precedence
+  // when setting the default app.
+  bool default_already_set = false;
+  // Google document are not opened by drive apps but file manager.
+  if (!has_google_document) {
+    if (!FindDriveAppTasks(info_list, result_list, &default_already_set))
+      return false;
+  }
+
+  // Take the union of platform app file handlers, and all previous Drive
+  // and extension tasks. As above, we know there aren't duplicates because
+  // they're entirely different kinds of
+  // tasks.
+  if (!FindAppTasks(file_paths, result_list, &default_already_set))
+    return false;
+
+  // Take the union of Drive and extension tasks: Because any Drive tasks we
+  // found must apply to all of the files (intersection), and because the same
+  // is true of the extensions, we simply take the union of two lists by adding
+  // the extension tasks to the Drive task list. We know there aren't duplicates
+  // because they're entirely different kinds of tasks, but there could be both
+  // kinds of tasks for a file type (an image file, for instance).
+  file_tasks::FileBrowserHandlerList common_tasks;
+  file_tasks::FileBrowserHandlerList default_tasks;
+  if (!file_tasks::FindCommonTasks(profile_, file_urls, &common_tasks))
+    return false;
+  file_tasks::FindDefaultTasks(profile_, file_paths,
+                                      common_tasks, &default_tasks);
+
+  ExtensionService* service =
+      extensions::ExtensionSystem::Get(profile_)->extension_service();
+  for (file_tasks::FileBrowserHandlerList::const_iterator iter =
+           common_tasks.begin();
+       iter != common_tasks.end();
+       ++iter) {
+    const FileBrowserHandler* handler = *iter;
+    const std::string extension_id = handler->extension_id();
+    const Extension* extension = service->GetExtensionById(extension_id, false);
+    CHECK(extension);
+    DictionaryValue* task = new DictionaryValue;
+    task->SetString("taskId", file_tasks::MakeTaskID(
+        extension_id, file_tasks::kTaskFile, handler->id()));
+    task->SetString("title", handler->title());
+    // TODO(zelidrag): Figure out how to expose icon URL that task defined in
+    // manifest instead of the default extension icon.
+    GURL icon = extensions::ExtensionIconSource::GetIconURL(
+        extension,
+        extension_misc::EXTENSION_ICON_BITTY,
+        ExtensionIconSet::MATCH_BIGGER,
+        false,  // grayscale
+        NULL);  // exists
+    task->SetString("iconUrl", icon.spec());
+    task->SetBoolean("driveApp", false);
+
+    // Only set the default if there isn't already a default set.
+    if (!default_already_set &&
+        std::find(default_tasks.begin(), default_tasks.end(), *iter) !=
+        default_tasks.end()) {
+      task->SetBoolean("isDefault", true);
+      default_already_set = true;
+    } else {
+      task->SetBoolean("isDefault", false);
+    }
+
+    result_list->Append(task);
+  }
+
+  SendResponse(true);
+  return true;
+}
+
+SetDefaultTaskFunction::SetDefaultTaskFunction() {
+}
+
+SetDefaultTaskFunction::~SetDefaultTaskFunction() {
+}
+
+bool SetDefaultTaskFunction::RunImpl() {
+  // First param is task id that was to the extension with setDefaultTask call.
+  std::string task_id;
+  if (!args_->GetString(0, &task_id) || !task_id.size())
+    return false;
+
+  base::ListValue* file_url_list;
+  if (!args_->GetList(1, &file_url_list))
+    return false;
+
+  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
+  scoped_refptr<fileapi::FileSystemContext> context =
+      BrowserContext::GetStoragePartition(profile(), site_instance)->
+      GetFileSystemContext();
+
+  std::set<std::string> suffixes =
+      GetUniqueSuffixes(file_url_list, context.get());
+
+  // MIME types are an optional parameter.
+  base::ListValue* mime_type_list;
+  std::set<std::string> mime_types;
+  if (args_->GetList(2, &mime_type_list) && !mime_type_list->empty()) {
+    if (mime_type_list->GetSize() != file_url_list->GetSize())
+      return false;
+    mime_types = GetUniqueMimeTypes(mime_type_list);
+  }
+
+  if (VLOG_IS_ON(1))
+    LogDefaultTask(mime_types, suffixes, task_id);
+
+  // If there weren't any mime_types, and all the suffixes were blank,
+  // then we "succeed", but don't actually associate with anything.
+  // Otherwise, any time we set the default on a file with no extension
+  // on the local drive, we'd fail.
+  // TODO(gspencer): Fix file manager so that it never tries to set default in
+  // cases where extensionless local files are part of the selection.
+  if (suffixes.empty() && mime_types.empty()) {
+    SetResult(new base::FundamentalValue(true));
+    return true;
+  }
+
+  file_tasks::UpdateDefaultTask(profile_, task_id, suffixes, mime_types);
+
+  return true;
+}
+
+ViewFilesFunction::ViewFilesFunction() {
+}
+
+ViewFilesFunction::~ViewFilesFunction() {
+}
+
+bool ViewFilesFunction::RunImpl() {
+  if (args_->GetSize() < 1) {
+    return false;
+  }
+
+  ListValue* path_list = NULL;
+  args_->GetList(0, &path_list);
+  DCHECK(path_list);
+
+  std::string internal_task_id;
+  args_->GetString(1, &internal_task_id);
+
+  std::vector<base::FilePath> files;
+  for (size_t i = 0; i < path_list->GetSize(); ++i) {
+    std::string url_as_string;
+    path_list->GetString(i, &url_as_string);
+    base::FilePath path = util::GetLocalPathFromURL(
+        render_view_host(), profile(), GURL(url_as_string));
+    if (path.empty())
+      return false;
+    files.push_back(path);
+  }
+
+  Browser* browser = chrome::FindOrCreateTabbedBrowser(
+      profile_, chrome::HOST_DESKTOP_TYPE_ASH);
+  bool success = browser;
+
+  if (browser) {
+    for (size_t i = 0; i < files.size(); ++i) {
+      bool handled = util::ExecuteBuiltinHandler(
+          browser, files[i], internal_task_id);
+      if (!handled && files.size() == 1)
+        success = false;
+    }
+  }
+
+  SetResult(Value::CreateBooleanValue(success));
+  SendResponse(true);
+  return true;
+}
+
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h
new file mode 100644
index 0000000..a81bf94
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h
@@ -0,0 +1,133 @@
+// Copyright 2013 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.
+//
+// This file provides task related API functions.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_TASKS_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_TASKS_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
+
+namespace drive {
+class DriveAppRegistry;
+}
+
+namespace file_manager {
+
+// Implements the chrome.fileBrowserPrivate.executeTask method.
+class ExecuteTaskFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.executeTask",
+                             FILEBROWSERPRIVATE_EXECUTETASK)
+
+  ExecuteTaskFunction();
+
+ protected:
+  virtual ~ExecuteTaskFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+  void OnTaskExecuted(bool success);
+};
+
+// Implements the chrome.fileBrowserPrivate.getFileTasks method.
+class GetFileTasksFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getFileTasks",
+                             FILEBROWSERPRIVATE_GETFILETASKS)
+
+  GetFileTasksFunction();
+
+ protected:
+  virtual ~GetFileTasksFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  struct FileInfo;
+  typedef std::vector<FileInfo> FileInfoList;
+
+  // Holds fields to build a task result.
+  struct TaskInfo;
+
+  // Map from a task id to TaskInfo.
+  typedef std::map<std::string, TaskInfo> TaskInfoMap;
+
+  // Looks up available apps for each file in |file_info_list| in the
+  // |registry|, and returns the intersection of all available apps as a
+  // map from task id to TaskInfo.
+  static void GetAvailableDriveTasks(drive::DriveAppRegistry* registry,
+                                     const FileInfoList& file_info_list,
+                                     TaskInfoMap* task_info_map);
+
+  // Looks in the preferences and finds any of the available apps that are
+  // also listed as default apps for any of the files in the info list.
+  void FindDefaultDriveTasks(const FileInfoList& file_info_list,
+                             const TaskInfoMap& task_info_map,
+                             std::set<std::string>* default_tasks);
+
+  // Creates a list of each task in |task_info_map| and stores the result into
+  // |result_list|. If a default task is set in the result list,
+  // |default_already_set| is set to true.
+  static void CreateDriveTasks(const TaskInfoMap& task_info_map,
+                               const std::set<std::string>& default_tasks,
+                               ListValue* result_list,
+                               bool* default_already_set);
+
+  // Find the list of drive apps that can be used with the given file types. If
+  // a default task is set in the result list, then |default_already_set| is set
+  // to true.
+  bool FindDriveAppTasks(const FileInfoList& file_info_list,
+                         ListValue* result_list,
+                         bool* default_already_set);
+
+  // Find the list of app file handlers that can be used with the given file
+  // types, appending them to the |result_list|. If a default task is set in the
+  // result list, then |default_already_set| is set to true.
+  bool FindAppTasks(const std::vector<base::FilePath>& file_paths,
+                    ListValue* result_list,
+                    bool* default_already_set);
+};
+
+// Implements the chrome.fileBrowserPrivate.setDefaultTask method.
+class SetDefaultTaskFunction : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.setDefaultTask",
+                             FILEBROWSERPRIVATE_SETDEFAULTTASK)
+
+  SetDefaultTaskFunction();
+
+ protected:
+  virtual ~SetDefaultTaskFunction();
+
+  // SyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+// Implements the chrome.fileBrowserPrivate.viewFiles method.
+// Views multiple selected files.  Window stays open.
+class ViewFilesFunction : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.viewFiles",
+                             FILEBROWSERPRIVATE_VIEWFILES)
+
+  ViewFilesFunction();
+
+ protected:
+  virtual ~ViewFilesFunction();
+
+  // AsyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_TASKS_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
new file mode 100644
index 0000000..4998a4f
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
@@ -0,0 +1,187 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
+
+#include "base/files/file_path.h"
+#include "chrome/browser/chromeos/drive/drive.pb.h"
+#include "chrome/browser/chromeos/drive/drive_integration_service.h"
+#include "chrome/browser/chromeos/drive/file_errors.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
+#include "chrome/browser/extensions/extension_function_dispatcher.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/shell_dialogs/selected_file_info.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+
+using content::BrowserThread;
+using google_apis::InstalledApp;
+
+namespace file_manager {
+namespace util {
+namespace {
+
+// The struct is used for GetSelectedFileInfo().
+struct GetSelectedFileInfoParams {
+  bool for_opening;
+  GetSelectedFileInfoCallback callback;
+  std::vector<base::FilePath> file_paths;
+  std::vector<ui::SelectedFileInfo> selected_files;
+};
+
+// Forward declarations of helper functions for GetSelectedFileInfo().
+void ContinueGetSelectedFileInfo(Profile* profile,
+                                 scoped_ptr<GetSelectedFileInfoParams> params,
+                                 drive::FileError error,
+                                 const base::FilePath& local_file_path,
+                                 scoped_ptr<drive::ResourceEntry> entry);
+
+// Part of GetSelectedFileInfo().
+void GetSelectedFileInfoInternal(Profile* profile,
+                                 scoped_ptr<GetSelectedFileInfoParams> params) {
+  DCHECK(profile);
+
+  for (size_t i = params->selected_files.size();
+       i < params->file_paths.size(); ++i) {
+    const base::FilePath& file_path = params->file_paths[i];
+    // When opening a drive file, we should get local file path.
+    if (params->for_opening &&
+        drive::util::IsUnderDriveMountPoint(file_path)) {
+      drive::DriveIntegrationService* integration_service =
+          drive::DriveIntegrationServiceFactory::GetForProfile(profile);
+      // |integration_service| is NULL if Drive is disabled.
+      if (!integration_service) {
+        ContinueGetSelectedFileInfo(profile,
+                                    params.Pass(),
+                                    drive::FILE_ERROR_FAILED,
+                                    base::FilePath(),
+                                    scoped_ptr<drive::ResourceEntry>());
+        return;
+      }
+      integration_service->file_system()->GetFileByPath(
+          drive::util::ExtractDrivePath(file_path),
+          base::Bind(&ContinueGetSelectedFileInfo,
+                     profile,
+                     base::Passed(&params)));
+      return;
+    } else {
+      params->selected_files.push_back(
+          ui::SelectedFileInfo(file_path, base::FilePath()));
+    }
+  }
+  params->callback.Run(params->selected_files);
+}
+
+// Part of GetSelectedFileInfo().
+void ContinueGetSelectedFileInfo(Profile* profile,
+                                 scoped_ptr<GetSelectedFileInfoParams> params,
+                                 drive::FileError error,
+                                 const base::FilePath& local_file_path,
+                                 scoped_ptr<drive::ResourceEntry> entry) {
+  DCHECK(profile);
+
+  const int index = params->selected_files.size();
+  const base::FilePath& file_path = params->file_paths[index];
+  base::FilePath local_path;
+  if (error == drive::FILE_ERROR_OK) {
+    local_path = local_file_path;
+  } else {
+    DLOG(ERROR) << "Failed to get " << file_path.value()
+                << " with error code: " << error;
+  }
+  params->selected_files.push_back(ui::SelectedFileInfo(file_path, local_path));
+  GetSelectedFileInfoInternal(profile, params.Pass());
+}
+
+}  // namespace
+
+int32 GetTabId(ExtensionFunctionDispatcher* dispatcher) {
+  if (!dispatcher) {
+    LOG(WARNING) << "No dispatcher";
+    return 0;
+  }
+  if (!dispatcher->delegate()) {
+    LOG(WARNING) << "No delegate";
+    return 0;
+  }
+  content::WebContents* web_contents =
+      dispatcher->delegate()->GetAssociatedWebContents();
+  if (!web_contents) {
+    LOG(WARNING) << "No associated tab contents";
+    return 0;
+  }
+  return ExtensionTabUtil::GetTabId(web_contents);
+}
+
+GURL FindPreferredIcon(const InstalledApp::IconList& icons,
+                       int preferred_size) {
+  GURL result;
+  if (icons.empty())
+    return result;
+  result = icons.rbegin()->second;
+  for (InstalledApp::IconList::const_reverse_iterator iter = icons.rbegin();
+       iter != icons.rend() && iter->first >= preferred_size; ++iter) {
+    result = iter->second;
+  }
+  return result;
+}
+
+base::FilePath GetLocalPathFromURL(
+    content::RenderViewHost* render_view_host,
+    Profile* profile,
+    const GURL& url) {
+  DCHECK(render_view_host);
+  DCHECK(profile);
+
+  content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
+  scoped_refptr<fileapi::FileSystemContext> file_system_context =
+      content::BrowserContext::GetStoragePartition(profile, site_instance)->
+      GetFileSystemContext();
+
+  const fileapi::FileSystemURL filesystem_url(
+      file_system_context->CrackURL(url));
+  base::FilePath path;
+  if (!chromeos::FileSystemBackend::CanHandleURL(filesystem_url))
+    return base::FilePath();
+  return filesystem_url.path();
+}
+
+void GetSelectedFileInfo(content::RenderViewHost* render_view_host,
+                         Profile* profile,
+                         const std::vector<GURL>& file_urls,
+                         bool for_opening,
+                         GetSelectedFileInfoCallback callback) {
+  DCHECK(render_view_host);
+  DCHECK(profile);
+
+  scoped_ptr<GetSelectedFileInfoParams> params(new GetSelectedFileInfoParams);
+  params->for_opening = for_opening;
+  params->callback = callback;
+
+  for (size_t i = 0; i < file_urls.size(); ++i) {
+    const GURL& file_url = file_urls[i];
+    const base::FilePath path = GetLocalPathFromURL(
+        render_view_host, profile, file_url);
+    if (!path.empty()) {
+      DVLOG(1) << "Selected: file path: " << path.value();
+      params->file_paths.push_back(path);
+    }
+  }
+
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&GetSelectedFileInfoInternal,
+                 profile,
+                 base::Passed(&params)));
+}
+
+}  // namespace util
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.h b/chrome/browser/chromeos/extensions/file_manager/private_api_util.h
new file mode 100644
index 0000000..876266b
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.h
@@ -0,0 +1,69 @@
+// Copyright 2013 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.
+//
+// This file provides utility functions for fileBrowserPrivate API.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_UTIL_H_
+
+#include "base/callback_forward.h"
+#include "chrome/browser/google_apis/gdata_wapi_parser.h"
+#include "url/gurl.h"
+
+class ExtensionFunctionDispatcher;
+class Profile;
+
+namespace content {
+class RenderViewHost;
+}
+
+namespace ui {
+struct SelectedFileInfo;
+}
+
+namespace file_manager {
+namespace util {
+
+// Returns the ID of the tab associated with the dispatcher. Returns 0 on
+// error.
+int32 GetTabId(ExtensionFunctionDispatcher* dispatcher);
+
+// Finds an icon in the list of icons. If unable to find an icon of the exact
+// size requested, returns one with the next larger size. If all icons are
+// smaller than the preferred size, we'll return the largest one available.
+// Icons must be sorted by the icon size, smallest to largest. If there are no
+// icons in the list, returns an empty URL.
+GURL FindPreferredIcon(const google_apis::InstalledApp::IconList& icons,
+                       int preferred_size);
+
+// The preferred icon size, which should usually be used for FindPreferredIcon;
+const int kPreferredIconSize = 16;
+
+// Returns the local FilePath associated with |url|. If the file isn't of the
+// type FileSystemBackend handles, returns an empty
+// FilePath. |render_view_host| and |profile| are needed to obtain the
+// FileSystemContext currently in use.
+//
+// Local paths will look like "/home/chronos/user/Downloads/foo/bar.txt" or
+// "/special/drive/foo/bar.txt".
+base::FilePath GetLocalPathFromURL(
+    content::RenderViewHost* render_view_host,
+    Profile* profile,
+    const GURL& url);
+
+// The callback type is used for GetSelectedFileInfo().
+typedef base::Callback<void(const std::vector<ui::SelectedFileInfo>&)>
+    GetSelectedFileInfoCallback;
+
+// Gets the information for |file_urls|.
+void GetSelectedFileInfo(content::RenderViewHost* render_view_host,
+                         Profile* profile,
+                         const std::vector<GURL>& file_urls,
+                         bool for_opening,
+                         GetSelectedFileInfoCallback callback);
+
+}  // namespace util
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_UTIL_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/zip_file_creator.cc b/chrome/browser/chromeos/extensions/file_manager/zip_file_creator.cc
index 8833a52..4f81270 100644
--- a/chrome/browser/chromeos/extensions/file_manager/zip_file_creator.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/zip_file_creator.cc
@@ -23,7 +23,7 @@
 using content::BrowserThread;
 using content::UtilityProcessHost;
 
-namespace extensions {
+namespace file_manager {
 
 ZipFileCreator::ZipFileCreator(
     Observer* observer,
@@ -123,4 +123,4 @@
   observer_->OnZipDone(success);
 }
 
-}  // namespace extensions
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/zip_file_creator.h b/chrome/browser/chromeos/extensions/file_manager/zip_file_creator.h
index bc9defb..122d5cc 100644
--- a/chrome/browser/chromeos/extensions/file_manager/zip_file_creator.h
+++ b/chrome/browser/chromeos/extensions/file_manager/zip_file_creator.h
@@ -14,7 +14,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/utility_process_host_client.h"
 
-namespace extensions {
+namespace file_manager {
 
 // ZipFileCreator creates a ZIP file from a specified list of files and
 // directories under a common parent directory. This is done in a sandboxed
@@ -88,6 +88,6 @@
   bool got_response_;
 };
 
-}  // namespace extensions
+}  // namespace file_manager
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_ZIP_FILE_CREATOR_H_
diff --git a/chrome/browser/chromeos/extensions/info_private_api.cc b/chrome/browser/chromeos/extensions/info_private_api.cc
index 44db705..0a7f067 100644
--- a/chrome/browser/chromeos/extensions/info_private_api.cc
+++ b/chrome/browser/chromeos/extensions/info_private_api.cc
@@ -5,12 +5,15 @@
 #include "chrome/browser/chromeos/extensions/info_private_api.h"
 
 #include "base/values.h"
-#include "chrome/browser/chromeos/cros/network_library.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
 #include "chrome/browser/chromeos/system/statistics_provider.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state_handler.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 
-using chromeos::NetworkLibrary;
+using chromeos::NetworkHandler;
 
 namespace extensions {
 
@@ -64,8 +67,13 @@
     provider->GetMachineStatistic(chromeos::system::kHardwareClass, &hwid);
     return new base::StringValue(hwid);
   } else if (property_name == kPropertyHomeProvider) {
-    NetworkLibrary* netlib = NetworkLibrary::Get();
-    return new base::StringValue(netlib->GetCellularHomeCarrierId());
+    const chromeos::DeviceState* cellular_device =
+        NetworkHandler::Get()->network_state_handler()->GetDeviceStateByType(
+            flimflam::kTypeCellular);
+    std::string home_provider_id;
+    if (cellular_device)
+      home_provider_id = cellular_device->home_provider_id();
+    return new base::StringValue(home_provider_id);
   } else if (property_name == kPropertyInitialLocale) {
     return new base::StringValue(
         chromeos::StartupUtils::GetInitialLocale());
diff --git a/chrome/browser/chromeos/extensions/input_method_event_router.cc b/chrome/browser/chromeos/extensions/input_method_event_router.cc
index d0a82ac..e89acdc 100644
--- a/chrome/browser/chromeos/extensions/input_method_event_router.cc
+++ b/chrome/browser/chromeos/extensions/input_method_event_router.cc
@@ -9,7 +9,6 @@
 #include "base/json/json_writer.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/extensions/input_method_api.h"
-#include "chrome/browser/chromeos/web_socket_proxy_controller.h"
 #include "chrome/browser/extensions/event_names.h"
 #include "chrome/browser/extensions/event_router.h"
 #include "chrome/browser/extensions/extension_system.h"
diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
index 1d79a69..5fc5876 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
@@ -7,7 +7,7 @@
 #include <vector>
 
 #include "ash/shell.h"
-#include "ash/wm/window_cycle_controller.h"
+#include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_util.h"
 #include "base/file_util.h"
 #include "base/files/file_enumerator.h"
@@ -141,7 +141,7 @@
   }
 
   void BuildWindowListAndMinimizeInactive(aura::Window* active_window) {
-    windows_ = ash::WindowCycleController::BuildWindowList(NULL, false);
+    windows_ = ash::MruWindowTracker::BuildWindowList(false);
     // Remove active window.
     std::vector<aura::Window*>::iterator last =
         std::remove(windows_.begin(), windows_.end(), active_window);
diff --git a/chrome/browser/chromeos/external_metrics.cc b/chrome/browser/chromeos/external_metrics.cc
index 5c6b329..37e6703 100644
--- a/chrome/browser/chromeos/external_metrics.cc
+++ b/chrome/browser/chromeos/external_metrics.cc
@@ -19,6 +19,7 @@
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/file_util.h"
+#include "base/files/file_path.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/metrics/statistics_recorder.h"
@@ -96,10 +97,27 @@
   }
 }
 
+// Finds out if we're on a 2GB Parrot.
+//
+// This code reads and parses /etc/lsb-release. There are at least four other
+// places that open and parse /etc/lsb-release, and I wish I could fix the
+// mess.  At least this code is temporary.
+
+bool Is2GBParrot() {
+  base::FilePath path("/etc/lsb-release");
+  std::string contents;
+  if (!file_util::ReadFileToString(path, &contents))
+    return false;
+  if (contents.find("CHROMEOS_RELEASE_BOARD=parrot") == std::string::npos)
+    return false;
+  // There are 2GB and 4GB models.
+  return base::SysInfo::AmountOfPhysicalMemory() <= 2LL * 1024 * 1024 * 1024;
+}
+
 // Sets up field trial for measuring swap and CPU metrics after tab switch
 // and scroll events. crbug.com/253994
 void SetupSwapJankFieldTrial() {
-  const char name_of_experiment[] = "SwapJank64vs32";
+  const char name_of_experiment[] = "SwapJank64vs32Parrot";
 
   // Determine if this is a 32 or 64 bit build of Chrome.
   bool is_chrome_64 = sizeof(void*) == 8;
@@ -110,6 +128,9 @@
   // A 32 bit kernel requires 32 bit Chrome.
   DCHECK(is_kernel_64 || !is_chrome_64);
 
+  // Find out if we're on a 2GB Parrot.
+  bool is_parrot = Is2GBParrot();
+
   // All groups are either on or off.
   const base::FieldTrial::Probability kTotalProbability = 1;
   scoped_refptr<base::FieldTrial> trial =
@@ -120,11 +141,16 @@
   // Assign probability of 1 to this Chrome's group.  Assign 0 to all other
   // choices.
   trial->AppendGroup("kernel_64_chrome_64",
-                     is_kernel_64 && is_chrome_64 ? kTotalProbability : 0);
+                     is_parrot && is_kernel_64 && is_chrome_64 ?
+                     kTotalProbability : 0);
   trial->AppendGroup("kernel_64_chrome_32",
-                     is_kernel_64 && !is_chrome_64 ? kTotalProbability : 0);
+                     is_parrot && is_kernel_64 && !is_chrome_64 ?
+                     kTotalProbability : 0);
   trial->AppendGroup("kernel_32_chrome_32",
-                     !is_kernel_64 && !is_chrome_64 ? kTotalProbability : 0);
+                     is_parrot && !is_kernel_64 && !is_chrome_64 ?
+                     kTotalProbability : 0);
+  trial->AppendGroup("not_parrot",
+                     !is_parrot ? kTotalProbability : 0);
 
   // Announce the experiment to any listeners (especially important is the UMA
   // software, which will append the group names to UMA statistics).
@@ -152,8 +178,8 @@
   valid_user_actions_.insert("Updater.ServerCertificateChanged");
   valid_user_actions_.insert("Updater.ServerCertificateFailed");
 
-  // Initialize field trials that don't need to read from files.
-  SetupSwapJankFieldTrial();
+  // Initialize here field trials that don't need to read from files.
+  // (None for the moment.)
 
   // Initialize any chromeos field trials that need to read from a file (e.g.,
   // those that have an upstart script determine their experimental group for
@@ -388,6 +414,7 @@
   // Field trials that do not read from files can be initialized in
   // ExternalMetrics::Start() above.
   SetupProgressiveScanFieldTrial();
+  SetupSwapJankFieldTrial();
 
   ScheduleCollector();
 }
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.cc b/chrome/browser/chromeos/fileapi/file_system_backend.cc
index a259b1a..f25daa0 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.cc
@@ -16,17 +16,15 @@
 #include "chromeos/dbus/cros_disks_client.h"
 #include "webkit/browser/blob/file_stream_reader.h"
 #include "webkit/browser/fileapi/async_file_util_adapter.h"
-#include "webkit/browser/fileapi/copy_or_move_file_validator.h"
 #include "webkit/browser/fileapi/external_mount_points.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_file_stream_reader.h"
 #include "webkit/browser/fileapi/file_system_operation_context.h"
-#include "webkit/browser/fileapi/file_system_task_runners.h"
+#include "webkit/browser/fileapi/file_system_operation_impl.h"
 #include "webkit/browser/fileapi/file_system_url.h"
 #include "webkit/browser/fileapi/isolated_context.h"
 #include "webkit/browser/fileapi/isolated_file_util.h"
 #include "webkit/browser/fileapi/local_file_stream_writer.h"
-#include "webkit/browser/fileapi/local_file_system_operation.h"
 
 namespace {
 
@@ -251,8 +249,8 @@
   scoped_ptr<fileapi::FileSystemOperationContext> operation_context(
       new fileapi::FileSystemOperationContext(context));
   operation_context->set_root_path(GetFileSystemRootPath(url));
-  return new fileapi::LocalFileSystemOperation(url, context,
-                                               operation_context.Pass());
+  return new fileapi::FileSystemOperationImpl(url, context,
+                                              operation_context.Pass());
 }
 
 scoped_ptr<webkit_blob::FileStreamReader>
@@ -295,7 +293,7 @@
   DCHECK(url.type() == fileapi::kFileSystemTypeNativeLocal);
   return scoped_ptr<fileapi::FileStreamWriter>(
       new fileapi::LocalFileStreamWriter(
-          context->task_runners()->file_task_runner(), url.path(), offset));
+          context->default_file_task_runner(), url.path(), offset));
 }
 
 bool FileSystemBackend::GetVirtualPath(
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
index 0bef727..b43213a 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
@@ -12,9 +12,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/url_util.h"
 #include "webkit/browser/fileapi/external_mount_points.h"
-#include "webkit/browser/fileapi/file_permission_policy.h"
 #include "webkit/browser/fileapi/file_system_url.h"
-#include "webkit/browser/fileapi/isolated_context.h"
 #include "webkit/browser/quota/mock_special_storage_policy.h"
 
 #define FPL(x) FILE_PATH_LITERAL(x)
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index 56ade22..0b0fbae 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -94,6 +94,7 @@
   { "m17n:te:itrans",
     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_te_phone" },
   { "m17n:fa:isiri", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_fa" },
+  { "m17n:ar:kbd", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_ar" },
   // TODO(nona): Remove following migration map in M32
   { "m17n:zh:quick",
     "_comp_ime_ekbifjdfhkmdeeajnolmgdlmkllopefizh-hant-t-i0-und" },
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
index 978007e..d0547bd 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
@@ -306,15 +306,8 @@
   InitIBusBus();
   manager_->EnableLayouts("en-US", "");
   EXPECT_EQ(5U, manager_->GetNumActiveInputMethods());
-  {
-    // For http://crbug.com/19655#c11 - (1)
-    scoped_ptr<InputMethodDescriptors> methods(
-        manager_->GetActiveInputMethods());
-    const InputMethodDescriptor* id_to_find =
-        manager_->GetInputMethodUtil()->GetInputMethodDescriptorFromId(
-            "english-m");  // The "English Mystery" IME.
-    EXPECT_FALSE(Contain(*methods.get(), *id_to_find));
-  }
+  for (size_t i = 0; i < manager_->GetActiveInputMethodIds().size(); ++i)
+    LOG(ERROR) << manager_->GetActiveInputMethodIds().at(i);
   // For http://crbug.com/19655#c11 - (2)
   EXPECT_EQ(0, mock_ibus_daemon_controller_->start_count());
 
diff --git a/chrome/browser/chromeos/input_method/keyboard_browsertest.cc b/chrome/browser/chromeos/input_method/keyboard_browsertest.cc
new file mode 100644
index 0000000..21c41b9
--- /dev/null
+++ b/chrome/browser/chromeos/input_method/keyboard_browsertest.cc
@@ -0,0 +1,94 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/chromeos/input_method/textinput_test_helper.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+typedef TextInputTestBase KeyboardEventEndToEndTest;
+
+IN_PROC_BROWSER_TEST_F(KeyboardEventEndToEndTest, AltGrToCtrlAltKeyDown) {
+  TextInputTestHelper helper;
+
+  GURL url = ui_test_utils::GetTestUrl(
+      base::FilePath(FILE_PATH_LITERAL("textinput")),
+      base::FilePath(FILE_PATH_LITERAL("keyevent_logging.html")));
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  content::WebContents* tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  helper.ClickElement("text_id", tab);
+  helper.WaitForTextInputStateChanged(ui::TEXT_INPUT_TYPE_TEXT);
+
+  {
+    ASSERT_TRUE(content::ExecuteScript(
+        tab,
+        "initKeyDownExpectations(["
+        // Alt down has (only) altKey true in this case.
+        "{ keyCode:18, shiftKey:false, ctrlKey:false, altKey:true }]);"));
+    EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(),
+                                                ui::VKEY_MENU,
+                                                false,
+                                                false,
+                                                false,
+                                                false));
+    bool result = false;
+    ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+        tab,
+        "didTestSucceed();",
+        &result));
+    EXPECT_TRUE(result);
+  }
+  {
+    ASSERT_TRUE(content::ExecuteScript(
+        tab,
+        "initKeyDownExpectations(["
+        // Ctrl down has (only) ctrlKey true in this case.
+        "{ keyCode:17, shiftKey:false, ctrlKey:true, altKey:false }]);"));
+    EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(),
+                                                ui::VKEY_CONTROL,
+                                                false,
+                                                false,
+                                                false,
+                                                false));
+    bool result = false;
+    ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+        tab,
+        "didTestSucceed();",
+        &result));
+    EXPECT_TRUE(result);
+  }
+  {
+    ASSERT_TRUE(content::ExecuteScript(
+        tab,
+        "initKeyDownExpectations(["
+        // Ctrl down has ctrlKey false in this case.
+        "{ keyCode:17, shiftKey:false , ctrlKey:false , altKey:false },"
+        // Alt down has altKey false in this case.
+        "{ keyCode:18, shiftKey:false , ctrlKey:false , altKey:false }]);"));
+    EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(),
+                                                ui::VKEY_ALTGR,
+                                                false,
+                                                false,
+                                                false,
+                                                false));
+    bool result = false;
+    ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+        tab,
+        "didTestSucceed();",
+        &result));
+    EXPECT_TRUE(result);
+  }
+}
+
+// TODO(nona): Add AltGr modifier test. Need to add AltGr handling into
+//             SendKeyPressSync(crbug.com/262928).
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/captive_portal_view.cc b/chrome/browser/chromeos/login/captive_portal_view.cc
index 5f52672..c61b7a3 100644
--- a/chrome/browser/chromeos/login/captive_portal_view.cc
+++ b/chrome/browser/chromeos/login/captive_portal_view.cc
@@ -70,7 +70,7 @@
 
   // Naive way to determine the redirection. This won't be needed after portal
   // detection will be done on the Chrome side.
-  GURL url = source->GetURL();
+  GURL url = source->GetLastCommittedURL();
   // Note, |url| will be empty for "client3.google.com/generate_204" page.
   if (!redirected_  && url != GURL::EmptyGURL() &&
       url != GURL(CaptivePortalStartURL())) {
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index aa317f3..dbb9f5e 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -79,6 +79,7 @@
       ::switches::kDisableForceCompositingMode,
       ::switches::kDisableGpuShaderDiskCache,
       ::switches::kDisableGpuWatchdog,
+      ::switches::kDisableGpuCompositing,
       ::switches::kDisableLegacyEncryptedMedia,
       ::switches::kDisablePanelFitting,
       ::switches::kDisableSeccompFilterSandbox,
@@ -107,6 +108,7 @@
       ::switches::kEnableViewport,
       ::switches::kForceDeviceScaleFactor,
       ::switches::kGpuStartupDialog,
+      ::switches::kGpuSandboxAllowSysVShm,
       ::switches::kMultiProfiles,
       ::switches::kNoSandbox,
       ::switches::kPpapiFlashArgs,
@@ -141,7 +143,6 @@
       ash::switches::kAshDefaultWallpaperSmall,
 #if defined(OS_CHROMEOS)
       ash::switches::kAshDisableAudioDeviceMenu,
-      ash::switches::kAshDisableNewAudioHandler,
 #endif
       ash::switches::kAshHostWindowBounds,
       ash::switches::kAshTouchHud,
diff --git a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc
index 55b1d82..f1cd9a1 100644
--- a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc
+++ b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/chromeos/login/user.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
-#include "chrome/browser/managed_mode/managed_user_registration_service_factory.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
 #include "content/public/browser/browser_thread.h"
@@ -50,8 +49,7 @@
 LocallyManagedUserCreationController::UserCreationContext::UserCreationContext()
     : token_acquired(false),
       token_succesfully_written(false),
-      manager_profile(NULL),
-      service(NULL) {}
+      manager_profile(NULL) {}
 
 LocallyManagedUserCreationController::UserCreationContext::
     ~UserCreationContext() {}
@@ -152,14 +150,14 @@
 }
 
 void LocallyManagedUserCreationController::OnAddKeySuccess() {
-  creation_context_->service =
-      ManagedUserRegistrationServiceFactory::GetForProfile(
+  creation_context_->registration_utility =
+      ManagedUserRegistrationUtility::Create(
           creation_context_->manager_profile);
 
   VLOG(1) << "Creating user on server";
   ManagedUserRegistrationInfo info(creation_context_->display_name);
   info.master_key = creation_context_->master_key;
-  creation_context_->service->Register(
+  creation_context_->registration_utility->Register(
       info,
       base::Bind(&LocallyManagedUserCreationController::RegistrationCallback,
                  weak_factory_.GetWeakPtr()));
@@ -189,8 +187,7 @@
 }
 
 void LocallyManagedUserCreationController::CancelCreation() {
-  if (creation_context_->service)
-    creation_context_->service->CancelPendingRegistration();
+  creation_context_->registration_utility.reset();
   chrome::AttemptUserExit();
 }
 
diff --git a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.h b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.h
index 2185fe8..3736544 100644
--- a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.h
+++ b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.h
@@ -13,7 +13,7 @@
 #include "base/strings/string16.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/login/managed/managed_user_authenticator.h"
-#include "chrome/browser/managed_mode/managed_user_registration_service.h"
+#include "chrome/browser/managed_mode/managed_user_registration_utility.h"
 
 class Profile;
 
@@ -87,7 +87,7 @@
     std::string token;
     bool token_succesfully_written;
     Profile* manager_profile;
-    ManagedUserRegistrationService* service;
+    scoped_ptr<ManagedUserRegistrationUtility> registration_utility;
   };
 
   // ManagedUserAuthenticator::StatusConsumer overrides.
diff --git a/chrome/browser/chromeos/login/merge_session_load_page_unittest.cc b/chrome/browser/chromeos/login/merge_session_load_page_unittest.cc
index 3cab6ed..eb651e0 100644
--- a/chrome/browser/chromeos/login/merge_session_load_page_unittest.cc
+++ b/chrome/browser/chromeos/login/merge_session_load_page_unittest.cc
@@ -124,7 +124,7 @@
   base::RunLoop().RunUntilIdle();
 
   // The URL remains to be URL2.
-  EXPECT_EQ(kURL2, web_contents()->GetURL().spec());
+  EXPECT_EQ(kURL2, web_contents()->GetVisibleURL().spec());
 
   // Commit navigation and the interstitial page is gone.
   Navigate(kURL2, 2);
diff --git a/chrome/browser/chromeos/login/screens/core_oobe_actor.h b/chrome/browser/chromeos/login/screens/core_oobe_actor.h
new file mode 100644
index 0000000..e9d571f
--- /dev/null
+++ b/chrome/browser/chromeos/login/screens/core_oobe_actor.h
@@ -0,0 +1,42 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_CORE_OOBE_ACTOR_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_CORE_OOBE_ACTOR_H_
+
+#include <string>
+
+#include "chrome/browser/chromeos/login/help_app_launcher.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace chromeos {
+
+class CoreOobeActor {
+ public:
+  virtual ~CoreOobeActor() {}
+
+  virtual void ShowSignInError(int login_attempts,
+                               const std::string& error_text,
+                               const std::string& help_link_text,
+                               HelpAppLauncher::HelpTopic help_topic_id) = 0;
+  virtual void ShowTpmError() = 0;
+  virtual void ShowSignInUI(const std::string& email) = 0;
+  virtual void ResetSignInUI(bool force_online) = 0;
+  virtual void ClearUserPodPassword() = 0;
+  virtual void RefocusCurrentPod() = 0;
+  virtual void OnLoginSuccess(const std::string& username) = 0;
+  virtual void ShowPasswordChangedScreen(bool show_password_error) = 0;
+  virtual void SetUsageStats(bool checked) = 0;
+  virtual void SetOemEulaUrl(const std::string& oem_eula_url) = 0;
+  virtual void SetTpmPassword(const std::string& tmp_password) = 0;
+  virtual void ClearErrors() = 0;
+  virtual void ReloadContent(const base::DictionaryValue& dictionary) = 0;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_CORE_OOBE_ACTOR_H_
diff --git a/chrome/browser/chromeos/login/user_manager_impl.h b/chrome/browser/chromeos/login/user_manager_impl.h
index 189d4b2..cdfe74d 100644
--- a/chrome/browser/chromeos/login/user_manager_impl.h
+++ b/chrome/browser/chromeos/login/user_manager_impl.h
@@ -154,6 +154,7 @@
   friend class UserManager;
   friend class WallpaperManager;
   friend class UserManagerTest;
+  friend class WallpaperManagerTest;
 
   UserManagerImpl();
 
diff --git a/chrome/browser/chromeos/login/wallpaper_manager.cc b/chrome/browser/chromeos/login/wallpaper_manager.cc
index 854a529..03dd8c4 100644
--- a/chrome/browser/chromeos/login/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/wallpaper_manager.cc
@@ -111,6 +111,18 @@
 
 // WallpaperManager, public: ---------------------------------------------------
 
+// TestApi. For testing purpose
+WallpaperManager::TestApi::TestApi(WallpaperManager* wallpaper_manager)
+    : wallpaper_manager_(wallpaper_manager) {
+}
+
+WallpaperManager::TestApi::~TestApi() {
+}
+
+base::FilePath WallpaperManager::TestApi::current_wallpaper_path() {
+  return wallpaper_manager_->current_wallpaper_path_;
+}
+
 // static
 WallpaperManager* WallpaperManager::Get() {
   if (!g_wallpaper_manager)
@@ -122,6 +134,7 @@
     : no_observers_(true),
       loaded_wallpapers_(0),
       wallpaper_loader_(new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC)),
+      command_line_for_testing_(NULL),
       should_cache_wallpaper_(false),
       weak_factory_(this) {
   RestartTimer();
@@ -256,7 +269,15 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   UserManager* user_manager = UserManager::Get();
 
-  if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType))
+  CommandLine* command_line = GetComandLine();
+  if (command_line->HasSwitch(chromeos::switches::kGuestSession)) {
+    // Guest wallpaper should be initialized when guest login.
+    // Note: This maybe called before login. So IsLoggedInAsGuest can not be
+    // used here to determine if current user is guest.
+    return;
+  }
+
+  if (command_line->HasSwitch(::switches::kTestType))
     WizardController::SetZeroDelays();
 
   // Zero delays is also set in autotests.
@@ -267,14 +288,6 @@
     return;
   }
 
-  if (CommandLine::ForCurrentProcess()->
-          HasSwitch(chromeos::switches::kGuestSession)) {
-    // Guest wallpaper should be initialized when guest login.
-    // Note: This maybe called before login. So IsLoggedInAsGuest can not be
-    // used here to determine if current user is guest.
-    return;
-  }
-
   if (!user_manager->IsUserLoggedIn()) {
     if (!StartupUtils::IsDeviceRegistered())
       SetDefaultWallpaper();
@@ -295,8 +308,7 @@
       break;
     }
     case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
-      if (!CommandLine::ForCurrentProcess()->
-          HasSwitch(switches::kDisableBootAnimation)) {
+      if (!GetComandLine()->HasSwitch(switches::kDisableBootAnimation)) {
         BrowserThread::PostDelayedTask(
             BrowserThread::UI, FROM_HERE,
             base::Bind(&WallpaperManager::CacheUsersWallpapers,
@@ -618,6 +630,14 @@
 void WallpaperManager::UpdateWallpaper() {
   ClearWallpaperCache();
   current_wallpaper_path_.clear();
+  // For GAIA login flow, the last_selected_user_ may not be set before user
+  // login. If UpdateWallpaper is called at GAIA login screen, no wallpaper will
+  // be set. It could result a black screen on external monitors.
+  // See http://crbug.com/265689 for detail.
+  if (last_selected_user_.empty()) {
+    SetDefaultWallpaper();
+    return;
+  }
   SetUserWallpaper(last_selected_user_);
 }
 
@@ -752,6 +772,12 @@
     file_util::CreateDirectory(dir);
 }
 
+CommandLine* WallpaperManager::GetComandLine() {
+  CommandLine* command_line = command_line_for_testing_ ?
+      command_line_for_testing_ : CommandLine::ForCurrentProcess();
+  return command_line;
+}
+
 void WallpaperManager::FallbackToOldCustomWallpaper(const std::string& email,
                                                     const WallpaperInfo& info,
                                                     bool update_wallpaper){
@@ -770,7 +796,7 @@
   if (UserManager::Get()->IsUserLoggedIn())
     return;
 
-  bool disable_boot_animation = CommandLine::ForCurrentProcess()->
+  bool disable_boot_animation = GetComandLine()->
       HasSwitch(switches::kDisableBootAnimation);
   bool show_users = true;
   bool result = CrosSettings::Get()->GetBoolean(
diff --git a/chrome/browser/chromeos/login/wallpaper_manager.h b/chrome/browser/chromeos/login/wallpaper_manager.h
index c994aba..cd39319 100644
--- a/chrome/browser/chromeos/login/wallpaper_manager.h
+++ b/chrome/browser/chromeos/login/wallpaper_manager.h
@@ -24,6 +24,7 @@
 #include "third_party/icu/source/i18n/unicode/timezone.h"
 #include "ui/gfx/image/image_skia.h"
 
+class CommandLine;
 class PrefRegistrySimple;
 
 namespace base {
@@ -68,11 +69,29 @@
                         public chromeos::PowerManagerClient::Observer,
                         public content::NotificationObserver {
  public:
+  // For testing.
+  class TestApi {
+   public:
+    explicit TestApi(WallpaperManager* wallpaper_manager);
+    virtual ~TestApi();
+
+    base::FilePath current_wallpaper_path();
+
+   private:
+    WallpaperManager* wallpaper_manager_;  // not owned
+
+    DISALLOW_COPY_AND_ASSIGN(TestApi);
+  };
+
   static WallpaperManager* Get();
 
   WallpaperManager();
   virtual ~WallpaperManager();
 
+  void set_command_line_for_testing(CommandLine* command_line) {
+    command_line_for_testing_ = command_line;
+  }
+
   // Indicates imminent shutdown, allowing the WallpaperManager to remove any
   // observers it has registered.
   void Shutdown();
@@ -184,6 +203,7 @@
   void UpdateWallpaper();
 
  private:
+  friend class TestApi;
   friend class WallpaperManagerBrowserTest;
   typedef std::map<std::string, gfx::ImageSkia> CustomWallpaperMap;
 
@@ -225,6 +245,9 @@
   // Creates all new custom wallpaper directories for |email| if not exist.
   void EnsureCustomWallpaperDirectories(const std::string& email);
 
+  // Gets the CommandLine representing the current process's command line.
+  CommandLine* GetComandLine();
+
   // Loads custom wallpaper from old places and triggers move all custom
   // wallpapers to new places.
   // TODO(bshe): Remove this function when all custom wallpapers moved to the
@@ -341,6 +364,9 @@
   // Logged-in user wallpaper information.
   WallpaperInfo current_user_wallpaper_info_;
 
+  // If non-NULL, used in place of the real command line.
+  CommandLine* command_line_for_testing_;
+
   // Caches wallpapers of users. Accessed only on UI thread.
   CustomWallpaperMap wallpaper_cache_;
 
diff --git a/chrome/browser/chromeos/login/wallpaper_manager_browsertest.cc b/chrome/browser/chromeos/login/wallpaper_manager_browsertest.cc
index 210aaa2..fe126d8 100644
--- a/chrome/browser/chromeos/login/wallpaper_manager_browsertest.cc
+++ b/chrome/browser/chromeos/login/wallpaper_manager_browsertest.cc
@@ -346,6 +346,21 @@
   // can not handle pre migrated user profile (M21 profile or older).
 }
 
+// Test for http://crbug.com/265689. When hooked up a large external monitor,
+// the default large resolution wallpaper should load.
+IN_PROC_BROWSER_TEST_F(WallpaperManagerBrowserTest,
+                       HotPlugInScreenAtGAIALoginScreen) {
+  UpdateDisplay("800x600");
+  // Set initial wallpaper to the default wallpaper.
+  WallpaperManager::Get()->SetDefaultWallpaper();
+  WaitAsyncWallpaperLoad();
+
+  // Hook up a 2000x2000 display. The large resolution custom wallpaper should
+  // be loaded.
+  UpdateDisplay("800x600,2000x2000");
+  WaitAsyncWallpaperLoad();
+}
+
 class WallpaperManagerBrowserTestNoAnimation
     : public WallpaperManagerBrowserTest {
  public:
diff --git a/chrome/browser/chromeos/login/wallpaper_manager_unittest.cc b/chrome/browser/chromeos/login/wallpaper_manager_unittest.cc
new file mode 100644
index 0000000..5ed7c9f
--- /dev/null
+++ b/chrome/browser/chromeos/login/wallpaper_manager_unittest.cc
@@ -0,0 +1,139 @@
+// Copyright 2013 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.
+
+#include <cstdlib>
+#include <cstring>
+
+#include "ash/ash_resources/grit/ash_resources.h"
+#include "ash/desktop_background/desktop_background_controller.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_service.h"
+#include "base/prefs/testing_pref_service.h"
+#include "chrome/browser/chromeos/cros/network_library.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/chromeos/login/user_manager_impl.h"
+#include "chrome/browser/chromeos/login/wallpaper_manager.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/settings/cros_settings_names.h"
+#include "chrome/browser/chromeos/settings/cros_settings_provider.h"
+#include "chrome/browser/chromeos/settings/device_settings_service.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chromeos/chromeos_switches.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/resource/resource_bundle.h"
+
+using namespace ash;
+
+namespace {
+
+const char kTestUser1[] = "test-user@example.com";
+
+const int kLargeWallpaperResourceId = IDR_AURA_WALLPAPER_DEFAULT_LARGE;
+const int kSmallWallpaperResourceId = IDR_AURA_WALLPAPER_DEFAULT_SMALL;
+
+}  // namespace
+
+namespace chromeos {
+
+class WallpaperManagerTest : public test::AshTestBase {
+ public:
+  WallpaperManagerTest() : command_line_(CommandLine::NO_PROGRAM) {}
+
+  virtual ~WallpaperManagerTest() {}
+
+  virtual void SetUp() OVERRIDE {
+    test::AshTestBase::SetUp();
+
+    // Register an in-memory local settings instance.
+    local_state_.reset(new TestingPrefServiceSimple);
+    TestingBrowserProcess::GetGlobal()->SetLocalState(local_state_.get());
+    UserManager::RegisterPrefs(local_state_->registry());
+    // Wallpaper manager and user image managers prefs will be accessed by the
+    // unit-test as well.
+    UserImageManager::RegisterPrefs(local_state_->registry());
+    WallpaperManager::RegisterPrefs(local_state_->registry());
+
+    StartupUtils::RegisterPrefs(local_state_->registry());
+    StartupUtils::MarkDeviceRegistered();
+
+    ResetUserManager();
+  }
+
+  virtual void TearDown() OVERRIDE {
+    // Unregister the in-memory local settings instance.
+    TestingBrowserProcess::GetGlobal()->SetLocalState(0);
+
+    // Shut down the DeviceSettingsService.
+    DeviceSettingsService::Get()->UnsetSessionManager();
+
+    test::AshTestBase::TearDown();
+  }
+
+  void ResetUserManager() {
+    // Reset the UserManager singleton.
+    user_manager_enabler_.reset();
+    // Initialize the UserManager singleton to a fresh UserManagerImpl instance.
+    user_manager_enabler_.reset(
+        new ScopedUserManagerEnabler(new UserManagerImpl));
+  }
+
+  void AppendGuestSwitch() {
+    command_line_.AppendSwitch(switches::kGuestSession);
+    WallpaperManager::Get()->set_command_line_for_testing(&command_line_);
+  }
+
+ protected:
+  CommandLine command_line_;
+
+  scoped_ptr<TestingPrefServiceSimple> local_state_;
+
+  ScopedTestDeviceSettingsService test_device_settings_service_;
+  ScopedStubNetworkLibraryEnabler stub_network_library_enabler_;
+  ScopedTestCrosSettings test_cros_settings_;
+
+  scoped_ptr<ScopedUserManagerEnabler> user_manager_enabler_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WallpaperManagerTest);
+};
+
+// Test for crbug.com/260755. If this test fails, it is probably because the
+// wallpaper of last logged in user is set as guest wallpaper.
+TEST_F(WallpaperManagerTest, GuestUserUseGuestWallpaper) {
+  UserManager::Get()->UserLoggedIn(kTestUser1, kTestUser1, false);
+
+  base::FilePath old_wallpaper_path = WallpaperManager::Get()->
+      GetOriginalWallpaperPathForUser(kTestUser1);
+
+  // Saves wallpaper info to local state for user |kTestUser1|.
+  WallpaperInfo info = {
+      "DUMMY",
+      WALLPAPER_LAYOUT_CENTER_CROPPED,
+      User::CUSTOMIZED,
+      base::Time::Now().LocalMidnight()
+  };
+  WallpaperManager::Get()->SetUserWallpaperInfo(kTestUser1, info, true);
+  ResetUserManager();
+
+  AppendGuestSwitch();
+  scoped_ptr<WallpaperManager::TestApi> test_api;
+  test_api.reset(new WallpaperManager::TestApi(WallpaperManager::Get()));
+  // If last logged in user's wallpaper is used in function InitializeWallpaper,
+  // this test will crash. InitializeWallpaper should be a noop after
+  // AppendGuestSwitch being called.
+  WallpaperManager::Get()->InitializeWallpaper();
+  EXPECT_TRUE(test_api->current_wallpaper_path().empty());
+  UserManager::Get()->UserLoggedIn(UserManager::kGuestUserName,
+                                   UserManager::kGuestUserName, false);
+  EXPECT_FALSE(ash::Shell::GetInstance()->desktop_background_controller()->
+      SetDefaultWallpaper(true));
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/media/media_player.cc b/chrome/browser/chromeos/media/media_player.cc
index 5b0b683..ce6f47a 100644
--- a/chrome/browser/chromeos/media/media_player.cc
+++ b/chrome/browser/chromeos/media/media_player.cc
@@ -151,7 +151,7 @@
 }
 
 GURL MediaPlayer::GetMediaPlayerUrl() {
-  return file_manager_util::GetMediaPlayerUrl();
+  return file_manager::util::GetMediaPlayerUrl();
 }
 
 Browser* MediaPlayer::GetBrowser() {
@@ -159,7 +159,7 @@
     Browser* browser = *it;
     TabStripModel* tab_strip = browser->tab_strip_model();
     for (int idx = 0; idx < tab_strip->count(); idx++) {
-      const GURL& url = tab_strip->GetWebContentsAt(idx)->GetURL();
+      const GURL& url = tab_strip->GetWebContentsAt(idx)->GetVisibleURL();
       GURL base_url(url.GetOrigin().spec() + url.path().substr(1));
       if (base_url == GetMediaPlayerUrl())
         return browser;
diff --git a/chrome/browser/chromeos/memory/oom_priority_manager.cc b/chrome/browser/chromeos/memory/oom_priority_manager.cc
index 69477ae..352360a 100644
--- a/chrome/browser/chromeos/memory/oom_priority_manager.cc
+++ b/chrome/browser/chromeos/memory/oom_priority_manager.cc
@@ -567,7 +567,8 @@
       if (!contents->IsCrashed()) {
         TabStats stats;
         stats.is_app = is_browser_for_app;
-        stats.is_reloadable_ui = IsReloadableUI(contents->GetURL());
+        stats.is_reloadable_ui =
+            IsReloadableUI(contents->GetLastCommittedURL());
         stats.is_playing_audio = chrome::IsPlayingAudio(contents);
         stats.is_pinned = model->IsTabPinned(i);
         stats.is_selected = browser_active && model->IsTabSelected(i);
diff --git a/chrome/browser/chromeos/offline/offline_load_page.cc b/chrome/browser/chromeos/offline/offline_load_page.cc
index 2a12636..cc7f5f7 100644
--- a/chrome/browser/chromeos/offline/offline_load_page.cc
+++ b/chrome/browser/chromeos/offline/offline_load_page.cc
@@ -14,7 +14,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/cros/network_library.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/profiles/profile.h"
@@ -95,9 +94,6 @@
   strings.SetString("icon",
                     webui::GetBitmapDataUrlFromResource(IDR_PRODUCT_LOGO_32));
 
-  // Activation
-  strings.SetBoolean("show_activation", ShowActivationMessage());
-
   webui::SetFontAndTextDirection(&strings);
   string16 failed_url(ASCIIToUTF16(url_.spec()));
   if (base::i18n::IsRTL())
@@ -192,20 +188,4 @@
   }
 }
 
-bool OfflineLoadPage::ShowActivationMessage() {
-  NetworkLibrary* network_library = NetworkLibrary::Get();
-  if (!network_library || !network_library->cellular_available())
-    return false;
-
-  const CellularNetworkVector& cell_networks =
-      network_library->cellular_networks();
-  for (size_t i = 0; i < cell_networks.size(); ++i) {
-    chromeos::ActivationState activation_state =
-        cell_networks[i]->activation_state();
-    if (activation_state == ACTIVATION_STATE_ACTIVATED)
-      return false;
-  }
-  return true;
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/offline/offline_load_page.h b/chrome/browser/chromeos/offline/offline_load_page.h
index df8835d..8fdab58 100644
--- a/chrome/browser/chromeos/offline/offline_load_page.h
+++ b/chrome/browser/chromeos/offline/offline_load_page.h
@@ -74,10 +74,6 @@
                             base::DictionaryValue* strings) const;
   void GetNormalOfflineStrings(base::DictionaryValue* strings) const;
 
-  // True if there is a mobile network is available but
-  // has not been activated.
-  bool ShowActivationMessage();
-
   CompletionCallback callback_;
 
   // True if the proceed is chosen.
diff --git a/chrome/browser/chromeos/offline/offline_load_page_unittest.cc b/chrome/browser/chromeos/offline/offline_load_page_unittest.cc
index b0b4bec..54ac2af 100644
--- a/chrome/browser/chromeos/offline/offline_load_page_unittest.cc
+++ b/chrome/browser/chromeos/offline/offline_load_page_unittest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/cros/network_library.h"
 #include "chrome/browser/chromeos/offline/offline_load_page.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "content/public/browser/interstitial_page.h"
@@ -84,9 +83,6 @@
   friend class TestOfflineLoadPage;
 
   UserResponse user_response_;
-
-  // Initializes / shuts down a stub NetworkLibrary.
-  ScopedStubNetworkLibraryEnabler stub_network_library_enabler_;
 };
 
 void TestOfflineLoadPage::NotifyBlockingPageComplete(bool proceed) {
@@ -114,7 +110,7 @@
   EXPECT_EQ(OK, user_response());
 
   // The URL remains to be URL2.
-  EXPECT_EQ(kURL2, web_contents()->GetURL().spec());
+  EXPECT_EQ(kURL2, web_contents()->GetVisibleURL().spec());
 
   // Commit navigation and the interstitial page is gone.
   Navigate(kURL2, 2);
@@ -143,7 +139,7 @@
   // We did not proceed, the pending entry should be gone.
   EXPECT_FALSE(controller().GetPendingEntry());
   // the URL is set back to kURL1.
-  EXPECT_EQ(kURL1, web_contents()->GetURL().spec());
+  EXPECT_EQ(kURL1, web_contents()->GetLastCommittedURL().spec());
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/policy/app_pack_updater.cc b/chrome/browser/chromeos/policy/app_pack_updater.cc
index ad89025..3aca24a 100644
--- a/chrome/browser/chromeos/policy/app_pack_updater.cc
+++ b/chrome/browser/chromeos/policy/app_pack_updater.cc
@@ -5,25 +5,13 @@
 #include "chrome/browser/chromeos/policy/app_pack_updater.h"
 
 #include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/file_util.h"
-#include "base/files/file_enumerator.h"
-#include "base/location.h"
-#include "base/stl_util.h"
-#include "base/strings/string_util.h"
 #include "base/values.h"
-#include "base/version.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/policy/enterprise_install_attributes.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/cros_settings_names.h"
-#include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/external_loader.h"
 #include "chrome/browser/extensions/external_provider_impl.h"
-#include "chrome/browser/extensions/updater/extension_downloader.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/extension_constants.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
@@ -38,9 +26,6 @@
 // Directory where the AppPack extensions are cached.
 const char kAppPackCacheDir[] = "/var/cache/app_pack";
 
-// File name extension for CRX files (not case sensitive).
-const char kCRXFileExtension[] = ".crx";
-
 }  // namespace
 
 // A custom extensions::ExternalLoader that the AppPackUpdater creates and uses
@@ -79,16 +64,15 @@
 AppPackUpdater::AppPackUpdater(net::URLRequestContextGetter* request_context,
                                EnterpriseInstallAttributes* install_attributes)
     : weak_ptr_factory_(this),
-      initialized_(false),
       created_extension_loader_(false),
-      request_context_(request_context),
-      install_attributes_(install_attributes) {
+      install_attributes_(install_attributes),
+      external_cache_(kAppPackCacheDir, request_context, this) {
   chromeos::CrosSettings::Get()->AddSettingsObserver(chromeos::kAppPack, this);
 
   if (install_attributes_->GetMode() == DEVICE_MODE_RETAIL_KIOSK) {
     // Already in Kiosk mode, start loading.
     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                            base::Bind(&AppPackUpdater::Init,
+                            base::Bind(&AppPackUpdater::LoadPolicy,
                                        weak_ptr_factory_.GetWeakPtr()));
   } else {
     // Linger until the device switches to DEVICE_MODE_RETAIL_KIOSK and the
@@ -127,19 +111,6 @@
   }
 }
 
-void AppPackUpdater::Init() {
-  if (initialized_)
-    return;
-
-  initialized_ = true;
-  worker_pool_token_ = BrowserThread::GetBlockingPool()->GetSequenceToken();
-  notification_registrar_.Add(
-      this,
-      chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
-      content::NotificationService::AllBrowserContextsAndSources());
-  LoadPolicy();
-}
-
 void AppPackUpdater::Observe(int type,
                              const content::NotificationSource& source,
                              const content::NotificationDetails& details) {
@@ -147,21 +118,10 @@
     case chrome::NOTIFICATION_SYSTEM_SETTING_CHANGED:
       DCHECK_EQ(chromeos::kAppPack,
                 *content::Details<const std::string>(details).ptr());
-      if (install_attributes_->GetMode() == DEVICE_MODE_RETAIL_KIOSK) {
-        if (!initialized_)
-          Init();
-        else
-          LoadPolicy();
-      }
+      if (install_attributes_->GetMode() == DEVICE_MODE_RETAIL_KIOSK)
+        LoadPolicy();
       break;
 
-    case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
-      extensions::CrxInstaller* installer =
-          content::Source<extensions::CrxInstaller>(source).ptr();
-      OnDamagedFileDetected(installer->source_file());
-      break;
-    }
-
     default:
       NOTREACHED();
   }
@@ -175,7 +135,7 @@
     return;
   }
 
-  app_pack_extensions_.clear();
+  scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue());
   const base::Value* value = settings->GetPref(chromeos::kAppPack);
   const base::ListValue* list = NULL;
   if (value && value->GetAsList(&list)) {
@@ -190,7 +150,10 @@
       std::string update_url;
       if (dict->GetString(chromeos::kAppPackKeyExtensionId, &id) &&
           dict->GetString(chromeos::kAppPackKeyUpdateUrl, &update_url)) {
-        app_pack_extensions_[id] = update_url;
+        base::DictionaryValue* entry = new base::DictionaryValue();
+        entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
+                         update_url);
+        prefs->Set(id, entry);
       } else {
         LOG(WARNING) << "Failed to read required fields for an AppPack entry, "
                      << "ignoring.";
@@ -198,7 +161,7 @@
     }
   }
 
-  VLOG(1) << "Refreshed AppPack policy, got " << app_pack_extensions_.size()
+  VLOG(1) << "Refreshed AppPack policy, got " << prefs->size()
           << " entries.";
 
   value = settings->GetPref(chromeos::kScreenSaverExtensionId);
@@ -207,143 +170,23 @@
     SetScreenSaverPath(base::FilePath());
   }
 
-  CheckCacheNow();
+  external_cache_.UpdateExtensionsList(prefs.Pass());
 }
 
-void AppPackUpdater::CheckCacheNow() {
-  std::set<std::string>* valid_ids = new std::set<std::string>();
-  for (PolicyEntryMap::iterator it = app_pack_extensions_.begin();
-       it != app_pack_extensions_.end(); ++it) {
-    valid_ids->insert(it->first);
+void AppPackUpdater::OnExtensionListsUpdated(
+    const base::DictionaryValue* prefs) {
+  std::string crx_path;
+  const base::DictionaryValue* screen_saver = NULL;
+  if (prefs->GetDictionary(screen_saver_id_, &screen_saver)) {
+    screen_saver->GetString(extensions::ExternalProviderImpl::kExternalCrx,
+                            &crx_path);
   }
-  PostBlockingTask(FROM_HERE,
-                   base::Bind(&AppPackUpdater::BlockingCheckCache,
-                              weak_ptr_factory_.GetWeakPtr(),
-                              base::Owned(valid_ids)));
-}
-
-// static
-void AppPackUpdater::BlockingCheckCache(
-    base::WeakPtr<AppPackUpdater> app_pack_updater,
-    const std::set<std::string>* valid_ids) {
-  CacheEntryMap* entries = new CacheEntryMap();
-  BlockingCheckCacheInternal(valid_ids, entries);
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::Bind(&AppPackUpdater::OnCacheUpdated,
-                                     app_pack_updater,
-                                     base::Owned(entries)));
-}
-
-// static
-void AppPackUpdater::BlockingCheckCacheInternal(
-    const std::set<std::string>* valid_ids,
-    CacheEntryMap* entries) {
-  // Start by verifying that the cache dir exists.
-  base::FilePath dir(kAppPackCacheDir);
-  if (!base::DirectoryExists(dir)) {
-    // Create it now.
-    if (!file_util::CreateDirectory(dir))
-      LOG(ERROR) << "Failed to create AppPack directory at " << dir.value();
-    // Nothing else to do.
-    return;
-  }
-
-  // Enumerate all the files in the cache |dir|, including directories
-  // and symlinks. Each unrecognized file will be erased.
-  int types = base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES |
-      base::FileEnumerator::SHOW_SYM_LINKS;
-  base::FileEnumerator enumerator(dir, false /* recursive */, types);
-
-  for (base::FilePath path = enumerator.Next();
-       !path.empty(); path = enumerator.Next()) {
-    base::FileEnumerator::FileInfo info = enumerator.GetInfo();
-    std::string basename = path.BaseName().value();
-
-    if (info.IsDirectory() || file_util::IsLink(info.GetName())) {
-      LOG(ERROR) << "Erasing bad file in AppPack directory: " << basename;
-      base::DeleteFile(path, true /* recursive */);
-      continue;
-    }
-
-    // crx files in the cache are named <extension-id>-<version>.crx.
-    std::string id;
-    std::string version;
-
-    if (EndsWith(basename, kCRXFileExtension, false /* case-sensitive */)) {
-      size_t n = basename.find('-');
-      if (n != std::string::npos && n + 1 < basename.size() - 4) {
-        id = basename.substr(0, n);
-        // Size of |version| = total size - "<id>" - "-" - ".crx"
-        version = basename.substr(n + 1, basename.size() - 5 - id.size());
-      }
-    }
-
-    if (!extensions::Extension::IdIsValid(id)) {
-      LOG(ERROR) << "Bad AppPack extension id in cache: " << id;
-      id.clear();
-    } else if (!ContainsKey(*valid_ids, id)) {
-      LOG(WARNING) << basename << " is in the cache but is not configured by "
-                   << "the AppPack policy, and will be erased.";
-      id.clear();
-    }
-
-    if (!Version(version).IsValid()) {
-      LOG(ERROR) << "Bad AppPack extension version in cache: " << version;
-      version.clear();
-    }
-
-    if (id.empty() || version.empty()) {
-      LOG(ERROR) << "Invalid file in AppPack cache, erasing: " << basename;
-      base::DeleteFile(path, true /* recursive */);
-      continue;
-    }
-
-    // Enforce a lower-case id.
-    id = StringToLowerASCII(id);
-
-    // File seems good so far. Make sure there isn't another entry with the
-    // same id but a different version.
-
-    if (ContainsKey(*entries, id)) {
-      LOG(ERROR) << "Found two AppPack files for the same extension, will "
-                    "erase the oldest version";
-      CacheEntry& entry = (*entries)[id];
-      Version vEntry(entry.cached_version);
-      Version vCurrent(version);
-      DCHECK(vEntry.IsValid());
-      DCHECK(vCurrent.IsValid());
-      if (vEntry.CompareTo(vCurrent) < 0) {
-        base::DeleteFile(base::FilePath(entry.path), true /* recursive */);
-        entry.path = path.value();
-      } else {
-        base::DeleteFile(path, true /* recursive */);
-      }
-      continue;
-    }
-
-    // This is the only file for this |id| so far, add it.
-
-    CacheEntry& entry = (*entries)[id];
-    entry.path = path.value();
-    entry.cached_version = version;
-  }
-}
-
-void AppPackUpdater::OnCacheUpdated(CacheEntryMap* cache_entries) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  cached_extensions_.swap(*cache_entries);
-
-  CacheEntryMap::iterator it = cached_extensions_.find(screen_saver_id_);
-  if (it != cached_extensions_.end())
-    SetScreenSaverPath(base::FilePath(it->second.path));
+  if (!crx_path.empty())
+    SetScreenSaverPath(base::FilePath(crx_path));
   else
     SetScreenSaverPath(base::FilePath());
 
-  VLOG(1) << "Updated AppPack cache, there are " << cached_extensions_.size()
-          << " extensions cached and "
-          << (screen_saver_path_.empty() ? "no" : "the") << " screensaver";
   UpdateExtensionLoader();
-  DownloadMissingExtensions();
 }
 
 void AppPackUpdater::UpdateExtensionLoader() {
@@ -353,206 +196,17 @@
     return;
   }
 
-  // Build a DictionaryValue with the format that
-  // extensions::ExternalProviderImpl expects, containing info about the locally
-  // cached extensions.
+  scoped_ptr<base::DictionaryValue> prefs(
+      external_cache_.cached_extensions()->DeepCopy());
 
-  scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue());
-  for (CacheEntryMap::iterator it = cached_extensions_.begin();
-       it != cached_extensions_.end(); ++it) {
-    const std::string& id = it->first;
-    // The screensaver isn't installed into the Profile.
-    if (id == screen_saver_id_)
-      continue;
-
-    base::DictionaryValue* dict = new base::DictionaryValue();
-    dict->SetString(extensions::ExternalProviderImpl::kExternalCrx,
-                    it->second.path);
-    dict->SetString(extensions::ExternalProviderImpl::kExternalVersion,
-                    it->second.cached_version);
-
-    // Include this optional flag if the extension's update url is the Webstore.
-    PolicyEntryMap::iterator policy_entry = app_pack_extensions_.find(id);
-    if (policy_entry != app_pack_extensions_.end() &&
-        extension_urls::IsWebstoreUpdateUrl(
-            GURL(policy_entry->second))) {
-      dict->SetBoolean(extensions::ExternalProviderImpl::kIsFromWebstore, true);
-    }
-
-    prefs->Set(it->first, dict);
-
-    VLOG(1) << "Updating AppPack extension loader, added " << it->second.path;
-  }
+  // The screensaver isn't installed into the Profile.
+  prefs->Remove(screen_saver_id_, NULL);
 
   extension_loader_->SetCurrentAppPackExtensions(prefs.Pass());
 }
 
-void AppPackUpdater::DownloadMissingExtensions() {
-  // Check for updates for all extensions configured by the policy. Some of
-  // them may already be in the cache; only those with updated version will be
-  // downloaded, in that case.
-  if (!downloader_.get()) {
-    downloader_.reset(new extensions::ExtensionDownloader(this,
-                                                          request_context_));
-  }
-  for (PolicyEntryMap::iterator it = app_pack_extensions_.begin();
-       it != app_pack_extensions_.end(); ++it) {
-    downloader_->AddPendingExtension(it->first, GURL(it->second), 0);
-  }
-  VLOG(1) << "Downloading AppPack update manifest now";
-  downloader_->StartAllPending();
-}
-
-void AppPackUpdater::OnExtensionDownloadFailed(
-    const std::string& id,
-    extensions::ExtensionDownloaderDelegate::Error error,
-    const extensions::ExtensionDownloaderDelegate::PingResult& ping_result,
-    const std::set<int>& request_ids) {
-  if (error == NO_UPDATE_AVAILABLE) {
-    if (!ContainsKey(cached_extensions_, id))
-      LOG(ERROR) << "AppPack extension " << id << " not found on update server";
-  } else {
-    LOG(ERROR) << "AppPack failed to download extension " << id
-               << ", error " << error;
-  }
-}
-
-void AppPackUpdater::OnExtensionDownloadFinished(
-    const std::string& id,
-    const base::FilePath& path,
-    const GURL& download_url,
-    const std::string& version,
-    const extensions::ExtensionDownloaderDelegate::PingResult& ping_result,
-    const std::set<int>& request_ids) {
-  // The explicit copy ctors are to make sure that Bind() binds a copy and not
-  // a reference to the arguments.
-  PostBlockingTask(FROM_HERE,
-                   base::Bind(&AppPackUpdater::BlockingInstallCacheEntry,
-                              weak_ptr_factory_.GetWeakPtr(),
-                              std::string(id),
-                              base::FilePath(path),
-                              std::string(version)));
-}
-
-void AppPackUpdater::OnBlacklistDownloadFinished(
-    const std::string& data,
-    const std::string& package_hash,
-    const std::string& version,
-    const extensions::ExtensionDownloaderDelegate::PingResult& ping_result,
-    const std::set<int>& request_ids) {
-  NOTREACHED();
-}
-
-bool AppPackUpdater::IsExtensionPending(const std::string& id) {
-  // Pending means that there is no installed version yet.
-  return ContainsKey(app_pack_extensions_, id) &&
-         !ContainsKey(cached_extensions_, id);
-}
-
-bool AppPackUpdater::GetExtensionExistingVersion(const std::string& id,
-                                                 std::string* version) {
-  if (!ContainsKey(app_pack_extensions_, id) ||
-      !ContainsKey(cached_extensions_, id)) {
-    return false;
-  }
-
-  *version = cached_extensions_[id].cached_version;
-  return true;
-}
-
-// static
-void AppPackUpdater::BlockingInstallCacheEntry(
-    base::WeakPtr<AppPackUpdater> app_pack_updater,
-    const std::string& id,
-    const base::FilePath& path,
-    const std::string& version) {
-  Version version_validator(version);
-  if (!version_validator.IsValid()) {
-    LOG(ERROR) << "AppPack downloaded extension " << id << " but got bad "
-               << "version: " << version;
-    base::DeleteFile(path, true /* recursive */);
-    return;
-  }
-
-  std::string basename = id + "-" + version + kCRXFileExtension;
-  base::FilePath cache_dir(kAppPackCacheDir);
-  base::FilePath cached_crx_path = cache_dir.Append(basename);
-
-  if (base::PathExists(cached_crx_path)) {
-    LOG(WARNING) << "AppPack downloaded a crx whose filename will overwrite "
-                 << "an existing cached crx.";
-    base::DeleteFile(cached_crx_path, true /* recursive */);
-  }
-
-  if (!base::DirectoryExists(cache_dir)) {
-    LOG(ERROR) << "AppPack cache directory does not exist, creating now: "
-               << cache_dir.value();
-    if (!file_util::CreateDirectory(cache_dir)) {
-      LOG(ERROR) << "Failed to create the AppPack cache dir!";
-      base::DeleteFile(path, true /* recursive */);
-      return;
-    }
-  }
-
-  if (!base::Move(path, cached_crx_path)) {
-    LOG(ERROR) << "Failed to move AppPack crx from " << path.value()
-               << " to " << cached_crx_path.value();
-    base::DeleteFile(path, true /* recursive */);
-    return;
-  }
-
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::Bind(&AppPackUpdater::OnCacheEntryInstalled,
-                                     app_pack_updater,
-                                     std::string(id),
-                                     cached_crx_path.value(),
-                                     std::string(version)));
-}
-
-void AppPackUpdater::OnCacheEntryInstalled(const std::string& id,
-                                           const std::string& path,
-                                           const std::string& version) {
-  VLOG(1) << "AppPack installed a new extension in the cache: " << path;
-  // Add to the list of cached extensions.
-  CacheEntry& entry = cached_extensions_[id];
-  entry.path = path;
-  entry.cached_version = version;
-
-  if (id == screen_saver_id_) {
-    VLOG(1) << "AppPack got the screen saver extension at " << path;
-    SetScreenSaverPath(base::FilePath(path));
-  } else {
-    UpdateExtensionLoader();
-  }
-}
-
 void AppPackUpdater::OnDamagedFileDetected(const base::FilePath& path) {
-  // Search for |path| in |cached_extensions_|, and delete it if found.
-  for (CacheEntryMap::iterator it = cached_extensions_.begin();
-       it != cached_extensions_.end(); ++it) {
-    if (it->second.path == path.value()) {
-      LOG(ERROR) << "AppPack extension at " << path.value() << " failed to "
-                 << "install, deleting it.";
-      cached_extensions_.erase(it);
-      UpdateExtensionLoader();
-
-      // The file will be downloaded again on the next restart.
-      BrowserThread::PostTask(
-          BrowserThread::FILE, FROM_HERE,
-          base::Bind(base::IgnoreResult(base::DeleteFile), path, true));
-
-      // Don't try to DownloadMissingExtensions() from here,
-      // since it can cause a fail/retry loop.
-      break;
-    }
-  }
-}
-
-void AppPackUpdater::PostBlockingTask(const tracked_objects::Location& location,
-                                      const base::Closure& task) {
-  BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
-      worker_pool_token_, location, task,
-      base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+  external_cache_.OnDamagedFileDetected(path);
 }
 
 void AppPackUpdater::SetScreenSaverPath(const base::FilePath& path) {
diff --git a/chrome/browser/chromeos/policy/app_pack_updater.h b/chrome/browser/chromeos/policy/app_pack_updater.h
index 9f256b8..cd22b9c 100644
--- a/chrome/browser/chromeos/policy/app_pack_updater.h
+++ b/chrome/browser/chromeos/policy/app_pack_updater.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_CHROMEOS_POLICY_APP_PACK_UPDATER_H_
 #define CHROME_BROWSER_CHROMEOS_POLICY_APP_PACK_UPDATER_H_
 
-#include <map>
-#include <set>
 #include <string>
 
 #include "base/basictypes.h"
@@ -14,15 +12,10 @@
 #include "base/compiler_specific.h"
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "chrome/browser/extensions/updater/extension_downloader_delegate.h"
+#include "chrome/browser/chromeos/extensions/external_cache.h"
 #include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-
-class GURL;
 
 namespace extensions {
-class ExtensionDownloader;
 class ExternalLoader;
 }
 
@@ -30,10 +23,6 @@
 class URLRequestContextGetter;
 }
 
-namespace tracked_objects {
-class Location;
-}
-
 namespace policy {
 
 class AppPackExternalLoader;
@@ -43,7 +32,7 @@
 // device policy to be locally cached and installed into the Demo user account
 // at login time.
 class AppPackUpdater : public content::NotificationObserver,
-                       public extensions::ExtensionDownloaderDelegate {
+                       public chromeos::ExternalCache::Delegate {
  public:
   // Callback to listen for updates to the screensaver extension's path.
   typedef base::Callback<void(const base::FilePath&)> ScreenSaverUpdateCallback;
@@ -74,123 +63,28 @@
   void OnDamagedFileDetected(const base::FilePath& path);
 
  private:
-  struct CacheEntry {
-    std::string path;
-    std::string cached_version;
-  };
-
-  // Maps an extension ID to its update URL.
-  typedef std::map<std::string, std::string> PolicyEntryMap;
-
-  // Maps an extension ID to a CacheEntry.
-  typedef std::map<std::string, CacheEntry> CacheEntryMap;
-
-  void Init();
-
   // content::NotificationObserver:
   virtual void Observe(int type,
                        const content::NotificationSource& source,
                        const content::NotificationDetails& details) OVERRIDE;
 
+  // Implementation of ExternalCache::Delegate:
+  virtual void OnExtensionListsUpdated(
+      const base::DictionaryValue* prefs) OVERRIDE;
+
   // Loads the current policy and schedules a cache update.
   void LoadPolicy();
 
-  // Starts a cache update check immediately.
-  void CheckCacheNow();
-
-  // Performs a cache update check on the blocking pool. |app_pack_updater| is
-  // used to reply in the UI thread. |valid_ids| contains the list of IDs that
-  // are currently configured by policy; anything else is invalid, and should
-  // be removed from the cache. |valid_ids| is owned by the posted task.
-  static void BlockingCheckCache(base::WeakPtr<AppPackUpdater> app_pack_updater,
-                                 const std::set<std::string>* valid_ids);
-
-  // Helper for BlockingCheckCache().
-  static void BlockingCheckCacheInternal(
-      const std::set<std::string>* valid_ids,
-      CacheEntryMap* entries);
-
-  // Invoked when the cache has been updated. |cache_entries| contains all the
-  // currently valid crx files in the cache, and is owned by the posted task.
-  void OnCacheUpdated(CacheEntryMap* cache_entries);
-
   // Notifies the |extension_loader_| that the cache has been updated, providing
   // it with an updated list of app-pack extensions.
   void UpdateExtensionLoader();
 
-  // Schedules downloads of all the extensions that are currently configured
-  // by the policy but missing in the cache.
-  void DownloadMissingExtensions();
-
-  // Implementation of ExtensionDownloaderDelegate:
-
-  virtual void OnExtensionDownloadFailed(
-      const std::string& id,
-      Error error,
-      const PingResult& ping_result,
-      const std::set<int>& request_ids) OVERRIDE;
-
-  virtual void OnExtensionDownloadFinished(
-      const std::string& id,
-      const base::FilePath& path,
-      const GURL& download_url,
-      const std::string& version,
-      const PingResult& ping_result,
-      const std::set<int>& request_ids) OVERRIDE;
-
-  virtual void OnBlacklistDownloadFinished(
-      const std::string& data,
-      const std::string& package_hash,
-      const std::string& version,
-      const PingResult& ping_result,
-      const std::set<int>& request_ids) OVERRIDE;
-
-  virtual bool IsExtensionPending(const std::string& id) OVERRIDE;
-
-  virtual bool GetExtensionExistingVersion(const std::string& id,
-                                           std::string* version) OVERRIDE;
-
-  // Invoked to install the downloaded crx file at |path| in the AppPack cache.
-  static void BlockingInstallCacheEntry(
-      base::WeakPtr<AppPackUpdater> app_pack_updater,
-      const std::string& id,
-      const base::FilePath& path,
-      const std::string& version);
-
-  // Invoked on the UI thread when a new AppPack entry has been installed in
-  // the AppPack cache.
-  void OnCacheEntryInstalled(const std::string& id,
-                             const std::string& path,
-                             const std::string& version);
-
-  // Helper to post blocking IO tasks to the blocking pool.
-  void PostBlockingTask(const tracked_objects::Location& from_here,
-                        const base::Closure& task);
-
   // Sets |screen_saver_path_| and invokes |screen_saver_update_callback_| if
   // appropriate.
   void SetScreenSaverPath(const base::FilePath& path);
 
   base::WeakPtrFactory<AppPackUpdater> weak_ptr_factory_;
 
-  // Observes failures to install CRX files.
-  content::NotificationRegistrar notification_registrar_;
-
-  // Unique sequence token so that tasks posted by the AppPackUpdater are
-  // executed sequentially in the blocking pool.
-  base::SequencedWorkerPool::SequenceToken worker_pool_token_;
-
-  // Whether the updater has initialized. This is only done if the device is in
-  // kiosk mode and the app pack policy is present.
-  bool initialized_;
-
-  // This is the list of extensions currently configured by the policy.
-  PolicyEntryMap app_pack_extensions_;
-
-  // This contains extensions that are both currently configured by the policy
-  // and that have a valid crx in the cache.
-  CacheEntryMap cached_extensions_;
-
   // The extension ID and path of the CRX file of the screen saver extension,
   // if it is configured by the policy. Otherwise these fields are empty.
   std::string screen_saver_id_;
@@ -205,16 +99,12 @@
   bool created_extension_loader_;
   base::WeakPtr<AppPackExternalLoader> extension_loader_;
 
-  // Used to download the extensions configured via policy, and to check for
-  // updates.
-  scoped_ptr<extensions::ExtensionDownloader> downloader_;
-
-  // Request context used by the |downloader_|.
-  net::URLRequestContextGetter* request_context_;
-
   // For checking the device mode.
   EnterpriseInstallAttributes* install_attributes_;
 
+  // Extension cache.
+  chromeos::ExternalCache external_cache_;
+
   DISALLOW_COPY_AND_ASSIGN(AppPackUpdater);
 };
 
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
index e12200f..ff380c1 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
@@ -241,7 +241,7 @@
     EnrollmentStatus status) {
   if (status.status() == EnrollmentStatus::STATUS_SUCCESS) {
     core()->Connect(enrollment_handler_->ReleaseClient());
-    core()->StartRefreshScheduler();
+    StartRefreshScheduler();
     core()->TrackRefreshDelayPref(local_state_,
                                   prefs::kDevicePolicyRefreshRate);
     attestation_policy_observer_.reset(
@@ -262,7 +262,7 @@
       store()->is_managed() &&
       !service()) {
     core()->Connect(CreateClient());
-    core()->StartRefreshScheduler();
+    StartRefreshScheduler();
     core()->TrackRefreshDelayPref(local_state_,
                                   prefs::kDevicePolicyRefreshRate);
     attestation_policy_observer_.reset(
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index c5c9a48..d62be55 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -343,8 +343,10 @@
   ASSERT_TRUE(tabs);
   int expected_tab_count = static_cast<int>(arraysize(kStartupURLs));
   EXPECT_EQ(expected_tab_count, tabs->count());
-  for (int i = 0; i < expected_tab_count && i < tabs->count(); ++i)
-    EXPECT_EQ(GURL(kStartupURLs[i]), tabs->GetWebContentsAt(i)->GetURL());
+  for (int i = 0; i < expected_tab_count && i < tabs->count(); ++i) {
+    EXPECT_EQ(GURL(kStartupURLs[i]),
+              tabs->GetWebContentsAt(i)->GetVisibleURL());
+  }
 }
 
 IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, TermsOfService) {
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
index 7d25a8a..237d66a 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
@@ -399,7 +399,7 @@
       // have to pass the channel in here, only ping the update engine to tell
       // it to fetch the channel from the policy.
       chromeos::DBusThreadManager::Get()->GetUpdateEngineClient()->
-          SetReleaseTrack(channel);
+          SetChannel(channel, false);
     }
     if (container.has_release_channel_delegated()) {
       policies->Set(key::kChromeOsReleaseChannelDelegated,
diff --git a/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc b/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc
index e16c255..ee6ae4f 100644
--- a/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc
+++ b/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc
@@ -41,25 +41,11 @@
       failed_(false),
       callback_(callback) {}
 
-PolicyOAuth2TokenFetcher::PolicyOAuth2TokenFetcher(
-    net::URLRequestContextGetter* system_context_getter,
-    const std::string& oauth2_refresh_token,
-    const TokenCallback& callback)
-    : system_context_getter_(system_context_getter),
-      oauth2_refresh_token_(oauth2_refresh_token),
-      retry_count_(0),
-      failed_(false),
-      callback_(callback) {}
-
 PolicyOAuth2TokenFetcher::~PolicyOAuth2TokenFetcher() {}
 
 void PolicyOAuth2TokenFetcher::Start() {
   retry_count_ = 0;
-  if (oauth2_refresh_token_.empty()) {
-    StartFetchingRefreshToken();
-  } else {
-    StartFetchingAccessToken();
-  }
+  StartFetchingRefreshToken();
 }
 
 void PolicyOAuth2TokenFetcher::StartFetchingRefreshToken() {
diff --git a/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.h b/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.h
index 65a8d3a..aa46e87 100644
--- a/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.h
+++ b/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.h
@@ -38,12 +38,6 @@
   typedef base::Callback<void(const std::string&,
                               const GoogleServiceAuthError&)> TokenCallback;
 
-  // Fetches the device management service's OAuth2 token using
-  // |oauth2_tokens.refresh_token|.
-  PolicyOAuth2TokenFetcher(net::URLRequestContextGetter* system_context_getter,
-                           const std::string& oauth2_refresh_token,
-                           const TokenCallback& callback);
-
   // Fetches the device management service's oauth2 token, after also retrieving
   // the OAuth2 refresh tokens.
   PolicyOAuth2TokenFetcher(net::URLRequestContextGetter* auth_context_getter,
diff --git a/chrome/browser/chromeos/policy/recommendation_restorer_unittest.cc b/chrome/browser/chromeos/policy/recommendation_restorer_unittest.cc
index 664681e..f0e9477 100644
--- a/chrome/browser/chromeos/policy/recommendation_restorer_unittest.cc
+++ b/chrome/browser/chromeos/policy/recommendation_restorer_unittest.cc
@@ -129,7 +129,7 @@
   ASSERT_FALSE(restorer_);
   TestingProfile* profile = profile_manager_.CreateTestingProfile(
       chrome::kInitialProfile, prefs_owner_.Pass(),
-      UTF8ToUTF16(chrome::kInitialProfile), 0);
+      UTF8ToUTF16(chrome::kInitialProfile), 0, false);
   restorer_ = RecommendationRestorerFactory::GetForProfile(profile);
   EXPECT_TRUE(restorer_);
 }
@@ -137,7 +137,7 @@
 void RecommendationRestorerTest::CreateUserProfile() {
   ASSERT_FALSE(restorer_);
   TestingProfile* profile = profile_manager_.CreateTestingProfile(
-      "user", prefs_owner_.Pass(), UTF8ToUTF16("user"), 0);
+      "user", prefs_owner_.Pass(), UTF8ToUTF16("user"), 0, false);
   restorer_ = RecommendationRestorerFactory::GetForProfile(profile);
   EXPECT_TRUE(restorer_);
 }
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
index dcc41d5..e3452f9 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
@@ -7,7 +7,6 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/logging.h"
-#include "base/memory/ref_counted.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h"
@@ -65,13 +64,13 @@
   }
 }
 
-void UserCloudPolicyManagerChromeOS::OnRefreshTokenAvailable(
-    const std::string& refresh_token) {
-  oauth2_login_tokens_.refresh_token = refresh_token;
-  if (!oauth2_login_tokens_.refresh_token.empty() &&
-      service() && service()->IsInitializationComplete() &&
+void UserCloudPolicyManagerChromeOS::OnAccessTokenAvailable(
+    const std::string& access_token) {
+  access_token_ = access_token;
+  if (service() && service()->IsInitializationComplete() &&
       client() && !client()->is_registered()) {
-    FetchPolicyOAuthTokenUsingRefreshToken();
+    OnOAuth2PolicyTokenFetched(
+        access_token, GoogleServiceAuthError(GoogleServiceAuthError::NONE));
   }
 }
 
@@ -130,15 +129,16 @@
   // fetch a refresh token, and then the policy token is fetched.
   //
   // If |wait_for_policy_fetch_| is false then the UserCloudPolicyTokenForwarder
-  // service will eventually call OnRefreshTokenAvailable() once the
-  // TokenService has one. That call may have already happened while waiting for
-  // initialization of the CloudPolicyService, so in that case check if a
-  // refresh token is already available.
+  // service will eventually call OnAccessTokenAvailable() once an access token
+  // is available. That call may have already happened while waiting for
+  // initialization of the CloudPolicyService, so in that case check if an
+  // access token is already available.
   if (!client()->is_registered()) {
     if (wait_for_policy_fetch_) {
       FetchPolicyOAuthTokenUsingSigninProfile();
-    } else if (!oauth2_login_tokens_.refresh_token.empty()) {
-      FetchPolicyOAuthTokenUsingRefreshToken();
+    } else if (!access_token_.empty()) {
+      OnOAuth2PolicyTokenFetched(
+          access_token_, GoogleServiceAuthError(GoogleServiceAuthError::NONE));
     }
   }
 
@@ -148,7 +148,7 @@
     // Start the refresh scheduler now, which will eventually refresh the
     // cached policy or make the first fetch once the OAuth2 token is
     // available.
-    StartRefreshScheduler();
+    StartRefreshSchedulerIfReady();
   }
 }
 
@@ -188,7 +188,7 @@
 
 void UserCloudPolicyManagerChromeOS::OnComponentCloudPolicyUpdated() {
   CheckAndPublishPolicy();
-  StartRefreshScheduler();
+  StartRefreshSchedulerIfReady();
 }
 
 void UserCloudPolicyManagerChromeOS::FetchPolicyOAuthTokenUsingSigninProfile() {
@@ -211,24 +211,10 @@
   token_fetcher_->Start();
 }
 
-void UserCloudPolicyManagerChromeOS::FetchPolicyOAuthTokenUsingRefreshToken() {
-  token_fetcher_.reset(new PolicyOAuth2TokenFetcher(
-      g_browser_process->system_request_context(),
-      oauth2_login_tokens_.refresh_token,
-      base::Bind(&UserCloudPolicyManagerChromeOS::OnOAuth2PolicyTokenFetched,
-                 base::Unretained(this))));
-  token_fetcher_->Start();
-}
-
 void UserCloudPolicyManagerChromeOS::OnOAuth2PolicyTokenFetched(
     const std::string& policy_token,
     const GoogleServiceAuthError& error) {
   DCHECK(!client()->is_registered());
-  // The TokenService will reuse the refresh token fetched by the
-  // |token_fetcher_|, if it fetched one.
-  if (token_fetcher_->has_oauth2_tokens())
-    oauth2_login_tokens_ = token_fetcher_->oauth2_tokens();
-
   if (error.state() == GoogleServiceAuthError::NONE) {
     // Start client registration. Either OnRegistrationStateChanged() or
     // OnClientError() will be called back.
@@ -252,10 +238,10 @@
   CheckAndPublishPolicy();
   // Now that |wait_for_policy_fetch_| is guaranteed to be false, the scheduler
   // can be started.
-  StartRefreshScheduler();
+  StartRefreshSchedulerIfReady();
 }
 
-void UserCloudPolicyManagerChromeOS::StartRefreshScheduler() {
+void UserCloudPolicyManagerChromeOS::StartRefreshSchedulerIfReady() {
   if (core()->refresh_scheduler())
     return;  // Already started.
 
@@ -273,7 +259,7 @@
     return;
   }
 
-  core()->StartRefreshScheduler();
+  StartRefreshScheduler();
   core()->TrackRefreshDelayPref(local_state_, prefs::kUserPolicyRefreshRate);
 }
 
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h
index 94fcfff..b5c6623 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h
@@ -17,8 +17,8 @@
 #include "chrome/browser/policy/cloud/cloud_policy_service.h"
 #include "chrome/browser/policy/cloud/component_cloud_policy_service.h"
 #include "components/browser_context_keyed_service/browser_context_keyed_service.h"
-#include "google_apis/gaia/gaia_auth_consumer.h"
 
+class GoogleServiceAuthError;
 class PrefService;
 
 namespace net {
@@ -55,19 +55,20 @@
                scoped_refptr<net::URLRequestContextGetter> request_context,
                UserAffiliation user_affiliation);
 
-  // The OAuth2 login |refresh_token| can be used to obtain a policy OAuth2
-  // token, if the CloudPolicyClient isn't registered yet.
-  void OnRefreshTokenAvailable(const std::string& refresh_token);
+  // This class is one of the policy providers, and must be ready for the
+  // creation of the Profile's PrefService; all the other
+  // BrowserContextKeyedServices depend on the PrefService, so this class can't
+  // depend on other BCKS to avoid a circular dependency. So instead of using
+  // the ProfileOAuth2TokenService directly to get the access token, a 3rd
+  // service (UserCloudPolicyTokenForwarder) will fetch it later and pass it
+  // to this method once available.
+  // The |access_token| can then be used to authenticate the registration
+  // request to the DMServer.
+  void OnAccessTokenAvailable(const std::string& access_token);
 
   // Returns true if the underlying CloudPolicyClient is already registered.
   bool IsClientRegistered() const;
 
-  // Returns the OAuth2 tokens obtained by the manager for the initial
-  // registration, if it had to perform a blocking policy fetch.
-  const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens() const {
-    return oauth2_login_tokens_;
-  }
-
   // ConfigurationPolicyProvider:
   virtual void Shutdown() OVERRIDE;
   virtual bool IsInitializationComplete(PolicyDomain domain) const OVERRIDE;
@@ -90,11 +91,12 @@
   virtual void OnComponentCloudPolicyUpdated() OVERRIDE;
 
  private:
-  // These methods fetch a policy token using either the authentication context
-  // of the signin Profile or a refresh token passed in OnRefreshTokenAvailable.
-  // OnOAuth2PolicyTokenFetched is called back when the policy token is fetched.
+  // Fetches a policy token using the authentication context of the signin
+  // Profile, and calls back to OnOAuth2PolicyTokenFetched when done.
   void FetchPolicyOAuthTokenUsingSigninProfile();
-  void FetchPolicyOAuthTokenUsingRefreshToken();
+
+  // Called once the policy access token is available, and starts the
+  // registration with the policy server if the token was successfully fetched.
   void OnOAuth2PolicyTokenFetched(const std::string& policy_token,
                                   const GoogleServiceAuthError& error);
 
@@ -108,7 +110,7 @@
   // have completed).
   void CancelWaitForPolicyFetch();
 
-  void StartRefreshScheduler();
+  void StartRefreshSchedulerIfReady();
 
   // Owns the store, note that CloudPolicyManager just keeps a plain pointer.
   scoped_ptr<CloudPolicyStore> store_;
@@ -128,9 +130,9 @@
   // a callback with an unretained reference to the manager, when it exists.
   scoped_ptr<PolicyOAuth2TokenFetcher> token_fetcher_;
 
-  // The OAuth2 login tokens fetched by the |token_fetcher_|, which can be
-  // retrieved using oauth2_tokens().
-  GaiaAuthConsumer::ClientOAuthResult oauth2_login_tokens_;
+  // The access token passed to OnAccessTokenAvailable. It is stored here so
+  // that it can be used if OnInitializationCompleted is called later.
+  std::string access_token_;
 
   DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyManagerChromeOS);
 };
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
index 7c3d9df..23c6ed8 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
@@ -27,6 +27,8 @@
 #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/prefs/pref_service_syncable.h"
+#include "chrome/browser/signin/profile_oauth2_token_service.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/token_service.h"
 #include "chrome/browser/signin/token_service_factory.h"
 #include "chrome/common/chrome_constants.h"
@@ -87,7 +89,7 @@
     ASSERT_TRUE(profile_manager_->SetUp());
     profile_ = profile_manager_->CreateTestingProfile(
         chrome::kInitialProfile, scoped_ptr<PrefServiceSyncable>(),
-        UTF8ToUTF16("testing_profile"), 0);
+        UTF8ToUTF16("testing_profile"), 0, false);
     signin_profile_ = profile_manager_->CreateTestingProfile(kSigninProfile);
     signin_profile_->set_incognito(true);
     // Usually the signin Profile and the main Profile are separate, but since
@@ -129,6 +131,8 @@
   }
 
   virtual void TearDown() OVERRIDE {
+    if (token_forwarder_)
+      token_forwarder_->Shutdown();
     if (manager_) {
       manager_->RemoveObserver(&observer_);
       manager_->Shutdown();
@@ -155,12 +159,12 @@
     EXPECT_FALSE(manager_->core()->service()->IsInitializationComplete());
 
     if (!wait_for_fetch) {
-      // Create the UserCloudPolicyTokenForwarder, which forwards the refresh
-      // token from the TokenService to the UserCloudPolicyManagerChromeOS.
-      // This service is automatically created for regular Profiles but not for
-      // testing Profiles.
-      TokenService* token_service =
-          TokenServiceFactory::GetForProfile(profile_);
+      // Create the UserCloudPolicyTokenForwarder, which fetches the access
+      // token using the OAuth2PolicyFetcher and forwards it to the
+      // UserCloudPolicyManagerChromeOS. This service is automatically created
+      // for regular Profiles but not for testing Profiles.
+      ProfileOAuth2TokenService* token_service =
+          ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
       ASSERT_TRUE(token_service);
       token_forwarder_.reset(
           new UserCloudPolicyTokenForwarder(manager_.get(), token_service));
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.cc b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.cc
index cba9426..ba98790 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.cc
@@ -4,45 +4,91 @@
 
 #include "chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h"
 
-#include "base/logging.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
-#include "chrome/browser/signin/token_service.h"
+#include "chrome/browser/policy/cloud/cloud_policy_core.h"
+#include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "content/public/browser/notification_source.h"
+#include "google_apis/gaia/gaia_constants.h"
 
 namespace policy {
 
 UserCloudPolicyTokenForwarder::UserCloudPolicyTokenForwarder(
     UserCloudPolicyManagerChromeOS* manager,
-    TokenService* token_service)
+    ProfileOAuth2TokenService* token_service)
     : manager_(manager),
       token_service_(token_service) {
-  if (token_service_->HasOAuthLoginToken()) {
-    manager_->OnRefreshTokenAvailable(
-        token_service_->GetOAuth2LoginRefreshToken());
+  // Start by waiting for the CloudPolicyService to be initialized, so that
+  // we can check if it already has a DMToken or not.
+  if (manager_->core()->service()->IsInitializationComplete()) {
+    Initialize();
   } else {
-    registrar_.Add(this,
-                   chrome::NOTIFICATION_TOKEN_AVAILABLE,
-                   content::Source<TokenService>(token_service_));
+    manager_->core()->service()->AddObserver(this);
   }
 }
 
 UserCloudPolicyTokenForwarder::~UserCloudPolicyTokenForwarder() {}
 
 void UserCloudPolicyTokenForwarder::Shutdown() {
-  registrar_.RemoveAll();
+  request_.reset();
+  token_service_->RemoveObserver(this);
+  manager_->core()->service()->RemoveObserver(this);
 }
 
-void UserCloudPolicyTokenForwarder::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  DCHECK(type == chrome::NOTIFICATION_TOKEN_AVAILABLE);
-  if (token_service_->HasOAuthLoginToken()) {
-    registrar_.RemoveAll();
-    manager_->OnRefreshTokenAvailable(
-        token_service_->GetOAuth2LoginRefreshToken());
+void UserCloudPolicyTokenForwarder::OnRefreshTokenAvailable(
+    const std::string& account_id) {
+  RequestAccessToken();
+}
+
+void UserCloudPolicyTokenForwarder::OnGetTokenSuccess(
+    const OAuth2TokenService::Request* request,
+    const std::string& access_token,
+    const base::Time& expiration_time) {
+  manager_->OnAccessTokenAvailable(access_token);
+  // All done here.
+  Shutdown();
+}
+
+void UserCloudPolicyTokenForwarder::OnGetTokenFailure(
+    const OAuth2TokenService::Request* request,
+    const GoogleServiceAuthError& error) {
+  // This should seldom happen: if the user is signing in for the first time
+  // then this was an online signin and network errors are unlikely; if the
+  // user had already signed in before then he should have policy cached, and
+  // RequestAccessToken() wouldn't have been invoked.
+  // Still, something just went wrong (server 500, or something). Currently
+  // we don't recover in this case, and we'll just try to register for policy
+  // again on the next signin.
+  // TODO(joaodasilva, atwilson): consider blocking signin when this happens,
+  // so that the user has to try again before getting into the session. That
+  // would guarantee that a session always has fresh policy, or at least
+  // enforces a cached policy.
+  Shutdown();
+}
+
+void UserCloudPolicyTokenForwarder::OnInitializationCompleted(
+    CloudPolicyService* service) {
+  Initialize();
+}
+
+void UserCloudPolicyTokenForwarder::Initialize() {
+  if (manager_->IsClientRegistered()) {
+    // We already have a DMToken, so no need to ask for an access token.
+    // All done here.
+    Shutdown();
+    return;
   }
+
+  if (token_service_->RefreshTokenIsAvailable())
+    RequestAccessToken();
+  else
+    token_service_->AddObserver(this);
+}
+
+void UserCloudPolicyTokenForwarder::RequestAccessToken() {
+  OAuth2TokenService::ScopeSet scopes;
+  scopes.insert(GaiaConstants::kDeviceManagementServiceOAuth);
+  request_ = token_service_->StartRequest(scopes, this);
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h
index 952bd25..dbd93a4 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h
@@ -5,45 +5,58 @@
 #ifndef CHROME_BROWSER_CHROMEOS_POLICY_USER_CLOUD_POLICY_TOKEN_FORWARDER_H_
 #define CHROME_BROWSER_CHROMEOS_POLICY_USER_CLOUD_POLICY_TOKEN_FORWARDER_H_
 
-#include <string>
-
 #include "base/basictypes.h"
+#include "chrome/browser/policy/cloud/cloud_policy_service.h"
+#include "chrome/browser/signin/oauth2_token_service.h"
 #include "components/browser_context_keyed_service/browser_context_keyed_service.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 
-class TokenService;
+class ProfileOAuth2TokenService;
 
 namespace policy {
 
 class UserCloudPolicyManagerChromeOS;
 
-// A PKS that observes a TokenService and passes its refresh token to a
-// UserCloudPolicyManagerChromeOS, when it becomes available. This service
-// decouples the UserCloudPolicyManagerChromeOS from depending directly on the
-// TokenService, since it is initialized much earlier.
+// A PKS that observes a ProfileOAuth2TokenService and mints the policy access
+// token for the UserCloudPolicyManagerChromeOS, when the token service becomes
+// ready. This service decouples the UserCloudPolicyManagerChromeOS from
+// depending directly on the ProfileOAuth2TokenService, since it is initialized
+// much earlier.
 class UserCloudPolicyTokenForwarder : public BrowserContextKeyedService,
-                                      public content::NotificationObserver {
+                                      public OAuth2TokenService::Observer,
+                                      public OAuth2TokenService::Consumer,
+                                      public CloudPolicyService::Observer {
  public:
   // The factory of this PKS depends on the factories of these two arguments,
   // so this object will be Shutdown() first and these pointers can be used
   // until that point.
   UserCloudPolicyTokenForwarder(UserCloudPolicyManagerChromeOS* manager,
-                                TokenService* token_service);
+                                ProfileOAuth2TokenService* token_service);
   virtual ~UserCloudPolicyTokenForwarder();
 
   // BrowserContextKeyedService:
   virtual void Shutdown() OVERRIDE;
 
-  // NotificationObserver:
-  virtual void Observe(int type,
-                       const content::NotificationSource& source,
-                       const content::NotificationDetails& details) OVERRIDE;
+  // OAuth2TokenService::Observer:
+  virtual void OnRefreshTokenAvailable(const std::string& account_id) OVERRIDE;
+
+  // OAuth2TokenService::Consumer:
+  virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+                                 const std::string& access_token,
+                                 const base::Time& expiration_time) OVERRIDE;
+  virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+                                 const GoogleServiceAuthError& error) OVERRIDE;
+
+  // CloudPolicyService::Observer:
+  virtual void OnInitializationCompleted(CloudPolicyService* service) OVERRIDE;
 
  private:
+  void Initialize();
+
+  void RequestAccessToken();
+
   UserCloudPolicyManagerChromeOS* manager_;
-  TokenService* token_service_;
-  content::NotificationRegistrar registrar_;
+  ProfileOAuth2TokenService* token_service_;
+  scoped_ptr<OAuth2TokenService::Request> request_;
 
   DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyTokenForwarder);
 };
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.cc b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.cc
index 394c4a8..fbb3abd 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.cc
@@ -8,8 +8,8 @@
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/token_service.h"
-#include "chrome/browser/signin/token_service_factory.h"
+#include "chrome/browser/signin/profile_oauth2_token_service.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
 
 namespace policy {
@@ -24,7 +24,7 @@
     : BrowserContextKeyedServiceFactory(
         "UserCloudPolicyTokenForwarder",
         BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(TokenServiceFactory::GetInstance());
+  DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
   DependsOn(UserCloudPolicyManagerFactoryChromeOS::GetInstance());
 }
 
@@ -36,19 +36,10 @@
   Profile* profile = static_cast<Profile*>(context);
   UserCloudPolicyManagerChromeOS* manager =
       UserCloudPolicyManagerFactoryChromeOS::GetForProfile(profile);
-  TokenService* token_service =
-      TokenServiceFactory::GetForProfile(profile);
+  ProfileOAuth2TokenService* token_service =
+      ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
   if (!token_service || !manager)
     return NULL;
-  if (manager->IsClientRegistered()) {
-    // The CloudPolicyClient is already registered, so the manager doesn't need
-    // the refresh token. The manager may have fetched a refresh token if it
-    // performed a blocking policy fetch; send it to the TokenService in that
-    // case, so that it can be reused for other services.
-    if (!manager->oauth2_tokens().refresh_token.empty())
-      token_service->UpdateCredentialsWithOAuth2(manager->oauth2_tokens());
-    return NULL;
-  }
   return new UserCloudPolicyTokenForwarder(manager, token_service);
 }
 
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.h b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.h
index a904cbe..3ccc7c0 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.h
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.h
@@ -15,8 +15,6 @@
 
 namespace policy {
 
-class UserCloudPolicyTokenForwarder;
-
 // Creates instances of UserCloudPolicyTokenForwarder for Profiles that may need
 // to fetch the policy token.
 class UserCloudPolicyTokenForwarderFactory
diff --git a/chrome/browser/chromeos/power/idle_action_warning_dialog_view.cc b/chrome/browser/chromeos/power/idle_action_warning_dialog_view.cc
index b1fa93e..8c99893 100644
--- a/chrome/browser/chromeos/power/idle_action_warning_dialog_view.cc
+++ b/chrome/browser/chromeos/power/idle_action_warning_dialog_view.cc
@@ -73,7 +73,7 @@
       this, ash::Shell::GetPrimaryRootWindow(), NULL)->Show();
 }
 
-void IdleActionWarningDialogView::Close() {
+void IdleActionWarningDialogView::CloseDialog() {
   closing_ = true;
   GetDialogClientView()->CancelWindow();
 }
diff --git a/chrome/browser/chromeos/power/idle_action_warning_dialog_view.h b/chrome/browser/chromeos/power/idle_action_warning_dialog_view.h
index c45b2ff..92dc34e 100644
--- a/chrome/browser/chromeos/power/idle_action_warning_dialog_view.h
+++ b/chrome/browser/chromeos/power/idle_action_warning_dialog_view.h
@@ -17,7 +17,7 @@
 class IdleActionWarningDialogView : public views::DialogDelegateView {
  public:
   IdleActionWarningDialogView();
-  void Close();
+  void CloseDialog();
 
   // views::DialogDelegateView:
   virtual ui::ModalType GetModalType() const OVERRIDE;
diff --git a/chrome/browser/chromeos/power/idle_action_warning_observer.cc b/chrome/browser/chromeos/power/idle_action_warning_observer.cc
index eed0c9c..d452cba 100644
--- a/chrome/browser/chromeos/power/idle_action_warning_observer.cc
+++ b/chrome/browser/chromeos/power/idle_action_warning_observer.cc
@@ -16,7 +16,7 @@
 IdleActionWarningObserver::~IdleActionWarningObserver() {
   DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
   if (warning_dialog_)
-    warning_dialog_->Close();
+    warning_dialog_->CloseDialog();
 }
 
 void IdleActionWarningObserver::IdleActionImminent() {
@@ -26,7 +26,7 @@
 
 void IdleActionWarningObserver::IdleActionDeferred() {
   if (warning_dialog_)
-    warning_dialog_->Close();
+    warning_dialog_->CloseDialog();
   warning_dialog_ = NULL;
 }
 
diff --git a/chrome/browser/chromeos/profiles/profile_util.cc b/chrome/browser/chromeos/profiles/profile_util.cc
new file mode 100644
index 0000000..25222ba
--- /dev/null
+++ b/chrome/browser/chromeos/profiles/profile_util.cc
@@ -0,0 +1,22 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/chromeos/profiles/profile_util.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/login/login_state.h"
+
+namespace chromeos {
+
+bool IsProfileAssociatedWithGaiaAccount(Profile* profile) {
+  if (!chromeos::LoginState::IsInitialized())
+    return false;
+  if (!chromeos::LoginState::Get()->IsUserGaiaAuthenticated())
+    return false;
+  if (profile->IsOffTheRecord())
+    return false;
+  return true;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/profiles/profile_util.h b/chrome/browser/chromeos/profiles/profile_util.h
new file mode 100644
index 0000000..e6270b0
--- /dev/null
+++ b/chrome/browser/chromeos/profiles/profile_util.h
@@ -0,0 +1,19 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PROFILES_PROFILE_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_PROFILES_PROFILE_UTIL_H_
+
+class Profile;
+
+namespace chromeos {
+
+// Checks if the current log-in state and the profile is GAIA-authenticated and
+// thus has access to Google services. This excludes, for example, public
+// accounts, locally managed users, guest or incognito profiles.
+bool IsProfileAssociatedWithGaiaAccount(Profile* profile);
+
+} // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PROFILES_PROFILE_UTIL_H_
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
index 2c122a9..2e63102 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
@@ -190,16 +190,17 @@
 void DeviceOAuth2TokenService::ValidatingConsumer::InformConsumer() {
   DCHECK(token_fetch_done_);
   DCHECK(token_validation_done_);
+  token_service_->OnValidationComplete(token_is_valid_);
+  // Note: this object (which is also the Request instance) may be deleted in
+  // these consumer callbacks, so the callbacks must be the last line executed.
   if (!token_is_valid_) {
-    consumer_->OnGetTokenFailure(request_.get(), GoogleServiceAuthError(
+    consumer_->OnGetTokenFailure(this, GoogleServiceAuthError(
         GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
   } else if (error_) {
-    consumer_->OnGetTokenFailure(request_.get(), *error_.get());
+    consumer_->OnGetTokenFailure(this, *error_.get());
   } else {
-    consumer_->OnGetTokenSuccess(request_.get(), access_token_,
-                                 expiration_time_);
+    consumer_->OnGetTokenSuccess(this, access_token_, expiration_time_);
   }
-  token_service_->OnValidationComplete(this, token_is_valid_);
 }
 
 DeviceOAuth2TokenService::DeviceOAuth2TokenService(
@@ -237,7 +238,6 @@
 }
 
 void DeviceOAuth2TokenService::OnValidationComplete(
-    ValidatingConsumer* validator,
     bool refresh_token_is_valid) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   refresh_token_is_valid_ = refresh_token_is_valid;
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.h b/chrome/browser/chromeos/settings/device_oauth2_token_service.h
index a53f964..ca6f41e 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service.h
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.h
@@ -69,7 +69,7 @@
   // Implementation of OAuth2TokenService.
   virtual net::URLRequestContextGetter* GetRequestContext() OVERRIDE;
 
-  void OnValidationComplete(ValidatingConsumer* validator, bool token_is_valid);
+  void OnValidationComplete(bool token_is_valid);
 
   bool refresh_token_is_valid_;
   int max_refresh_token_validation_retries_;
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc
index 14c02e6..f18d144 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -15,7 +15,6 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/cros/network_library.h"
 #include "chrome/browser/chromeos/policy/device_local_account.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/cros_settings_names.h"
@@ -26,6 +25,12 @@
 #include "chrome/browser/ui/options/options_util.h"
 #include "chrome/installer/util/google_update_settings.h"
 #include "chromeos/chromeos_switches.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/network_device_handler.h"
+#include "chromeos/network/network_event_log.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state_handler.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 
 using google::protobuf::RepeatedField;
 using google::protobuf::RepeatedPtrField;
@@ -86,6 +91,12 @@
   return GoogleUpdateSettings::GetCollectStatsConsent();
 }
 
+void LogShillError(
+    const std::string& name,
+    scoped_ptr<base::DictionaryValue> error_data) {
+  NET_LOG_ERROR("Shill error: " + name, "Network operation failed.");
+}
+
 }  // namespace
 
 DeviceSettingsProvider::DeviceSettingsProvider(
@@ -778,20 +789,35 @@
 }
 
 void DeviceSettingsProvider::ApplyRoamingSetting(bool new_value) {
-  if (!NetworkLibrary::Get())
-    return;  // May not be initialized in tests.
-  NetworkLibrary* cros = NetworkLibrary::Get();
-  const NetworkDevice* cellular = cros->FindCellularDevice();
-  if (cellular) {
-    bool device_value = cellular->data_roaming_allowed();
-    if (!device_value && cros->IsCellularAlwaysInRoaming()) {
-      // If operator requires roaming always enabled, ignore supplied value
-      // and set data roaming allowed in true always.
-      cros->SetCellularDataRoamingAllowed(true);
-    } else if (device_value != new_value) {
-      cros->SetCellularDataRoamingAllowed(new_value);
-    }
+  // TODO(armansito): Look up the device by explicitly using the device path.
+  const DeviceState* cellular =
+      NetworkHandler::Get()->network_state_handler()->
+          GetDeviceStateByType(flimflam::kTypeCellular);
+  if (!cellular) {
+    NET_LOG_DEBUG("No cellular device is available",
+                  "Roaming is only supported by cellular devices.");
+    return;
   }
+  bool current_value;
+  if (!cellular->properties().GetBooleanWithoutPathExpansion(
+          flimflam::kCellularAllowRoamingProperty, &current_value)) {
+    NET_LOG_ERROR("Could not get \"allow roaming\" property from cellular "
+                  "device.", cellular->path());
+    return;
+  }
+
+  // Only set the value if the current value is different from |new_value|.
+  // If roaming is required by the provider, always try to set to true.
+  new_value = (cellular->provider_requires_roaming() ? true : new_value);
+  if (new_value == current_value)
+    return;
+
+  NetworkHandler::Get()->network_device_handler()->SetDeviceProperty(
+      cellular->path(),
+      flimflam::kCellularAllowRoamingProperty,
+      base::FundamentalValue(new_value),
+      base::Bind(&base::DoNothing),
+      base::Bind(&LogShillError));
 }
 
 void DeviceSettingsProvider::ApplySideEffects(
diff --git a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
index 8cca8f5..1f8a44a 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/path_service.h"
 #include "base/test/scoped_path_override.h"
 #include "base/values.h"
-#include "chrome/browser/chromeos/cros/network_library.h"
 #include "chrome/browser/chromeos/policy/device_local_account.h"
 #include "chrome/browser/chromeos/settings/cros_settings_names.h"
 #include "chrome/browser/chromeos/settings/device_settings_test_helper.h"
@@ -58,8 +57,6 @@
     DeviceSettingsTestBase::TearDown();
   }
 
-  ScopedStubNetworkLibraryEnabler stub_network_library_enabler_;
-
   ScopedTestingLocalState local_state_;
 
   scoped_ptr<DeviceSettingsProvider> provider_;
diff --git a/chrome/browser/chromeos/settings/device_settings_test_helper.cc b/chrome/browser/chromeos/settings/device_settings_test_helper.cc
index d99553a..f03ebf3 100644
--- a/chrome/browser/chromeos/settings/device_settings_test_helper.cc
+++ b/chrome/browser/chromeos/settings/device_settings_test_helper.cc
@@ -10,6 +10,8 @@
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "chrome/browser/chromeos/settings/mock_owner_key_util.h"
 #include "chrome/browser/policy/proto/chromeos/chrome_device_policy.pb.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/network/network_handler.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace chromeos {
@@ -205,6 +207,11 @@
 }
 
 void DeviceSettingsTestBase::SetUp() {
+  // Initialize DBusThreadManager with a stub implementation.
+  DBusThreadManager::InitializeWithStub();
+  NetworkHandler::Initialize();
+  loop_.RunUntilIdle();
+
   device_policy_.payload().mutable_metrics_enabled()->set_metrics_enabled(
       false);
   owner_key_util_->SetPublicKeyFromPrivateKey(device_policy_.signing_key());
@@ -217,6 +224,8 @@
 void DeviceSettingsTestBase::TearDown() {
   FlushDeviceSettings();
   device_settings_service_.UnsetSessionManager();
+  NetworkHandler::Shutdown();
+  DBusThreadManager::Shutdown();
 }
 
 void DeviceSettingsTestBase::FlushDeviceSettings() {
diff --git a/chrome/browser/chromeos/status/network_menu_icon.cc b/chrome/browser/chromeos/status/network_menu_icon.cc
deleted file mode 100644
index b57c923..0000000
--- a/chrome/browser/chromeos/status/network_menu_icon.cc
+++ /dev/null
@@ -1,983 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/chromeos/status/network_menu_icon.h"
-
-#include <algorithm>
-#include <cmath>
-#include <map>
-#include <utility>
-
-#include "ash/system/chromeos/network/network_icon_animation.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/chromeos/accessibility/accessibility_util.h"
-#include "grit/ash_resources.h"
-#include "grit/ash_strings.h"
-#include "grit/generated_resources.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image_skia_operations.h"
-#include "ui/gfx/image/image_skia_source.h"
-#include "ui/gfx/size_conversions.h"
-
-using std::max;
-using std::min;
-
-using ash::network_icon::NetworkIconAnimation;
-
-namespace chromeos {
-
-namespace {
-
-// Amount to fade icons while connecting.
-const double kConnectingImageAlpha = 0.5;
-
-// Images for strength bars for wired networks.
-const int kNumBarsImages = 5;
-gfx::ImageSkia* kBarsImagesAnimatingDark[kNumBarsImages - 1];
-gfx::ImageSkia* kBarsImagesAnimatingLight[kNumBarsImages - 1];
-
-// Imagaes for strength arcs for wireless networks.
-const int kNumArcsImages = 5;
-gfx::ImageSkia* kArcsImagesAnimatingDark[kNumArcsImages - 1];
-gfx::ImageSkia* kArcsImagesAnimatingLight[kNumArcsImages - 1];
-
-// Badge offsets. The right and bottom offsets are computed based on the size
-// of the network icon and the badge in order to accomodate multiple icon
-// resolutions (ie. standard and high DPI).
-const int kBadgeLeftX = 0;
-const int kBadgeTopY = 0;
-
-int StrengthIndex(int strength, int count) {
-  if (strength == 0) {
-    return 0;
-  } else {
-    // Return an index in the range [1, count].
-    const float findex = (static_cast<float>(strength) / 100.0f) *
-        nextafter(static_cast<float>(count), 0);
-    int index = 1 + static_cast<int>(findex);
-    index = max(min(index, count), 1);
-    return index;
-  }
-}
-
-int WifiStrengthIndex(const WifiNetwork* wifi) {
-  return StrengthIndex(wifi->strength(), kNumArcsImages - 1);
-}
-
-int WimaxStrengthIndex(const WimaxNetwork* wimax) {
-  return StrengthIndex(wimax->strength(), kNumBarsImages - 1);
-}
-
-int CellularStrengthIndex(const CellularNetwork* cellular) {
-  return StrengthIndex(cellular->strength(), kNumBarsImages - 1);
-}
-
-const gfx::ImageSkia* BadgeForNetworkTechnology(
-    const CellularNetwork* cellular,
-    NetworkMenuIcon::ResourceColorTheme color) {
-  const int kUnknownBadgeType = -1;
-  int id = kUnknownBadgeType;
-  switch (cellular->network_technology()) {
-    case NETWORK_TECHNOLOGY_EVDO:
-      id = (color == NetworkMenuIcon::COLOR_DARK) ?
-          IDR_AURA_UBER_TRAY_NETWORK_3G_DARK :
-          IDR_AURA_UBER_TRAY_NETWORK_3G_LIGHT;
-      break;
-    case NETWORK_TECHNOLOGY_1XRTT:
-      id = IDR_AURA_UBER_TRAY_NETWORK_1X;
-      break;
-    case NETWORK_TECHNOLOGY_GPRS:
-      id = (color == NetworkMenuIcon::COLOR_DARK) ?
-          IDR_AURA_UBER_TRAY_NETWORK_GPRS_DARK :
-          IDR_AURA_UBER_TRAY_NETWORK_GPRS_LIGHT;
-      break;
-    case NETWORK_TECHNOLOGY_EDGE:
-      id = (color == NetworkMenuIcon::COLOR_DARK) ?
-          IDR_AURA_UBER_TRAY_NETWORK_EDGE_DARK :
-          IDR_AURA_UBER_TRAY_NETWORK_EDGE_LIGHT;
-      break;
-    case NETWORK_TECHNOLOGY_UMTS:
-      id = (color == NetworkMenuIcon::COLOR_DARK) ?
-          IDR_AURA_UBER_TRAY_NETWORK_3G_DARK :
-          IDR_AURA_UBER_TRAY_NETWORK_3G_LIGHT;
-      break;
-    case NETWORK_TECHNOLOGY_HSPA:
-      id = (color == NetworkMenuIcon::COLOR_DARK) ?
-          IDR_AURA_UBER_TRAY_NETWORK_HSPA_DARK :
-          IDR_AURA_UBER_TRAY_NETWORK_HSPA_LIGHT;
-      break;
-    case NETWORK_TECHNOLOGY_HSPA_PLUS:
-      id = (color == NetworkMenuIcon::COLOR_DARK) ?
-          IDR_AURA_UBER_TRAY_NETWORK_HSPA_PLUS_DARK :
-          IDR_AURA_UBER_TRAY_NETWORK_HSPA_PLUS_LIGHT;
-      break;
-    case NETWORK_TECHNOLOGY_LTE:
-      id = (color == NetworkMenuIcon::COLOR_DARK) ?
-          IDR_AURA_UBER_TRAY_NETWORK_LTE_DARK :
-          IDR_AURA_UBER_TRAY_NETWORK_LTE_LIGHT;
-      break;
-    case NETWORK_TECHNOLOGY_LTE_ADVANCED:
-      id = (color == NetworkMenuIcon::COLOR_DARK) ?
-          IDR_AURA_UBER_TRAY_NETWORK_LTE_ADVANCED_DARK :
-          IDR_AURA_UBER_TRAY_NETWORK_LTE_ADVANCED_LIGHT;
-      break;
-    case NETWORK_TECHNOLOGY_GSM:
-      id = (color == NetworkMenuIcon::COLOR_DARK) ?
-          IDR_AURA_UBER_TRAY_NETWORK_GPRS_DARK :
-          IDR_AURA_UBER_TRAY_NETWORK_GPRS_LIGHT;
-      break;
-    case NETWORK_TECHNOLOGY_UNKNOWN:
-      break;
-  }
-  if (id == kUnknownBadgeType)
-    return NULL;
-  else
-    return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id);
-}
-
-const SkBitmap GetEmptyBitmap(const gfx::Size pixel_size) {
-  typedef std::pair<int, int> SizeKey;
-  typedef std::map<SizeKey, SkBitmap> SizeBitmapMap;
-  static SizeBitmapMap* empty_bitmaps_ = new SizeBitmapMap;
-
-  SizeKey key(pixel_size.width(), pixel_size.height());
-
-  SizeBitmapMap::iterator iter = empty_bitmaps_->find(key);
-  if (iter != empty_bitmaps_->end())
-    return iter->second;
-
-  SkBitmap empty;
-  empty.setConfig(SkBitmap::kARGB_8888_Config, key.first, key.second);
-  empty.allocPixels();
-  empty.eraseARGB(0, 0, 0, 0);
-  (*empty_bitmaps_)[key] = empty;
-  return empty;
-}
-
-class EmptyImageSource: public gfx::ImageSkiaSource {
- public:
-  explicit EmptyImageSource(const gfx::Size& size)
-      : size_(size) {
-  }
-
-  virtual gfx::ImageSkiaRep GetImageForScale(
-      ui::ScaleFactor scale_factor) OVERRIDE {
-    gfx::Size pixel_size = gfx::ToFlooredSize(
-        gfx::ScaleSize(size_, ui::GetScaleFactorScale(scale_factor)));
-    SkBitmap empty_bitmap = GetEmptyBitmap(pixel_size);
-    return gfx::ImageSkiaRep(empty_bitmap, scale_factor);
-  }
- private:
-  const gfx::Size size_;
-
-  DISALLOW_COPY_AND_ASSIGN(EmptyImageSource);
-};
-
-// This defines how we assemble a network icon.
-class NetworkIconImageSource : public gfx::ImageSkiaSource {
- public:
-  NetworkIconImageSource(const gfx::ImageSkia& icon,
-                         const gfx::ImageSkia* top_left_badge,
-                         const gfx::ImageSkia* top_right_badge,
-                         const gfx::ImageSkia* bottom_left_badge,
-                         const gfx::ImageSkia* bottom_right_badge)
-      : icon_(icon),
-        top_left_badge_(top_left_badge),
-        top_right_badge_(top_right_badge),
-        bottom_left_badge_(bottom_left_badge),
-        bottom_right_badge_(bottom_right_badge) {
-  }
-  virtual ~NetworkIconImageSource() {}
-
-  virtual gfx::ImageSkiaRep GetImageForScale(
-      ui::ScaleFactor scale_factor) OVERRIDE {
-    gfx::ImageSkiaRep icon_rep = icon_.GetRepresentation(scale_factor);
-    if (icon_rep.is_null())
-      return gfx::ImageSkiaRep();
-    gfx::Canvas canvas(icon_rep, false);
-    if (top_left_badge_)
-      canvas.DrawImageInt(*top_left_badge_, kBadgeLeftX, kBadgeTopY);
-    if (top_right_badge_)
-      canvas.DrawImageInt(*top_right_badge_,
-                          icon_.width() - top_right_badge_->width(),
-                          kBadgeTopY);
-    if (bottom_left_badge_) {
-      canvas.DrawImageInt(*bottom_left_badge_,
-                          kBadgeLeftX,
-                          icon_.height() - bottom_left_badge_->height());
-    }
-    if (bottom_right_badge_) {
-      canvas.DrawImageInt(*bottom_right_badge_,
-                          icon_.width() - bottom_right_badge_->width(),
-                          icon_.height() - bottom_right_badge_->height());
-    }
-    return canvas.ExtractImageRep();
-  }
-
- private:
-  const gfx::ImageSkia icon_;
-  const gfx::ImageSkia *top_left_badge_;
-  const gfx::ImageSkia *top_right_badge_;
-  const gfx::ImageSkia *bottom_left_badge_;
-  const gfx::ImageSkia *bottom_right_badge_;
-
-  DISALLOW_COPY_AND_ASSIGN(NetworkIconImageSource);
-};
-
-gfx::ImageSkia GetEmptyImage(const gfx::Size& size) {
-  return gfx::ImageSkia(new EmptyImageSource(size), size);
-}
-
-}  // namespace
-
-////////////////////////////////////////////////////////////////////////////////
-// NetworkIcon
-
-// Sets up and generates an ImageSkia for a Network icon.
-class NetworkIcon {
- public:
-  // Default constructor is used by the status bar icon (NetworkMenuIcon).
-  explicit NetworkIcon(NetworkMenuIcon::ResourceColorTheme color);
-
-  // Service path constructor for cached network service icons.
-  NetworkIcon(const std::string& service_path,
-              NetworkMenuIcon::ResourceColorTheme color);
-
-  ~NetworkIcon();
-
-  // Resets the icon state.
-  void ClearIconAndBadges();
-
-  // Resets the saved state to force an update.
-  void SetDirty();
-
-  // Updates |vpn_connected_|, returns true if it changed.
-  bool SetOrClearVpnConnected(const Network* network);
-
-  // Determines whether or not the associated network might be dirty and if so
-  // updates and generates the icon. Does nothing if network no longer exists.
-  void Update();
-
-  // Sets up the base icon image.
-  void SetIcon(const Network* network);
-
-  // Sets up the various badges:
-  // top_left: cellular roaming
-  // top_right: libcros warning
-  // bottom_left: VPN
-  // bottom_right: disconnected / secure / technology / warning
-  void SetBadges(const Network* network);
-
-  // Clears any previous state then sets the base icon and badges.
-  void UpdateIcon(const Network* network);
-
-  // Generates the image. Call after setting the icon and badges.
-  void GenerateImage();
-
-  const gfx::ImageSkia GetImage() const { return image_; }
-
-  bool ShouldShowInTray() const;
-
-  ConnectionType type() { return type_; }
-  void set_type(ConnectionType type) { type_ = type; }
-  void set_state(ConnectionState state) { state_ = state; }
-  void set_icon(const gfx::ImageSkia& icon) { icon_ = icon; }
-  void set_top_left_badge(const gfx::ImageSkia* badge) {
-    top_left_badge_ = badge;
-  }
-  void set_top_right_badge(const gfx::ImageSkia* badge) {
-    top_right_badge_ = badge;
-  }
-  void set_bottom_left_badge(const gfx::ImageSkia* badge) {
-    bottom_left_badge_ = badge;
-  }
-  void set_bottom_right_badge(const gfx::ImageSkia* badge) {
-    bottom_right_badge_ = badge;
-  }
-
- private:
-  // Updates strength_index_ for wifi or cellular networks.
-  // Returns true if |strength_index_| changed.
-  bool UpdateWirelessStrengthIndex(const Network* network);
-
-  // Updates the local state for cellular networks.
-  bool UpdateCellularState(const Network* network);
-
-  std::string service_path_;
-  ConnectionType type_;
-  ConnectionState state_;
-  NetworkMenuIcon::ResourceColorTheme resource_color_theme_;
-  int strength_index_;
-  gfx::ImageSkia image_;
-  gfx::ImageSkia icon_;
-  const gfx::ImageSkia* top_left_badge_;
-  const gfx::ImageSkia* top_right_badge_;
-  const gfx::ImageSkia* bottom_left_badge_;
-  const gfx::ImageSkia* bottom_right_badge_;
-  bool is_status_bar_;
-  const Network* connected_network_;  // weak pointer; used for VPN icons.
-  bool vpn_connected_;
-  NetworkRoamingState roaming_state_;
-
-  DISALLOW_COPY_AND_ASSIGN(NetworkIcon);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// NetworkIcon
-
-NetworkIcon::NetworkIcon(NetworkMenuIcon::ResourceColorTheme color)
-    : type_(TYPE_UNKNOWN),
-      state_(STATE_UNKNOWN),
-      resource_color_theme_(color),
-      strength_index_(-1),
-      top_left_badge_(NULL),
-      top_right_badge_(NULL),
-      bottom_left_badge_(NULL),
-      bottom_right_badge_(NULL),
-      is_status_bar_(true),
-      connected_network_(NULL),
-      vpn_connected_(false),
-      roaming_state_(ROAMING_STATE_UNKNOWN) {
-}
-
-NetworkIcon::NetworkIcon(const std::string& service_path,
-                         NetworkMenuIcon::ResourceColorTheme color)
-    : service_path_(service_path),
-      type_(TYPE_UNKNOWN),
-      state_(STATE_UNKNOWN),
-      resource_color_theme_(color),
-      strength_index_(-1),
-      top_left_badge_(NULL),
-      top_right_badge_(NULL),
-      bottom_left_badge_(NULL),
-      bottom_right_badge_(NULL),
-      is_status_bar_(false),
-      connected_network_(NULL),
-      vpn_connected_(false),
-      roaming_state_(ROAMING_STATE_UNKNOWN) {
-}
-
-NetworkIcon::~NetworkIcon() {
-}
-
-void NetworkIcon::ClearIconAndBadges() {
-  icon_ = gfx::ImageSkia();
-  top_left_badge_ = NULL;
-  top_right_badge_ = NULL;
-  bottom_left_badge_ = NULL;
-  bottom_right_badge_ = NULL;
-}
-
-void NetworkIcon::SetDirty() {
-  state_ = STATE_UNKNOWN;
-  strength_index_ = -1;
-}
-
-bool NetworkIcon::SetOrClearVpnConnected(const Network* network) {
-  if (network->type() == TYPE_VPN)
-    return false;  // Never show the VPN badge for a VPN network.
-  chromeos::NetworkLibrary* cros =
-      chromeos::NetworkLibrary::Get();
-  bool vpn_connected = (network->connected() &&
-                        cros->virtual_network() &&
-                        cros->virtual_network()->connected());
-  if (vpn_connected_ != vpn_connected) {
-    vpn_connected_ = vpn_connected;
-    return true;
-  }
-  return false;
-}
-
-void NetworkIcon::Update() {
-  chromeos::NetworkLibrary* cros =
-      chromeos::NetworkLibrary::Get();
-  // First look for a visible network.
-  const Network* network = cros->FindNetworkByPath(service_path_);
-  if (!network) {
-    // If not a visible network, check for a remembered network.
-    network = cros->FindRememberedNetworkByPath(service_path_);
-    if (!network) {
-      LOG(WARNING) << "Unable to find network:" << service_path_;
-      return;
-    }
-  }
-
-  // Determine whether or not we need to update the icon.
-  bool dirty = image_.isNull();
-
-  // If the network state has changed, the icon needs updating.
-  if (state_ != network->state()) {
-    state_ = network->state();
-    dirty = true;
-  }
-
-  type_ = network->type();
-
-  if (type_ == TYPE_WIFI || type_ == TYPE_WIMAX || type_ == TYPE_CELLULAR) {
-    if (UpdateWirelessStrengthIndex(network))
-      dirty = true;
-  }
-
-  if (type_ == TYPE_CELLULAR) {
-    if (UpdateCellularState(network))
-      dirty = true;
-  }
-
-  if (type_ == TYPE_VPN) {
-    // For VPN, check to see if the connected network has changed.
-    if (cros->connected_network() != connected_network_) {
-      connected_network_ = cros->connected_network();
-      dirty = true;
-    }
-  }
-
-  if (dirty) {
-    // Set the icon and badges based on the network.
-    UpdateIcon(network);
-    // Generate the image from the icon.
-    GenerateImage();
-  }
-}
-
-void NetworkIcon::SetIcon(const Network* network) {
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-
-  set_type(network->type());
-  set_state(network->state());
-
-  switch (type_) {
-    case TYPE_ETHERNET: {
-      icon_ = *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED);
-      break;
-    }
-    case TYPE_WIFI: {
-      const WifiNetwork* wifi = static_cast<const WifiNetwork*>(network);
-      if (strength_index_ == -1)
-        strength_index_ = WifiStrengthIndex(wifi);
-      icon_ = NetworkMenuIcon::GetImage(
-          NetworkMenuIcon::ARCS, strength_index_, resource_color_theme_);
-      break;
-    }
-    case TYPE_WIMAX: {
-      const WimaxNetwork* wimax = static_cast<const WimaxNetwork*>(network);
-      if (strength_index_ == -1)
-        strength_index_ =  WimaxStrengthIndex(wimax);
-      icon_ = NetworkMenuIcon::GetImage(
-          NetworkMenuIcon::BARS, strength_index_, resource_color_theme_);
-      break;
-    }
-    case TYPE_CELLULAR: {
-      const CellularNetwork* cellular =
-          static_cast<const CellularNetwork*>(network);
-      if (strength_index_ == -1)
-        strength_index_ = CellularStrengthIndex(cellular);
-      icon_ = NetworkMenuIcon::GetImage(
-          NetworkMenuIcon::BARS, strength_index_, resource_color_theme_);
-      break;
-    }
-    case TYPE_VPN: {
-      icon_ = *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_VPN);
-      break;
-    }
-    default: {
-      LOG(WARNING) << "Request for icon for unsupported type: " << type_;
-      icon_ = *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED);
-      break;
-    }
-  }
-}
-
-void NetworkIcon::SetBadges(const Network* network) {
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  chromeos::NetworkLibrary* cros =
-      chromeos::NetworkLibrary::Get();
-
-  bool use_dark_icons = resource_color_theme_ == NetworkMenuIcon::COLOR_DARK;
-  switch (network->type()) {
-    case TYPE_ETHERNET:
-      break;
-    case TYPE_WIFI: {
-      const WifiNetwork* wifi = static_cast<const WifiNetwork*>(network);
-      if (wifi->encrypted() && use_dark_icons) {
-        bottom_right_badge_ = rb.GetImageSkiaNamed(
-            IDR_AURA_UBER_TRAY_NETWORK_SECURE_DARK);
-      }
-      break;
-    }
-    case TYPE_WIMAX: {
-      top_left_badge_ = rb.GetImageSkiaNamed(
-          use_dark_icons ?
-          IDR_AURA_UBER_TRAY_NETWORK_4G_DARK :
-          IDR_AURA_UBER_TRAY_NETWORK_4G_LIGHT);
-      break;
-    }
-    case TYPE_CELLULAR: {
-      const CellularNetwork* cellular =
-            static_cast<const CellularNetwork*>(network);
-      if (cellular->roaming_state() == ROAMING_STATE_ROAMING &&
-          !cros->IsCellularAlwaysInRoaming()) {
-        // For cellular that always in roaming don't show roaming badge.
-        bottom_right_badge_ = rb.GetImageSkiaNamed(use_dark_icons ?
-            IDR_AURA_UBER_TRAY_NETWORK_ROAMING_DARK :
-            IDR_AURA_UBER_TRAY_NETWORK_ROAMING_LIGHT);
-      }
-      if (!cellular->connecting()) {
-        top_left_badge_ = BadgeForNetworkTechnology(cellular,
-                                                    resource_color_theme_);
-      }
-      break;
-    }
-    default:
-      break;
-  }
-  if (vpn_connected_ && network->type() != TYPE_VPN) {
-    bottom_left_badge_ = rb.GetImageSkiaNamed(
-        IDR_AURA_UBER_TRAY_NETWORK_VPN_BADGE);
-  }
-}
-
-void NetworkIcon::UpdateIcon(const Network* network) {
-  ClearIconAndBadges();
-  SetIcon(network);
-  SetBadges(network);
-}
-
-void NetworkIcon::GenerateImage() {
-  if (icon_.isNull()) {
-    set_icon(NetworkMenuIcon::GetDisconnectedImage(
-        NetworkMenuIcon::ARCS, resource_color_theme_));
-  }
-  image_ = NetworkMenuIcon::GenerateImageFromComponents(icon_, top_left_badge_,
-      top_right_badge_, bottom_left_badge_, bottom_right_badge_);
-}
-
-bool NetworkIcon::ShouldShowInTray() const {
-  if (type_ != TYPE_ETHERNET)
-    return true;
-  if (!Network::IsConnectedState(state_))
-    return true;
-  NetworkLibrary* crosnet = NetworkLibrary::Get();
-  if (crosnet->virtual_network())
-    return true;
-  return false;
-}
-
-bool NetworkIcon::UpdateWirelessStrengthIndex(const Network* network) {
-  bool dirty = false;
-  ConnectionType type = network->type();
-  int index = 0;
-  if (type == TYPE_WIFI) {
-    index = WifiStrengthIndex(static_cast<const WifiNetwork*>(network));
-  } else if (type == TYPE_WIMAX) {
-    index = WimaxStrengthIndex(static_cast<const WimaxNetwork*>(network));
-  } else if (type == TYPE_CELLULAR) {
-    index = CellularStrengthIndex(static_cast<const CellularNetwork*>(network));
-  }
-  if (index != strength_index_) {
-    strength_index_ = index;
-    dirty = true;
-  }
-  return dirty;
-}
-
-bool NetworkIcon::UpdateCellularState(const Network* network) {
-  if (network->type() != TYPE_CELLULAR)
-    return false;
-  bool dirty = false;
-  const CellularNetwork* cellular =
-    static_cast<const CellularNetwork*>(network);
-  const gfx::ImageSkia* technology_badge = BadgeForNetworkTechnology(
-      cellular, resource_color_theme_);
-  if (technology_badge != top_left_badge_) {
-    dirty = true;
-  }
-  if (cellular->roaming_state() != roaming_state_) {
-    roaming_state_ = cellular->roaming_state();
-    dirty = true;
-  }
-  return dirty;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// NetworkMenuIcon
-
-NetworkMenuIcon::NetworkMenuIcon(Delegate* delegate, Mode mode)
-    : mode_(mode),
-      delegate_(delegate),
-      resource_color_theme_(COLOR_DARK),
-      connecting_index_(-1) {
-  // Initialize the icon.
-  icon_.reset(new NetworkIcon(resource_color_theme_));
-}
-
-NetworkMenuIcon::~NetworkMenuIcon() {
-  // Remove itself from NetworkIconAnimation's observer list just in case
-  // it has been added before and not been removed yet.
-  NetworkIconAnimation::GetInstance()->RemoveObserver(this);
-}
-
-// Public methods:
-
-void NetworkMenuIcon::SetResourceColorTheme(ResourceColorTheme color) {
-  if (color == resource_color_theme_)
-    return;
-
-  resource_color_theme_ = color;
-  icon_.reset(new NetworkIcon(resource_color_theme_));
-}
-
-bool NetworkMenuIcon::ShouldShowIconInTray() {
-  if (!icon_.get())
-    return false;
-  return icon_->ShouldShowInTray();
-}
-
-const gfx::ImageSkia NetworkMenuIcon::GetIconAndText(string16* text) {
-  if (SetIconAndText())
-    NetworkIconAnimation::GetInstance()->AddObserver(this);
-  else
-    NetworkIconAnimation::GetInstance()->RemoveObserver(this);
-  if (text)
-    *text = text_;
-  icon_->GenerateImage();
-  return icon_->GetImage();
-}
-
-const gfx::ImageSkia NetworkMenuIcon::GetVpnIconAndText(string16* text) {
-  if (SetVpnIconAndText())
-    NetworkIconAnimation::GetInstance()->AddObserver(this);
-  else
-    NetworkIconAnimation::GetInstance()->RemoveObserver(this);
-  if (text)
-    *text = text_;
-  icon_->GenerateImage();
-  return icon_->GetImage();
-}
-
-void NetworkMenuIcon::NetworkIconChanged() {
-  if (!delegate_ || !icon_.get())
-    return;
-  // Only send a message when the icon would change.
-  int connecting_index = GetConnectingIndex();
-  if (connecting_index != connecting_index_) {
-    connecting_index_ = connecting_index;
-    delegate_->NetworkMenuIconChanged();
-  }
-}
-
-// Private methods:
-
-// If disconnected: returns any connecting non-ethernet network.
-// Otherwise, only return a network if the conenction was user initiated.
-const Network* NetworkMenuIcon::GetConnectingNetwork() {
-  NetworkLibrary* cros = NetworkLibrary::Get();
-  const Network* connecting_network = cros->connecting_network();
-  if (connecting_network &&
-      connecting_network->type() != TYPE_ETHERNET &&
-      (!cros->connected_network() ||
-       connecting_network->connection_started())) {
-    return connecting_network;
-  }
-  return NULL;
-}
-
-double NetworkMenuIcon::GetAnimation() {
-  return NetworkIconAnimation::GetInstance()->GetAnimation();
-}
-
-int NetworkMenuIcon::GetConnectingIndex() {
-  DCHECK(icon_.get());
-  double animation = GetAnimation();
-  int image_count =
-      (icon_->type() == TYPE_WIFI) ? kNumArcsImages - 1 : kNumBarsImages - 1;
-  int index = animation * nextafter(static_cast<float>(image_count), 0);
-  return std::max(std::min(index, image_count - 1), 0);
-}
-
-
-// TODO(stevenjb): move below SetIconAndText.
-void NetworkMenuIcon::SetConnectingIconAndText(
-    const Network* connecting_network) {
-  connecting_network_ = connecting_network;
-  ConnectionType type;
-  ConnectionState state;
-  if (connecting_network_) {
-    type = connecting_network_->type();
-    state = connecting_network_->state();
-    if (mode_ == MENU_MODE) {
-      text_ = l10n_util::GetStringFUTF16(
-          IDS_STATUSBAR_NETWORK_CONNECTING_TOOLTIP,
-          UTF8ToUTF16(connecting_network_->name()));
-    } else {
-      text_ = UTF8ToUTF16(connecting_network_->name());
-    }
-  } else {
-    // When called with no connecting network, cellular is initializing.
-    type = TYPE_CELLULAR;
-    state = STATE_ASSOCIATION;
-    text_ = l10n_util::GetStringUTF16(
-        IDS_ASH_STATUS_TRAY_INITIALIZING_CELLULAR);
-  }
-  icon_->set_type(type);
-  icon_->set_state(state);
-
-  ImageType image_type;
-  gfx::ImageSkia** images;
-  if (type == TYPE_WIFI) {
-    image_type = ARCS;
-    images = resource_color_theme_ == COLOR_DARK ? kArcsImagesAnimatingDark :
-                                                   kArcsImagesAnimatingLight;
-  } else {
-    image_type = BARS;
-    images = resource_color_theme_ == COLOR_DARK ? kBarsImagesAnimatingDark :
-                                                   kBarsImagesAnimatingLight;
-  }
-  int index = GetConnectingIndex();
-
-  // Lazily cache images.
-  if (!images[index]) {
-    gfx::ImageSkia source =
-        GetImage(image_type, index + 1, resource_color_theme_);
-    images[index] =
-        new gfx::ImageSkia(NetworkMenuIcon::GenerateConnectingImage(source));
-  }
-  icon_->set_icon(*images[index]);
-  if (connecting_network_)
-    icon_->SetBadges(connecting_network_);
-}
-
-// Sets up the icon and badges for GenerateBitmap().
-bool NetworkMenuIcon::SetIconAndText() {
-  NetworkLibrary* cros = NetworkLibrary::Get();
-  DCHECK(cros);
-
-  icon_->ClearIconAndBadges();
-
-  // If we are connecting to a network and it was user-initiated or we are
-  // not connected, display that.
-  const Network* connecting_network = GetConnectingNetwork();
-  if (connecting_network) {
-    SetConnectingIconAndText(connecting_network);
-    return true;
-  }
-
-  // If not connecting to a network, show the active or connected network.
-  const Network* network;
-  if (mode_ == DROPDOWN_MODE && cros->connected_network())
-    network = cros->connected_network();
-  else
-    network = cros->active_nonvirtual_network();
-  if (network)
-    return SetActiveNetworkIconAndText(network);
-
-  // If no connected network, check if we are initializing Cellular.
-  if (mode_ != DROPDOWN_MODE && cros->cellular_initializing()) {
-    initialize_state_time_ = base::Time::Now();
-    SetConnectingIconAndText(NULL);
-    return true;
-  }
-  // There can be a delay between leaving the Initializing state and when a
-  // Cellular device shows up, so keep showing the initializing animation
-  // for a few extra seconds to avoid flashing the disconnect icon.
-  const int kInitializingDelaySeconds = 1;
-  base::TimeDelta dtime = base::Time::Now() - initialize_state_time_;
-  if (dtime.InSeconds() < kInitializingDelaySeconds) {
-    SetConnectingIconAndText(NULL);
-    return true;
-  }
-
-  // No connecting, connected, or active network.
-  SetDisconnectedIconAndText();
-  return false;
-}
-
-bool NetworkMenuIcon::SetVpnIconAndText() {
-  NetworkLibrary* cros = NetworkLibrary::Get();
-  DCHECK(cros);
-
-  icon_->ClearIconAndBadges();
-  const VirtualNetwork* vpn = cros->virtual_network();
-  if (!vpn) {
-    LOG(WARNING) << "SetVpnIconAndText called with no VPN";
-    SetDisconnectedIconAndText();
-    return false;
-  }
-  if (vpn->connecting()) {
-    SetConnectingIconAndText(vpn);
-    return true;
-  }
-
-  // If not connecting to a VPN, show the active/connected VPN.
-  return SetActiveNetworkIconAndText(vpn);
-}
-
-bool NetworkMenuIcon::SetActiveNetworkIconAndText(const Network* network) {
-  NetworkLibrary* cros = NetworkLibrary::Get();
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  bool animating = false;
-
-  // Set icon and badges. Call SetDirty() since network may have changed.
-  icon_->SetDirty();
-  icon_->SetOrClearVpnConnected(network);
-  icon_->UpdateIcon(network);
-  // Overlay the VPN badge if connecting to a VPN.
-  if (network->type() != TYPE_VPN &&
-      cros->virtual_network() && cros->virtual_network()->connecting()) {
-    const gfx::ImageSkia* vpn_badge =
-        rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_VPN_BADGE);
-    const double animation = GetAnimation();
-    animating = true;
-    // Even though this is the only place we use vpn_connecting_badge_,
-    // it is important that this is a member variable since we set a
-    // pointer to it and access that pointer in icon_->GenerateImage().
-    vpn_connecting_badge_ = gfx::ImageSkiaOperations::CreateBlendedImage(
-        GetEmptyImage(vpn_badge->size()), *vpn_badge, animation);
-    icon_->set_bottom_left_badge(&vpn_connecting_badge_);
-  }
-
-  // Set the text to display.
-  if (network->type() == TYPE_ETHERNET) {
-    if (mode_ == MENU_MODE) {
-      text_ = l10n_util::GetStringFUTF16(
-          IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP,
-          l10n_util::GetStringUTF16(
-              IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET));
-    } else {
-      text_ = l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET);
-    }
-  } else {
-    if (mode_ == MENU_MODE) {
-      text_ = l10n_util::GetStringFUTF16(
-          IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP,
-          UTF8ToUTF16(network->name()));
-    } else {
-      text_ = UTF8ToUTF16(network->name());
-    }
-  }
-  return animating;
-}
-
-void NetworkMenuIcon::SetDisconnectedIconAndText() {
-  icon_->set_icon(GetDisconnectedImage(ARCS, resource_color_theme_));
-  if (mode_ == MENU_MODE) {
-    text_ = l10n_util::GetStringUTF16(
-        IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED);
-  } else {
-    text_ = l10n_util::GetStringUTF16(IDS_NETWORK_SELECTION_NONE_SELECTED);
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static functions for generating network icon images:
-
-// This defines how we assemble a network icon.
-// Currently we iterate over all the available resolutions in |icon|. This will
-// be wrong once we dynamically load image resolutions.
-// TODO(pkotwicz): Figure out what to do when a new image resolution becomes
-// available.
-const gfx::ImageSkia NetworkMenuIcon::GenerateImageFromComponents(
-    const gfx::ImageSkia& icon,
-    const gfx::ImageSkia* top_left_badge,
-    const gfx::ImageSkia* top_right_badge,
-    const gfx::ImageSkia* bottom_left_badge,
-    const gfx::ImageSkia* bottom_right_badge) {
-  return gfx::ImageSkia(new NetworkIconImageSource(icon,
-                                                   top_left_badge,
-                                                   top_right_badge,
-                                                   bottom_left_badge,
-                                                   bottom_right_badge),
-                   icon.size());
-}
-
-// We blend connecting icons with a black image to generate a faded icon.
-const gfx::ImageSkia NetworkMenuIcon::GenerateConnectingImage(
-    const gfx::ImageSkia& source) {
-  return gfx::ImageSkiaOperations::CreateBlendedImage(
-      GetEmptyImage(source.size()), source, kConnectingImageAlpha);
-}
-
-// Generates and caches an icon image for a network's current state.
-const gfx::ImageSkia NetworkMenuIcon::GetImage(const Network* network,
-                                               ResourceColorTheme color) {
-  DCHECK(network);
-  // Maintain a static (global) icon map. Note: Icons are never destroyed;
-  // it is assumed that a finite and reasonable number of network icons will be
-  // created during a session.
-
-  typedef std::map<std::string, NetworkIcon*> NetworkIconMap;
-  static NetworkIconMap* icon_map_dark = NULL;
-  static NetworkIconMap* icon_map_light = NULL;
-  if (icon_map_dark == NULL)
-    icon_map_dark = new NetworkIconMap;
-  if (icon_map_light == NULL)
-    icon_map_light = new NetworkIconMap;
-
-  NetworkIconMap* icon_map = color == COLOR_DARK ? icon_map_dark :
-                                                   icon_map_light;
-  // Find or add the icon.
-  NetworkIcon* icon;
-  NetworkIconMap::iterator iter = icon_map->find(network->service_path());
-  if (iter == icon_map->end()) {
-    icon = new NetworkIcon(network->service_path(), color);
-    icon_map->insert(std::make_pair(network->service_path(), icon));
-  } else {
-    icon = iter->second;
-  }
-  // Update and return the icon's image.
-  icon->Update();
-  return icon->GetImage();
-}
-
-const gfx::ImageSkia NetworkMenuIcon::GetImage(ImageType type,
-                                               int index,
-                                               ResourceColorTheme color) {
-  int width, height = 0;
-  gfx::ImageSkia* images = NULL;
-  if (type == NetworkMenuIcon::ARCS) {
-    if (index >= kNumArcsImages)
-      return gfx::ImageSkia();
-    images = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-        color == NetworkMenuIcon::COLOR_DARK ?
-        IDR_AURA_UBER_TRAY_NETWORK_ARCS_DARK :
-        IDR_AURA_UBER_TRAY_NETWORK_ARCS_LIGHT);
-    width = images->width();
-    height = images->height() / kNumArcsImages;
-  } else {
-    if (index >= kNumBarsImages)
-      return gfx::ImageSkia();
-
-    images = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-        color == NetworkMenuIcon::COLOR_DARK ?
-        IDR_AURA_UBER_TRAY_NETWORK_BARS_DARK :
-        IDR_AURA_UBER_TRAY_NETWORK_BARS_LIGHT);
-    width = images->width();
-    height = images->height() / kNumBarsImages;
-  }
-  return gfx::ImageSkiaOperations::ExtractSubset(*images,
-      gfx::Rect(0, index * height, width, height));
-}
-
-const gfx::ImageSkia NetworkMenuIcon::GetDisconnectedImage(
-    ImageType type,
-    ResourceColorTheme color) {
-  return GetImage(type, 0, color);
-}
-
-const gfx::ImageSkia NetworkMenuIcon::GetConnectedImage(ImageType type,
-      ResourceColorTheme color) {
-  return GetImage(type, NumImages(type) - 1, color);
-}
-
-gfx::ImageSkia* NetworkMenuIcon::GetVirtualNetworkImage() {
-  return ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-      IDR_AURA_UBER_TRAY_NETWORK_VPN);
-}
-
-int NetworkMenuIcon::NumImages(ImageType type) {
-  return (type == ARCS) ? kNumArcsImages : kNumBarsImages;
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/status/network_menu_icon.h b/chrome/browser/chromeos/status/network_menu_icon.h
deleted file mode 100644
index f0fb554..0000000
--- a/chrome/browser/chromeos/status/network_menu_icon.h
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_STATUS_NETWORK_MENU_ICON_H_
-#define CHROME_BROWSER_CHROMEOS_STATUS_NETWORK_MENU_ICON_H_
-
-// NetworkMenuIcon Manages an icon that reflects the current state of the
-// network (see chromeos::NetworkLibrary). It takes an optional Delegate
-// argument in the constructor that signals the delegate when the icon changes.
-// Example usage:
-//   class MyIconDelegate : public NetworkMenuIcon::Delegate {
-//     virtual void NetworkMenuIconChanged() OVERRIDE {
-//       string16 tooltip;
-//       const ImageSkia* image = network_icon_->GetIconAndText(&tooltip);
-//       SetIcon(*bitmap);
-//       SetTooltip(tooltip);
-//       SchedulePaint();
-//     }
-//   }
-//   MyIconDelegate my_delegate;
-//   NetworkMenuIcon icon(&my_delegate, NetworkMenuIcon::MENU_MODE);
-//
-// NetworkMenuIcon also provides static functions for fetching network images
-// (e.g. for network entries in the menu or settings).
-// Example usage:
-//   Network* network = network_library->FindNetworkByPath(my_network_path_);
-//   SetIcon(NetworkMenuIcon::GetBitmap(network);
-//
-// This class is not explicitly thread-safe and functions are expected to be
-// called from the UI thread.
-
-#include <map>
-#include <string>
-
-#include "ash/system/chromeos/network/network_icon_animation_observer.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/time/time.h"
-#include "chrome/browser/chromeos/cros/network_library.h"
-#include "ui/gfx/image/image_skia.h"
-
-namespace chromeos {
-
-class NetworkIcon;
-
-class NetworkMenuIcon : public ash::network_icon::AnimationObserver {
- public:
-  enum Mode {
-    MENU_MODE,      // Prioritizes connecting networks and sets tooltips.
-    DROPDOWN_MODE,  // Prioritizes connected networks and sets display text.
-  };
-
-  enum ResourceColorTheme {
-    COLOR_DARK,
-    COLOR_LIGHT,
-  };
-
-  // Used for calls to GetBitmap() and GetNumBitmaps() below.
-  enum ImageType {
-    ARCS = 0,
-    BARS
-  };
-
-  class Delegate {
-   public:
-    Delegate() {}
-    virtual ~Delegate() {}
-    // Called when the image has changed due to animation. The callback should
-    // trigger a call to GetIconAndText() to generate and retrieve the image.
-    virtual void NetworkMenuIconChanged() = 0;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(Delegate);
-  };
-
-  // NetworkMenuIcon is owned by the caller. |delegate| can be NULL.
-  // |mode| determines the menu behavior (see enum).
-  NetworkMenuIcon(Delegate* delegate, Mode mode);
-  virtual ~NetworkMenuIcon();
-
-  // Sets the resource color theme (e.g. light or dark icons).
-  void SetResourceColorTheme(ResourceColorTheme color);
-
-  // Returns true if the icon should be visible in a system tray.
-  bool ShouldShowIconInTray();
-
-  // Generates and returns the icon image. If |text| is not NULL, sets it to
-  // the tooltip or display text to show, based on the value of mode_.
-  const gfx::ImageSkia GetIconAndText(string16* text);
-  // Generates and returns the icon image for vpn network connection.
-  const gfx::ImageSkia GetVpnIconAndText(string16* text);
-
-  // ash::network_icon::AnimationObserver implementation.
-  virtual void NetworkIconChanged() OVERRIDE;
-
-  // Static functions for generating network icon images:
-
-  // Composites the images to generate a network icon. Input parameters are
-  // the icon and badges that are composited to generate |result|. Public
-  // primarily for unit tests.
-  static const gfx::ImageSkia GenerateImageFromComponents(
-      const gfx::ImageSkia& icon,
-      const gfx::ImageSkia* top_left_badge,
-      const gfx::ImageSkia* top_right_badge,
-      const gfx::ImageSkia* bottom_left_badge,
-      const gfx::ImageSkia* bottom_right_badge);
-
-  // Returns a modified version of |source| representing the connecting state
-  // of a network. Public for unit tests.
-  static const gfx::ImageSkia GenerateConnectingImage(
-      const gfx::ImageSkia& source);
-
-  // Returns an image associated with |network|, reflecting its current state.
-  static const gfx::ImageSkia GetImage(const Network* network,
-                                       ResourceColorTheme color);
-
-  // Access a specific image of the specified color theme. If index is out of
-  // range, an empty image will be returned.
-  static const gfx::ImageSkia GetImage(ImageType type,
-                                       int index,
-                                       ResourceColorTheme color);
-
-  // Gets the disconnected image for given type.
-  static const gfx::ImageSkia GetDisconnectedImage(ImageType type,
-                                                   ResourceColorTheme color);
-
-  // Gets the connected image for given type.
-  static const gfx::ImageSkia GetConnectedImage(ImageType type,
-                                                ResourceColorTheme color);
-
-  // Gets a network image for VPN.
-  static gfx::ImageSkia* GetVirtualNetworkImage();
-
-  // Returns total number of images for given type.
-  static int NumImages(ImageType type);
-
- protected:
-  // Starts the connection animation if necessary and returns its current value.
-  // Virtual so that unit tests can override this.
-  virtual double GetAnimation();
-
- private:
-  // Returns the index for a connecting icon.
-  int GetConnectingIndex();
-  // Returns the appropriate connecting network if any.
-  const Network* GetConnectingNetwork();
-  // Sets the icon based on the state of the network and the network library.
-  // Sets text_ to the appropriate tooltip or display text.
-  // Returns true if the icon should animate.
-  bool SetIconAndText();
-  // Sets the icon and text for VPN connection.
-  // Returns true if the icon should animate.
-  bool SetVpnIconAndText();
-  // Set the icon and text to show a warning if unable to load the cros library.
-  void SetWarningIconAndText();
-  // Sets the icon and text when displaying a connecting state.
-  void SetConnectingIconAndText(const Network* connecting_network);
-  // Sets the icon and text when connected to |network|.
-  // Returns true if the icon should animate.
-  bool SetActiveNetworkIconAndText(const Network* network);
-  // Sets the icon and text when disconnected.
-  void SetDisconnectedIconAndText();
-
-  // Specifies whether this icon is for a normal menu or a dropdown menu.
-  Mode mode_;
-  // A delegate may be specified to receive notifications when this animates.
-  Delegate* delegate_;
-  // Generated image for connecting to a VPN.
-  gfx::ImageSkia vpn_connecting_badge_;
-  ResourceColorTheme resource_color_theme_;
-  // The generated icon image.
-  scoped_ptr<NetworkIcon> icon_;
-  // A weak pointer to the currently connecting network. Used only for
-  // comparison purposes; accessing this directly may be invalid.
-  const Network* connecting_network_;
-  // Index of current connecting animation.
-  int connecting_index_;
-  // The tooltip or display text associated with the menu icon.
-  string16 text_;
-  // Timer to eliminate noise while initializing cellular.
-  base::Time initialize_state_time_;
-
-  DISALLOW_COPY_AND_ASSIGN(NetworkMenuIcon);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_STATUS_NETWORK_MENU_ICON_H_
diff --git a/chrome/browser/chromeos/status/network_menu_icon_unittest.cc b/chrome/browser/chromeos/status/network_menu_icon_unittest.cc
deleted file mode 100644
index ba8c9fe..0000000
--- a/chrome/browser/chromeos/status/network_menu_icon_unittest.cc
+++ /dev/null
@@ -1,373 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/chromeos/status/network_menu_icon.h"
-
-#include "chrome/browser/chromeos/cros/network_library.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "grit/ash_resources.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/resource/resource_bundle.h"
-
-namespace {
-
-// Compares each pixel in the 1x representation of two images for testing.
-// TODO(pkotwicz): Compare pixels of all bitmaps contained within image.
-bool CompareTwoImages(const gfx::ImageSkia& image_a,
-                      const gfx::ImageSkia& image_b,
-                      int log_level) {
-  CHECK(!image_a.isNull());
-  CHECK(!image_b.isNull());
-
-  gfx::ImageSkiaRep image_rep_a =
-      image_a.GetRepresentation(ui::SCALE_FACTOR_100P);
-  CHECK_EQ(ui::SCALE_FACTOR_100P, image_rep_a.scale_factor());
-  gfx::ImageSkiaRep image_rep_b =
-      image_b.GetRepresentation(ui::SCALE_FACTOR_100P);
-  CHECK_EQ(ui::SCALE_FACTOR_100P, image_rep_b.scale_factor());
-
-  SkBitmap a = image_rep_a.sk_bitmap();
-  SkBitmap b = image_rep_b.sk_bitmap();
-
-  CHECK(!a.empty());
-  CHECK(!b.empty());
-  if (a.getSize() != b.getSize()) {
-    VLOG(log_level) << "Mistmatched size: "
-                    << a.getSize() << " != " << b.getSize();
-    return false;
-  }
-  size_t bytes = a.getSize();
-  SkAutoLockPixels locka(a);
-  SkAutoLockPixels lockb(b);
-  const char* pixa = static_cast<const char*>(a.getPixels());
-  const char* pixb = static_cast<const char*>(b.getPixels());
-  if (!pixa || !pixb) {
-    if (!pixa)
-      VLOG(log_level) << "getPixels() returned NULL for LHS";
-    if (!pixb)
-      VLOG(log_level) << "getPixels() returned NULL for RHS";
-    return false;
-  }
-  size_t width = a.width();
-  size_t height = a.height();
-  size_t bpp = a.bytesPerPixel();
-  if (width * height * bpp != bytes) {
-    VLOG(log_level) << "Width: " << width << " x Height: " << height
-                    << " x bpp: " << bpp << " != Size: " << bytes;
-    return false;
-  }
-  for (int y=0; y<a.height(); ++y) {
-    for (int x=0; x<a.width(); ++x) {
-      for (size_t i = 0; i<bpp; ++i) {
-        if (*pixa++ != *pixb++) {
-          VLOG(log_level) << "Icon: " << width << "x" << height << "x" << bpp
-                          << ", Mismatch at: " << x << "," << y << ":" << i;
-          return false;
-        }
-      }
-    }
-  }
-  return true;
-}
-
-} // namespace
-
-namespace chromeos {
-
-class NetworkMenuIconTest : public testing::Test {
- protected:
-  NetworkMenuIconTest() : rb_(ResourceBundle::GetSharedInstance()) {}
-
-  // testing::Test implementation.
-  virtual void SetUp() OVERRIDE {
-    cros_ = NetworkLibrary::Get();
-    // Ethernet connected = WIRED icon, no badges.
-    ethernet_connected_image_ = NetworkMenuIcon::GenerateImageFromComponents(
-        *rb_.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED),
-        NULL, NULL, NULL, NULL);
-    // Wifi connected, strength = 100% = ARCS4 icon, no badges.
-    wifi_connected_100_image_ = NetworkMenuIcon::GenerateImageFromComponents(
-        NetworkMenuIcon::GetImage(
-            NetworkMenuIcon::ARCS,
-            NetworkMenuIcon::NumImages(NetworkMenuIcon::ARCS) - 1,
-            NetworkMenuIcon::COLOR_DARK),
-        NULL, NULL, NULL, NULL);
-    // Wifi connected, strength = 50%, encrypted = ARCS2 icon + SECURE badge.
-    wifi_encrypted_50_image_ = NetworkMenuIcon::GenerateImageFromComponents(
-        NetworkMenuIcon::GetImage(NetworkMenuIcon::ARCS, 3,
-                                  NetworkMenuIcon::COLOR_DARK),
-        NULL, NULL, NULL,
-        rb_.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_SECURE_DARK));
-    // Wifi connecting = IDR_AURA_UBER_TRAY_NETWORK_ARCS1 (faded).
-    wifi_connecting_image_ = NetworkMenuIcon::GenerateConnectingImage(
-        NetworkMenuIcon::GetImage(NetworkMenuIcon::ARCS, 1,
-                                  NetworkMenuIcon::COLOR_DARK));
-    // 4G connected, strength = 50% = BARS4 icon + 4G badge.
-    wimax_connected_50_image_ =
-        NetworkMenuIcon::GenerateImageFromComponents(
-            NetworkMenuIcon::GetImage(
-                NetworkMenuIcon::BARS, 3,
-                NetworkMenuIcon::COLOR_DARK),
-            rb_.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_4G_DARK),
-            NULL, NULL, NULL);
-    // 3G connected, strength = 100% = BARS4 icon + 3G badge.
-    cellular_connected_100_image_ =
-        NetworkMenuIcon::GenerateImageFromComponents(
-            NetworkMenuIcon::GetImage(
-                NetworkMenuIcon::BARS,
-                NetworkMenuIcon::NumImages(NetworkMenuIcon::BARS) - 1,
-                NetworkMenuIcon::COLOR_DARK),
-            rb_.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_3G_DARK),
-            NULL, NULL, NULL);
-    // 3G connected, strength = 50%, roaming = BARS2 icon + roaming & 3G badges.
-    cellular_roaming_50_image_ = NetworkMenuIcon::GenerateImageFromComponents(
-        NetworkMenuIcon::GetImage(NetworkMenuIcon::BARS, 3,
-                                  NetworkMenuIcon::COLOR_DARK),
-        rb_.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_3G_DARK), NULL, NULL,
-        rb_.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_ROAMING_DARK));
-    // 3G connecting = IDR_AURA_UBER_TRAY_NETWORK_BARS1 (faded).
-    cellular_connecting_image_ = NetworkMenuIcon::GenerateConnectingImage(
-        NetworkMenuIcon::GetImage(NetworkMenuIcon::BARS, 1,
-                                  NetworkMenuIcon::COLOR_DARK));
-    // Disconnected = ARCS0 icon.
-    disconnected_image_ = NetworkMenuIcon::GenerateImageFromComponents(
-        NetworkMenuIcon::GetImage(NetworkMenuIcon::ARCS, 0,
-                                  NetworkMenuIcon::COLOR_DARK),
-        NULL, NULL, NULL, NULL);
-  }
-
-  virtual void TearDown() OVERRIDE {
-  }
-
- protected:
-  void SetConnected(Network* network) {
-    Network::TestApi test_network(network);
-    test_network.SetConnected();
-    test_network.SetUserConnectState(USER_CONNECT_CONNECTED);
-  }
-
-  void SetConnecting(Network* network, bool user_initiated) {
-    Network::TestApi test_network(network);
-    test_network.SetConnecting();
-    test_network.SetUserConnectState(
-        user_initiated ? USER_CONNECT_STARTED : USER_CONNECT_NONE);
-  }
-
-  void SetDisconnected(Network* network) {
-    Network::TestApi test_network(network);
-    test_network.SetDisconnected();
-    test_network.SetUserConnectState(USER_CONNECT_NONE);
-  }
-
-  void SetActive(Network* network, bool active) {
-    if (active) {
-      cros_->SetActiveNetwork(network->type(), network->service_path());
-    } else {
-      cros_->SetActiveNetwork(network->type(), "");
-    }
-  }
-
-  void SetStrength(WirelessNetwork* network, int strength) {
-    WirelessNetwork::TestApi test_network(network);
-    test_network.SetStrength(strength);
-  }
-
-  void SetEncryption(WifiNetwork* network, ConnectionSecurity encryption) {
-    WifiNetwork::TestApi test_network(network);
-    test_network.SetEncryption(encryption);
-  }
-
-  void SetRoamingState(CellularNetwork* network, NetworkRoamingState roaming) {
-    CellularNetwork::TestApi test_network(network);
-    test_network.SetRoamingState(roaming);
-  }
-
-  bool CompareImages(const gfx::ImageSkia& icon, const gfx::ImageSkia& base) {
-    if (CompareTwoImages(icon, base, 1))
-      return true;
-    EXPECT_FALSE(CompareTwoImages(icon, ethernet_connected_image_, 2));
-    EXPECT_FALSE(CompareTwoImages(icon, wifi_connected_100_image_, 2));
-    EXPECT_FALSE(CompareTwoImages(icon, wifi_encrypted_50_image_, 2));
-    EXPECT_FALSE(CompareTwoImages(icon, wifi_connecting_image_, 2));
-    EXPECT_FALSE(CompareTwoImages(icon, cellular_connected_100_image_, 2));
-    EXPECT_FALSE(CompareTwoImages(icon, cellular_roaming_50_image_, 2));
-    EXPECT_FALSE(CompareTwoImages(icon, cellular_connecting_image_, 2));
-    EXPECT_FALSE(CompareTwoImages(icon, disconnected_image_, 2));
-    return false;
-  }
-
- protected:
-  ScopedStubNetworkLibraryEnabler stub_network_library_enabler_;
-  NetworkLibrary* cros_;
-  ResourceBundle& rb_;
-  gfx::ImageSkia ethernet_connected_image_;
-  gfx::ImageSkia wifi_connected_100_image_;
-  gfx::ImageSkia wifi_encrypted_50_image_;
-  gfx::ImageSkia wifi_connecting_image_;
-  gfx::ImageSkia wimax_connected_50_image_;
-  gfx::ImageSkia cellular_connected_100_image_;
-  gfx::ImageSkia cellular_roaming_50_image_;
-  gfx::ImageSkia cellular_connecting_image_;
-  gfx::ImageSkia disconnected_image_;
-};
-
-// Compare icon cache results against expected results fron SetUp().
-TEST_F(NetworkMenuIconTest, EthernetIcon) {
-  Network* network = cros_->FindNetworkByPath("eth1");
-  ASSERT_NE(static_cast<const Network*>(NULL), network);
-  SetConnected(network);
-  gfx::ImageSkia icon = NetworkMenuIcon::GetImage(network,
-                                                  NetworkMenuIcon::COLOR_DARK);
-  EXPECT_TRUE(CompareImages(icon, ethernet_connected_image_));
-}
-
-TEST_F(NetworkMenuIconTest, WifiIcon) {
-  WifiNetwork* network = cros_->FindWifiNetworkByPath("wifi1");
-  ASSERT_NE(static_cast<const Network*>(NULL), network);
-  gfx::ImageSkia icon = NetworkMenuIcon::GetImage(network,
-                                                  NetworkMenuIcon::COLOR_DARK);
-  EXPECT_TRUE(CompareImages(icon, wifi_connected_100_image_));
-
-  SetStrength(network, 50);
-  SetEncryption(network, SECURITY_RSN);
-  icon = NetworkMenuIcon::GetImage(network,
-                                   NetworkMenuIcon::COLOR_DARK);
-  EXPECT_TRUE(CompareImages(icon, wifi_encrypted_50_image_));
-}
-
-TEST_F(NetworkMenuIconTest, CellularIcon) {
-  CellularNetwork* network = cros_->FindCellularNetworkByPath("cellular1");
-  ASSERT_NE(static_cast<const Network*>(NULL), network);
-  SetConnected(network);
-  SetStrength(network, 100);
-  SetRoamingState(network, ROAMING_STATE_HOME);
-  gfx::ImageSkia icon = NetworkMenuIcon::GetImage(network,
-                                                  NetworkMenuIcon::COLOR_DARK);
-  EXPECT_TRUE(CompareImages(icon, cellular_connected_100_image_));
-
-  SetStrength(network, 50);
-  SetRoamingState(network, ROAMING_STATE_ROAMING);
-  icon = NetworkMenuIcon::GetImage(network,
-                                   NetworkMenuIcon::COLOR_DARK);
-  EXPECT_TRUE(CompareImages(icon, cellular_roaming_50_image_));
-}
-
-namespace {
-
-class TestNetworkMenuIcon : public NetworkMenuIcon {
- public:
-  explicit TestNetworkMenuIcon(Mode mode)
-      : NetworkMenuIcon(&delegate_, mode),
-        animation_(0.0) {
-  }
-  virtual ~TestNetworkMenuIcon() {}
-
-  // NetworkMenuIcon override.
-  virtual double GetAnimation() OVERRIDE { return animation_; }
-
-  void set_animation(double animation) { animation_ = animation; }
-
- private:
-  class Delegate : public NetworkMenuIcon::Delegate {
-   public:
-    Delegate() : changed_(0) {}
-    virtual void NetworkMenuIconChanged() OVERRIDE {
-      ++changed_;
-    }
-    int changed() const { return changed_; }
-   private:
-    int changed_;
-  };
-  Delegate delegate_;
-  double animation_;
-};
-
-}  // namespace
-
-// Test Network Menu status icon logic.
-
-// Default relevent stub state:
-//  eth1: connected (active ethernet)
-//  wifi1: connected (active wifi)
-//  cellular1: connected  (active cellular)
-//  wimax2: connected  (active wimax)
-// See network_library_unit_test.cc for more info.
-
-TEST_F(NetworkMenuIconTest, StatusIconMenuMode) {
-  TestNetworkMenuIcon menu_icon(NetworkMenuIcon::MENU_MODE);
-  gfx::ImageSkia icon;
-
-  // Set cellular1 to connecting.
-  CellularNetwork* cellular1 = cros_->FindCellularNetworkByPath("cellular1");
-  ASSERT_NE(static_cast<const Network*>(NULL), cellular1);
-  SetRoamingState(cellular1, ROAMING_STATE_HOME);  // Clear romaing state
-  SetConnecting(cellular1, true);
-
-  // For user initiated connect always display the connecting icon (cellular1).
-  icon = menu_icon.GetIconAndText(NULL);
-  EXPECT_TRUE(CompareImages(icon, cellular_connecting_image_));
-
-  // Set cellular1 to connected; ethernet icon should be shown.
-  SetConnected(cellular1);
-  icon = menu_icon.GetIconAndText(NULL);
-  EXPECT_TRUE(CompareImages(icon, ethernet_connected_image_));
-
-  // Set ethernet to inactive/disconnected; wifi icon should be shown.
-  Network* eth1 = cros_->FindNetworkByPath("eth1");
-  ASSERT_NE(static_cast<const Network*>(NULL), eth1);
-  SetActive(eth1, false);
-  SetDisconnected(eth1);
-  icon = menu_icon.GetIconAndText(NULL);
-  EXPECT_TRUE(CompareImages(icon, wifi_connected_100_image_));
-
-  // Set all networks to disconnected; disconnected icon should be shown.
-  SetActive(cellular1, false);
-  SetDisconnected(cellular1);
-  WifiNetwork* wifi1 = cros_->FindWifiNetworkByPath("wifi1");
-  SetActive(wifi1, false);
-  SetDisconnected(wifi1);
-  WimaxNetwork* wimax2 = cros_->FindWimaxNetworkByPath("wimax2");
-  SetActive(wimax2, false);
-  SetDisconnected(wimax2);
-  icon = menu_icon.GetIconAndText(NULL);
-  EXPECT_TRUE(CompareImages(icon, disconnected_image_));
-}
-
-TEST_F(NetworkMenuIconTest, StatusIconDropdownMode) {
-  TestNetworkMenuIcon menu_icon(NetworkMenuIcon::DROPDOWN_MODE);
-  gfx::ImageSkia icon;
-
-  // Set wifi1 to connecting.
-  WifiNetwork* wifi1 = cros_->FindWifiNetworkByPath("wifi1");
-  ASSERT_NE(static_cast<const Network*>(NULL), wifi1);
-  SetConnecting(wifi1, false);
-
-  // For non user-initiated connect, show the connected network (ethernet).
-  icon = menu_icon.GetIconAndText(NULL);
-  EXPECT_TRUE(CompareImages(icon, ethernet_connected_image_));
-
-  // Set ethernet to inactive/disconnected.
-  Network* ethernet = cros_->FindNetworkByPath("eth1");
-  ASSERT_NE(static_cast<const Network*>(NULL), ethernet);
-  SetActive(ethernet, false);
-  SetDisconnected(ethernet);
-
-  // Icon should now be cellular connected icon.
-  icon = menu_icon.GetIconAndText(NULL);
-  EXPECT_TRUE(CompareImages(icon, cellular_connected_100_image_));
-
-  // Set cellular1 to disconnected; Icon should now be wimax icon.
-  CellularNetwork* cellular1 = cros_->FindCellularNetworkByPath("cellular1");
-  ASSERT_NE(static_cast<const Network*>(NULL), cellular1);
-  SetDisconnected(cellular1);
-  icon = menu_icon.GetIconAndText(NULL);
-  EXPECT_TRUE(CompareImages(icon, wimax_connected_50_image_));
-
-  // Set wifi1 to connected. Icon should now be wifi connected icon.
-  SetConnected(wifi1);
-  icon = menu_icon.GetIconAndText(NULL);
-  EXPECT_TRUE(CompareImages(icon, wifi_connected_100_image_));
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/system/ash_system_tray_delegate.cc b/chrome/browser/chromeos/system/ash_system_tray_delegate.cc
index 96f6f9d..1580e0c 100644
--- a/chrome/browser/chromeos/system/ash_system_tray_delegate.cc
+++ b/chrome/browser/chromeos/system/ash_system_tray_delegate.cc
@@ -18,7 +18,6 @@
 #include "ash/shell_window_ids.h"
 #include "ash/system/bluetooth/bluetooth_observer.h"
 #include "ash/system/brightness/brightness_observer.h"
-#include "ash/system/chromeos/audio/audio_observer.h"
 #include "ash/system/chromeos/network/network_observer.h"
 #include "ash/system/chromeos/network/network_tray_delegate.h"
 #include "ash/system/date/clock_observer.h"
@@ -48,7 +47,6 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
-#include "chrome/browser/chromeos/audio/audio_handler.h"
 #include "chrome/browser/chromeos/bluetooth/bluetooth_pairing_dialog.h"
 #include "chrome/browser/chromeos/choose_mobile_network_dialog.h"
 #include "chrome/browser/chromeos/cros/network_library.h"
@@ -230,7 +228,6 @@
 }
 
 class SystemTrayDelegate : public ash::SystemTrayDelegate,
-                           public AudioHandler::VolumeObserver,
                            public PowerManagerClient::Observer,
                            public SessionManagerClient::Observer,
                            public NetworkLibrary::NetworkManagerObserver,
@@ -293,9 +290,6 @@
   }
 
   virtual void Initialize() OVERRIDE {
-    if (!ash::switches::UseNewAudioHandler()) {
-      AudioHandler::GetInstance()->AddVolumeObserver(this);
-    }
     DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
     DBusThreadManager::Get()->GetSessionManagerClient()->AddObserver(this);
 
@@ -362,10 +356,6 @@
     // Unregister content notifications befure destroying any components.
     registrar_.reset();
 
-    if (!ash::switches::UseNewAudioHandler() && AudioHandler::GetInstance()) {
-      AudioHandler::GetInstance()->RemoveVolumeObserver(this);
-    }
-
     DBusThreadManager::Get()->GetSessionManagerClient()->RemoveObserver(this);
     DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
     DBusThreadManager::Get()->GetSystemClockClient()->RemoveObserver(this);
@@ -1023,17 +1013,6 @@
     GetSystemTrayNotifier()->NotifySessionLengthLimitChanged();
   }
 
-  // Overridden from AudioHandler::VolumeObserver.
-  virtual void OnVolumeChanged() OVERRIDE {
-    float level = AudioHandler::GetInstance()->GetVolumePercent() / 100.f;
-    GetSystemTrayNotifier()->NotifyVolumeChanged(level);
-  }
-
-  // Overridden from AudioHandler::VolumeObserver.
-  virtual void OnMuteToggled() OVERRIDE {
-    GetSystemTrayNotifier()->NotifyMuteToggled();
-  }
-
   // Overridden from PowerManagerClient::Observer.
   virtual void BrightnessChanged(int level, bool user_initiated) OVERRIDE {
     double leveld = static_cast<double>(level);
diff --git a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc
index feb1d8f..3ffc598 100644
--- a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc
@@ -125,24 +125,27 @@
     const KeyValueMap& user_log_files) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   if (succeeded) {
+    SystemLogsResponse* response = new SystemLogsResponse;
+    std::vector<Profile*> last_used = ProfileManager::GetLastOpenedProfiles();
     content::BrowserThread::PostBlockingPoolTaskAndReply(
         FROM_HERE,
-        base::Bind(
-            &DebugDaemonLogSource::ReadUserLogFiles,
-            weak_ptr_factory_.GetWeakPtr(),
-            user_log_files),
-        base::Bind(&DebugDaemonLogSource::RequestCompleted,
-                   weak_ptr_factory_.GetWeakPtr()));
+        base::Bind(&DebugDaemonLogSource::ReadUserLogFiles,
+                   user_log_files, last_used, response),
+        base::Bind(&DebugDaemonLogSource::MergeResponse,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   base::Owned(response)));
   } else {
     (*response_)[kUserLogFileKeyName] = kNotAvailable;
     RequestCompleted();
   }
 }
 
-void DebugDaemonLogSource::ReadUserLogFiles(const KeyValueMap& user_log_files) {
-  std::vector<Profile*> last_used = ProfileManager::GetLastOpenedProfiles();
-
-  for (size_t i = 0; i < last_used.size(); ++i) {
+// static
+void DebugDaemonLogSource::ReadUserLogFiles(
+    const KeyValueMap& user_log_files,
+    const std::vector<Profile*>& last_used_profiles,
+    SystemLogsResponse* response) {
+  for (size_t i = 0; i < last_used_profiles.size(); ++i) {
     std::string profile_prefix = "Profile[" + base::UintToString(i) + "] ";
     for (KeyValueMap::const_iterator it = user_log_files.begin();
          it != user_log_files.end();
@@ -150,18 +153,25 @@
       std::string key = it->first;
       std::string value;
       std::string filename = it->second;
-      base::FilePath profile_dir = last_used[i]->GetPath();
+      base::FilePath profile_dir = last_used_profiles[i]->GetPath();
       bool read_success = file_util::ReadFileToString(
           profile_dir.Append(filename), &value);
 
       if (read_success && !value.empty())
-        (*response_)[profile_prefix + key] = value;
+        (*response)[profile_prefix + key] = value;
       else
-        (*response_)[profile_prefix + filename] = kNotAvailable;
+        (*response)[profile_prefix + filename] = kNotAvailable;
     }
   }
 }
 
+void DebugDaemonLogSource::MergeResponse(SystemLogsResponse* response) {
+  for (SystemLogsResponse::const_iterator it = response->begin();
+       it != response->end(); ++it)
+    response_->insert(*it);
+  RequestCompleted();
+}
+
 void DebugDaemonLogSource::RequestCompleted() {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   DCHECK(!callback_.is_null());
diff --git a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.h b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.h
index 8d1626c..4a21357 100644
--- a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.h
+++ b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.h
@@ -12,6 +12,8 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/system_logs/system_logs_fetcher.h"
 
+class Profile;
+
 namespace chromeos {
 
 // Gathers log data from Debug Daemon.
@@ -40,8 +42,15 @@
                          const KeyValueMap& logs);
 
   // Read the contents of the specified user logs files and adds it to
-  // the response.
-  void ReadUserLogFiles(const KeyValueMap& user_log_files);
+  // the response parameter.
+  static void ReadUserLogFiles(
+      const KeyValueMap& user_log_files,
+      const std::vector<Profile*>& last_used_profiles,
+      SystemLogsResponse* response);
+
+  // Merge the responses from ReadUserLogFiles into the main response dict and
+  // call RequestComplete to indicate that the user log files read is complete.
+  void MergeResponse(SystemLogsResponse* response);
 
   // Sends the data to the callback_ when all the requests are completed
   void RequestCompleted();
diff --git a/chrome/browser/chromeos/ui/app_launch_view.cc b/chrome/browser/chromeos/ui/app_launch_view.cc
index f5354f3..9b8da56 100644
--- a/chrome/browser/chromeos/ui/app_launch_view.cc
+++ b/chrome/browser/chromeos/ui/app_launch_view.cc
@@ -78,7 +78,7 @@
 // static
 void AppLaunchView::CloseAppLaunchSplashScreen() {
   if (g_instance) {
-    g_instance->Close();
+    g_instance->GetWidget()->Close();
     g_instance = NULL;
   }
 }
@@ -120,11 +120,6 @@
   ShowWindow();
 }
 
-void AppLaunchView::Close() {
-  DCHECK(GetWidget());
-  GetWidget()->Close();
-}
-
 void AppLaunchView::AddChildWebContents() {
   content::BrowserContext* context =
       ash::Shell::GetInstance()->delegate()->GetCurrentBrowserContext();
diff --git a/chrome/browser/chromeos/ui/app_launch_view.h b/chrome/browser/chromeos/ui/app_launch_view.h
index 838fd7e..f4c4382 100644
--- a/chrome/browser/chromeos/ui/app_launch_view.h
+++ b/chrome/browser/chromeos/ui/app_launch_view.h
@@ -57,7 +57,6 @@
   virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
 
   void Show();
-  void Close();
 
   // Updates UI state of the app launch splash screen.
   void UpdateState(AppLaunchState state);
diff --git a/chrome/browser/chromeos/ui/idle_logout_dialog_view.cc b/chrome/browser/chromeos/ui/idle_logout_dialog_view.cc
index 63b9c7e..acbc569 100644
--- a/chrome/browser/chromeos/ui/idle_logout_dialog_view.cc
+++ b/chrome/browser/chromeos/ui/idle_logout_dialog_view.cc
@@ -76,7 +76,7 @@
 // static
 void IdleLogoutDialogView::CloseDialog() {
   if (g_instance)
-    g_instance->Close();
+    g_instance->GetWidget()->Close();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -93,6 +93,17 @@
   return l10n_util::GetStringUTF16(IDS_IDLE_LOGOUT_TITLE);
 }
 
+bool IdleLogoutDialogView::Close() {
+  if (timer_.IsRunning())
+    timer_.Stop();
+
+  // We just closed our dialog. The global
+  // instance is invalid now, set it to null.
+  g_instance = NULL;
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // IdleLogoutDialog private methods
 IdleLogoutDialogView::IdleLogoutDialogView()
@@ -161,18 +172,6 @@
                &IdleLogoutDialogView::UpdateCountdown);
 }
 
-void IdleLogoutDialogView::Close() {
-  DCHECK(GetWidget());
-
-  if (timer_.IsRunning())
-    timer_.Stop();
-  GetWidget()->Close();
-
-  // We just closed our dialog. The global
-  // instance is invalid now, set it to null.
-  g_instance = NULL;
-}
-
 void IdleLogoutDialogView::UpdateCountdown() {
   base::TimeDelta logout_warning_time = countdown_end_time_ -
                                         base::Time::Now();
diff --git a/chrome/browser/chromeos/ui/idle_logout_dialog_view.h b/chrome/browser/chromeos/ui/idle_logout_dialog_view.h
index f96cd64..7ef0ea5 100644
--- a/chrome/browser/chromeos/ui/idle_logout_dialog_view.h
+++ b/chrome/browser/chromeos/ui/idle_logout_dialog_view.h
@@ -47,6 +47,7 @@
   virtual int GetDialogButtons() const OVERRIDE;
   virtual ui::ModalType GetModalType() const OVERRIDE;
   virtual string16 GetWindowTitle() const OVERRIDE;
+  virtual bool Close() OVERRIDE;
 
  private:
   friend class MockIdleLogoutSettingsProvider;
@@ -62,7 +63,6 @@
   void InitAndShow();
 
   void Show();
-  void Close();
 
   void UpdateCountdown();
 
diff --git a/chrome/browser/chromeos/ui/idle_logout_dialog_view_browsertest.cc b/chrome/browser/chromeos/ui/idle_logout_dialog_view_browsertest.cc
index 7ff86e8..ff5fac3 100644
--- a/chrome/browser/chromeos/ui/idle_logout_dialog_view_browsertest.cc
+++ b/chrome/browser/chromeos/ui/idle_logout_dialog_view_browsertest.cc
@@ -9,6 +9,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/widget/widget.h"
 
 namespace chromeos {
 
@@ -26,7 +27,7 @@
   }
 
   virtual void LogoutCurrentUser(IdleLogoutDialogView* dialog) OVERRIDE {
-    dialog->Close();
+    dialog->GetWidget()->Close();
   }
 
 private:
@@ -77,7 +78,7 @@
   IdleLogoutDialogView::ShowDialog();
   EXPECT_NO_FATAL_FAILURE(ExpectOpenDialog());
 
-  IdleLogoutDialogView::current_instance()->Close();
+  IdleLogoutDialogView::CloseDialog();
   content::RunAllPendingInMessageLoop();
   ExpectClosedDialog();
 }
@@ -86,7 +87,7 @@
   IdleLogoutDialogView::ShowDialog();
   EXPECT_NO_FATAL_FAILURE(ExpectOpenDialog());
 
-  IdleLogoutDialogView::current_instance()->Close();
+  IdleLogoutDialogView::CloseDialog();
   content::RunAllPendingInMessageLoop();
   IdleLogoutDialogView::CloseDialog();
 
diff --git a/chrome/browser/chromeos/view_ids.h b/chrome/browser/chromeos/view_ids.h
deleted file mode 100644
index a09d1af..0000000
--- a/chrome/browser/chromeos/view_ids.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_VIEW_IDS_H_
-#define CHROME_BROWSER_CHROMEOS_VIEW_IDS_H_
-
-#include "chrome/browser/ui/view_ids.h"
-
-// View ID used in ChromeOS.
-enum ChromeOSViewIds {
-  // Start with the offset that is big enough to avoid possible
-  // collision.
-  VIEW_ID_STATUS_AREA = VIEW_ID_PREDEFINED_COUNT + 10000,
-  VIEW_ID_LAYOUT_MODE_BUTTON,
-
-  VIEW_ID_STATUS_BUTTON_ACCESSIBILITY,
-  VIEW_ID_STATUS_BUTTON_CAPS_LOCK,
-  VIEW_ID_STATUS_BUTTON_CLOCK,
-  VIEW_ID_STATUS_BUTTON_INPUT_METHOD,
-  VIEW_ID_STATUS_BUTTON_MEMORY,
-  VIEW_ID_STATUS_BUTTON_NETWORK_MENU,
-  VIEW_ID_STATUS_BUTTON_POWER,
-  VIEW_ID_STATUS_BUTTON_VOLUME,
-};
-
-#endif  // CHROME_BROWSER_CHROMEOS_VIEW_IDS_H_
diff --git a/chrome/browser/chromeos/web_socket_proxy.cc b/chrome/browser/chromeos/web_socket_proxy.cc
deleted file mode 100644
index b1465c2..0000000
--- a/chrome/browser/chromeos/web_socket_proxy.cc
+++ /dev/null
@@ -1,1918 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/chromeos/web_socket_proxy.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <algorithm>
-#include <limits>
-#include <list>
-#include <map>
-#include <vector>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <signal.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include "base/base64.h"
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/sequenced_task_runner_helpers.h"
-#include "base/sha1.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/sys_byteorder.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/web_socket_proxy_helper.h"
-#include "chrome/browser/internal_auth.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/common/url_constants.h"
-#include "extensions/common/constants.h"
-#include "net/base/address_list.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/cert/cert_verifier.h"
-#include "net/http/transport_security_state.h"
-#include "net/socket/client_socket_factory.h"
-#include "net/socket/client_socket_handle.h"
-#include "net/socket/ssl_client_socket.h"
-#include "net/socket/stream_socket.h"
-#include "net/ssl/ssl_config_service.h"
-#include "third_party/libevent/evdns.h"
-#include "third_party/libevent/event.h"
-#include "url/gurl.h"
-#include "url/url_parse.h"
-
-using content::BrowserThread;
-
-namespace chromeos {
-
-namespace {
-
-const uint8 kCRLF[] = "\r\n";
-const uint8 kCRLFCRLF[] = "\r\n\r\n";
-
-// Not a constant but preprocessor definition for easy concatenation.
-#define kProxyPath "/tcpproxy"
-
-enum WebSocketStatusCode {
-  WS_CLOSE_NORMAL = 1000,
-  WS_CLOSE_GOING_AWAY = 1001,
-  WS_CLOSE_PROTOCOL_ERROR = 1002,
-  WS_CLOSE_UNACCEPTABLE_DATA = 1003,
-
-  WS_CLOSE_DESTINATION_ERROR = 4000,
-  WS_CLOSE_LIMIT_VIOLATION = 4001,
-  WS_CLOSE_RESOLUTION_FAILED = 4002,
-  WS_CLOSE_UNEXPECTED = 4003
-};
-
-enum WebSocketFrameOpcode {
-  WS_OPCODE_TEXT = 1,
-  WS_OPCODE_CLOSE = 8
-};
-
-// Fixed-size (one-byte) messages communicated via control pipe.
-const char kControlMessageShutdown[] = { '.' };
-const char kControlMessageNetworkChange[] = { ':' };
-
-// Returns true on success.
-bool SetNonBlock(int fd) {
-  int flags = fcntl(fd, F_GETFL, 0);
-  return flags >= 0 && fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0;
-}
-
-// Returns true on success.
-bool IgnoreSigPipe() {
-  struct sigaction sa;
-  sa.sa_handler = SIG_IGN;
-  sa.sa_flags = 0;
-  if (sigemptyset(&sa.sa_mask) || sigaction(SIGPIPE, &sa, 0)) {
-    LOG(ERROR) << "WebSocketProxy: Failed to disable sigpipe";
-    return false;
-  }
-  return true;
-}
-
-uint64 ReadNetworkInteger(uint8* buf, int num_bytes) {
-  uint64 rv = 0;
-  DCHECK_GE(num_bytes, 0);
-  DCHECK_LE(num_bytes, 8);
-  while (num_bytes--) {
-    rv <<= 8;
-    rv += *buf++;
-  }
-  return rv;
-}
-
-void WriteNetworkInteger(int64 n, uint8* buf, int num_bytes) {
-  DCHECK_GE(num_bytes, 0);
-  DCHECK_LE(num_bytes, 8);
-  while (num_bytes--) {
-    buf[num_bytes] = n % (1 << 8);
-    n >>= 8;
-  }
-}
-
-typedef uint8 (*AsciiFilter)(uint8);
-
-uint8 AsciiFilterVerbatim(uint8 c) {
-  return c;
-}
-
-uint8 AsciiFilterLower(uint8 c) {
-  return base::ToLowerASCII(c);
-}
-
-std::string FetchAsciiSnippet(uint8* begin, uint8* end, AsciiFilter filter) {
-  std::string rv;
-  for (; begin < end; ++begin) {
-    if (!isascii(*begin))
-      return rv;
-    rv += filter(*begin);
-  }
-  return rv;
-}
-
-std::string FetchExtensionIdFromOrigin(const std::string &origin) {
-  GURL url(origin);
-  if (url.SchemeIs(extensions::kExtensionScheme))
-    return url.host();
-  else
-    return std::string();
-}
-
-inline size_t strlen(const void* s) {
-  return ::strlen(static_cast<const char*>(s));
-}
-
-void SendNotification(int port) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_WEB_SOCKET_PROXY_STARTED,
-      content::NotificationService::AllSources(), content::Details<int>(&port));
-}
-
-class Conn;
-
-// Websocket to TCP proxy server.
-class Serv {
- public:
-  Serv();
-  ~Serv();
-
-  // Do not call it twice.
-  void Run();
-
-  // Terminates running server (should be called on a different thread).
-  void Shutdown();
-  // Called on network change.  Reinitializes DNS resolution service.
-  void OnNetworkChange();
-
-  void ZapConn(Conn*);
-  void MarkConnImportance(Conn*, bool important);
-  Conn* GetFreshConn();
-  bool IsConnSane(Conn*);
-  void CloseAll();
-
-  static void OnConnect(int listening_sock, short event, void*);
-  static void OnControlRequest(int fd, short event, void*);
-
-  struct event_base* evbase() { return evbase_; }
-
-  // Libevent base.
-  struct event_base* evbase_;
-
-  // Socket to listen incoming websocket connections.
-  int listening_sock_;
-
-  // TODO(dilmah): remove this extra socket as soon as obsolete
-  // getPassportForTCP function is removed from webSocketProxyPrivate API.
-  // Additional socket to listen incoming connections on fixed port 10101.
-  int extra_listening_sock_;
-
-  // Used to communicate control requests: either shutdown request or network
-  // change notification.
-  int control_descriptor_[2];
-
-  // Flag whether shutdown has been requested.
-  bool shutdown_requested_;
-
-  // List of pending connections; We are trying to keep size of this list
-  // below kConnPoolLimit in LRU fashion.
-  typedef std::list<Conn*> ConnPool;
-  ConnPool conn_pool_;
-
-  // Reverse map to look up a connection in a conn_pool.
-  typedef std::map<Conn*, ConnPool::iterator> RevMap;
-  RevMap rev_map_;
-
-  scoped_ptr<struct event> connection_event_;
-  scoped_ptr<struct event> control_event_;
-  // TODO(dilmah): remove this extra event as soon as obsolete
-  // getPassportForTCP function is removed from webSocketProxyPrivate API.
-  scoped_ptr<struct event> extra_connection_event_;
-
-  DISALLOW_COPY_AND_ASSIGN(Serv);
-};
-
-// Connection (amalgamates both channels between proxy and javascript and
-// between proxy and destination).
-class Conn {
- public:
-  enum Phase {
-    // Initial stage of connection.
-    PHASE_WAIT_HANDSHAKE,
-    PHASE_WAIT_DESTFRAME,
-    PHASE_WAIT_DESTCONNECT,
-
-    // Operational stage of connection.
-    PHASE_OUTSIDE_FRAME,
-    PHASE_INSIDE_FRAME_BASE64,
-    PHASE_INSIDE_FRAME_SKIP,
-
-    // Terminal stage of connection.
-    PHASE_SHUT,    // Closing handshake was emitted, buffers may be pending.
-    PHASE_DEFUNCT  // Connection was nuked.
-  };
-
-  // Channel structure (either proxy<->browser or proxy<->destination).
-  class Chan {
-   public:
-    explicit Chan(Conn* master)
-        : master_(master),
-          write_pending_(false),
-          read_bev_(NULL),
-          write_bev_(NULL),
-          read_fd_(-1),
-          write_fd_(-1) {
-    }
-
-    ~Chan() {
-      Zap();
-    }
-
-    // Returns true on success.
-    bool Write(const void* data, size_t size) {
-      if (write_bev_ == NULL)
-        return false;
-      write_pending_ = true;
-      return (0 == bufferevent_write(write_bev_, data, size));
-    }
-
-    void Zap() {
-      if (read_bev_) {
-        bufferevent_disable(read_bev_, EV_READ);
-        bufferevent_free(read_bev_);
-      }
-      if (write_bev_ && write_bev_ != read_bev_) {
-        bufferevent_disable(write_bev_, EV_READ);
-        bufferevent_free(write_bev_);
-      }
-      read_bev_ = NULL;
-      write_bev_ = NULL;
-      if (write_fd_ && read_fd_ == write_fd_)
-        shutdown(write_fd_, SHUT_RDWR);
-      if (write_fd_ >= 0) {
-        close(write_fd_);
-        DCHECK_GE(read_fd_, 0);
-      }
-      if (read_fd_ && read_fd_ != write_fd_)
-        close(read_fd_);
-      read_fd_ = -1;
-      write_fd_ = -1;
-      write_pending_ = false;
-      master_->ConsiderSuicide();
-    }
-
-    void Shut() {
-      if (!write_pending_)
-        Zap();
-    }
-
-    int read_fd() const { return read_fd_; }
-    void set_read_fd(int fd) { read_fd_ = fd; }
-    int write_fd() const { return write_fd_; }
-    void set_write_fd(int fd) { write_fd_ = fd; }
-    bool write_pending() const { return write_pending_; }
-    void set_write_pending(bool pending) { write_pending_ = pending; }
-    struct bufferevent* read_bev() const { return read_bev_; }
-    void set_read_bev(struct bufferevent* bev) { read_bev_ = bev; }
-    struct bufferevent* write_bev() const { return write_bev_; }
-    void set_write_bev(struct bufferevent* bev) { write_bev_ = bev; }
-
-   private:
-    Conn* master_;
-    bool write_pending_;  // Whether write buffer is not flushed yet.
-    struct bufferevent* read_bev_;
-    struct bufferevent* write_bev_;
-    // UNIX descriptors.
-    int read_fd_;
-    int write_fd_;
-
-    DISALLOW_COPY_AND_ASSIGN(Chan);
-  };
-
-  // Status of processing incoming data.
-  enum Status {
-    STATUS_OK,
-    STATUS_INCOMPLETE,  // Not all required data is present in buffer yet.
-    STATUS_SKIP,
-    STATUS_ABORT        // Data is invalid.  We must shut connection.
-  };
-
-  // Unfortunately evdns callbacks are uncancellable,
-  // so potentially we can receive callback for a deleted Conn.
-  // Even worse, storage of deleted Conn may be reused
-  // for a new connection and new connection can receive callback
-  // destined for deleted Conn.
-  // EventKey is introduced in order to prevent that.
-  typedef void* EventKey;
-  typedef std::map<EventKey, Conn*> EventKeyMap;
-
-  explicit Conn(Serv* master);
-  ~Conn();
-
-  static Conn* Get(EventKey evkey);
-
-  void Shut(int status, const void* reason);
-
-  void ConsiderSuicide();
-
-  Status ConsumeHeader(struct evbuffer*);
-  Status ConsumeDestframe(struct evbuffer*);
-  Status ConsumeFrameHeader(struct evbuffer*);
-  Status ProcessFrameData(struct evbuffer*);
-
-  // Return true on success.
-  bool EmitHandshake(Chan*);
-  bool EmitFrame(
-      Chan*, WebSocketFrameOpcode opcode, const void* buf, size_t size);
-
-  // Attempts to establish second connection (to remote TCP service).
-  // Returns true on success.
-  bool TryConnectDest(const struct sockaddr*, socklen_t);
-
-  // Return security origin associated with this connection.
-  const std::string& GetOrigin();
-
-  // Used as libevent callbacks.
-  static void OnDestConnectTimeout(int, short, EventKey);
-  static void OnPrimchanRead(struct bufferevent*, EventKey);
-  static void OnPrimchanWrite(struct bufferevent*, EventKey);
-  static void OnPrimchanError(struct bufferevent*, short what, EventKey);
-  static void OnDestResolutionIPv4(int result, char type, int count,
-                                   int ttl, void* addr_list, EventKey);
-  static void OnDestResolutionIPv6(int result, char type, int count,
-                                   int ttl, void* addr_list, EventKey);
-  static void OnDestchanRead(struct bufferevent*, EventKey);
-  static void OnDestchanWrite(struct bufferevent*, EventKey);
-  static void OnDestchanError(struct bufferevent*, short what, EventKey);
-
-  Chan& primchan() { return primchan_; }
-  EventKey evkey() const { return evkey_; }
-
- private:
-  Serv* master_;
-  Phase phase_;
-  uint64 frame_bytes_remaining_;
-  uint8 frame_mask_[4];
-  int frame_mask_index_;
-
-  // We maintain two channels per Conn:
-  // primary channel is websocket connection.
-  Chan primchan_;
-  // Destination channel is a proxied connection.
-  Chan destchan_;
-
-  EventKey evkey_;
-
-  // Header fields supplied by client at initial websocket handshake.
-  std::map<std::string, std::string> header_fields_;
-
-  // Parameters requested via query component of GET resource.
-  std::map<std::string, std::string> requested_parameters_;
-
-  // Hostname and port of destination socket.
-  // Websocket client supplies them in first data frame (destframe).
-  std::string destname_;
-  int destport_;
-
-  // Preresolved |destname_| (empty if not pre-resolved).
-  std::string destaddr_;
-
-  // Whether TLS over TCP requested.
-  bool do_tls_;
-
-  // We try to DNS resolve hostname in both IPv4 and IPv6 domains.
-  // Track resolution failures here.
-  bool destresolution_ipv4_failed_;
-  bool destresolution_ipv6_failed_;
-
-  // Used to schedule a timeout for initial phase of connection.
-  scoped_ptr<struct event> destconnect_timeout_event_;
-
-  static base::LazyInstance<EventKeyMap>::Leaky evkey_map_;
-  static EventKey last_evkey_;
-
-  DISALLOW_COPY_AND_ASSIGN(Conn);
-};
-
-class SSLChan : public base::MessageLoopForIO::Watcher {
- public:
-  static void Start(const net::AddressList& address_list,
-                    const net::HostPortPair& host_port_pair,
-                    int read_pipe,
-                    int write_pipe) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    SSLChan* ALLOW_UNUSED chan = new SSLChan(
-        address_list, host_port_pair, read_pipe, write_pipe);
-  }
-
- private:
-  enum Phase {
-    PHASE_CONNECTING,
-    PHASE_RUNNING,
-    PHASE_CLOSING,
-    PHASE_CLOSED
-  };
-
-  class DerivedIOBufferWithSize : public net::IOBufferWithSize {
-   public:
-    DerivedIOBufferWithSize(net::IOBuffer* host, int size)
-        : IOBufferWithSize(host->data(), size), host_(host) {
-      DCHECK(host_.get());
-      DCHECK(host_->data());
-    }
-
-   protected:
-    virtual ~DerivedIOBufferWithSize() {
-      data_ = NULL;  // We do not own memory, bypass base class destructor.
-    }
-
-    scoped_refptr<net::IOBuffer> host_;
-  };
-
-  // Provides queue of data represented as IOBuffers.
-  class IOBufferQueue {
-   public:
-    // We do not allocate all capacity at once but lazily in |buf_size_| chunks.
-    explicit IOBufferQueue(int capacity)
-        : buf_size_(1 + capacity / kNumBuffersLimit) {
-    }
-
-    // Obtains IOBuffer to add new data to back.
-    net::IOBufferWithSize* GetIOBufferToFill() {
-      if (back_.get() == NULL) {
-        if (storage_.size() >= kNumBuffersLimit)
-          return NULL;
-        storage_.push_back(new net::IOBufferWithSize(buf_size_));
-        back_ = new net::DrainableIOBuffer(storage_.back().get(), buf_size_);
-      }
-      return new DerivedIOBufferWithSize(
-          back_.get(), back_->BytesRemaining());
-    }
-
-    // Obtains IOBuffer with some data from front.
-    net::IOBufferWithSize* GetIOBufferToProcess() {
-      if (front_.get() == NULL) {
-        if (storage_.empty())
-          return NULL;
-        front_ = new net::DrainableIOBuffer(storage_.front().get(), buf_size_);
-      }
-      int front_capacity =
-          (storage_.size() == 1 && back_.get()) ? back_->BytesConsumed()
-                                                : buf_size_;
-      return new DerivedIOBufferWithSize(
-          front_.get(), front_capacity - front_->BytesConsumed());
-    }
-
-    // Records number of bytes as added to back.
-    void DidFill(int bytes) {
-      DCHECK(back_.get());
-      back_->DidConsume(bytes);
-      if (back_->BytesRemaining() == 0)
-        back_ = NULL;
-    }
-
-    // Pops number of bytes from front.
-    void DidProcess(int bytes) {
-      DCHECK(front_.get());
-      front_->DidConsume(bytes);
-      if (front_->BytesRemaining() == 0) {
-        storage_.pop_front();
-        front_ = NULL;
-      }
-    }
-
-    void Clear() {
-      front_ = NULL;
-      back_ = NULL;
-      storage_.clear();
-    }
-
-   private:
-    static const unsigned kNumBuffersLimit = 12;
-    const int buf_size_;
-    std::list< scoped_refptr<net::IOBufferWithSize> > storage_;
-    scoped_refptr<net::DrainableIOBuffer> front_;
-    scoped_refptr<net::DrainableIOBuffer> back_;
-
-    DISALLOW_COPY_AND_ASSIGN(IOBufferQueue);
-  };
-
-  SSLChan(const net::AddressList address_list,
-          const net::HostPortPair host_port_pair,
-          int read_pipe,
-          int write_pipe)
-      : phase_(PHASE_CONNECTING),
-        host_port_pair_(host_port_pair),
-        inbound_stream_(WebSocketProxy::kBufferLimit),
-        outbound_stream_(WebSocketProxy::kBufferLimit),
-        read_pipe_(read_pipe),
-        write_pipe_(write_pipe),
-        weak_factory_(this) {
-    if (!SetNonBlock(read_pipe_) || !SetNonBlock(write_pipe_)) {
-      Shut(net::ERR_UNEXPECTED);
-      return;
-    }
-    net::ClientSocketFactory* factory =
-        net::ClientSocketFactory::GetDefaultFactory();
-    socket_.reset(factory->CreateTransportClientSocket(
-        address_list, NULL, net::NetLog::Source()));
-    if (socket_ == NULL) {
-      Shut(net::ERR_FAILED);
-      return;
-    }
-    int result = socket_->Connect(base::Bind(&SSLChan::OnSocketConnect,
-                                             base::Unretained(this)));
-    if (result != net::ERR_IO_PENDING)
-      OnSocketConnect(result);
-  }
-
-  virtual ~SSLChan() {
-    phase_ = PHASE_CLOSED;
-    write_pipe_controller_.StopWatchingFileDescriptor();
-    read_pipe_controller_.StopWatchingFileDescriptor();
-    close(write_pipe_);
-    close(read_pipe_);
-  }
-
-  void Shut(int ALLOW_UNUSED net_error_code) {
-    if (phase_ != PHASE_CLOSED) {
-      phase_ = PHASE_CLOSING;
-      scoped_refptr<net::IOBufferWithSize> buf[] = {
-          outbound_stream_.GetIOBufferToProcess(),
-          inbound_stream_.GetIOBufferToProcess()
-      };
-      for (int i = arraysize(buf); i--;) {
-        if (buf[i].get() && buf[i]->size() > 0) {
-          base::MessageLoop::current()->PostTask(
-              FROM_HERE,
-              base::Bind(&SSLChan::Proceed, weak_factory_.GetWeakPtr()));
-          return;
-        }
-      }
-      phase_ = PHASE_CLOSED;
-      if (socket_ != NULL) {
-        socket_->Disconnect();
-        socket_.reset();
-      }
-      base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
-    }
-  }
-
-  void OnSocketConnect(int result) {
-    if (phase_ != PHASE_CONNECTING) {
-      NOTREACHED();
-      return;
-    }
-    if (result) {
-      Shut(result);
-      return;
-    }
-    net::ClientSocketHandle* handle = new net::ClientSocketHandle();
-    handle->set_socket(socket_.release());
-    net::ClientSocketFactory* factory =
-        net::ClientSocketFactory::GetDefaultFactory();
-    net::SSLClientSocketContext ssl_context;
-    if (!cert_verifier_.get())
-      cert_verifier_.reset(net::CertVerifier::CreateDefault());
-    ssl_context.cert_verifier = cert_verifier_.get();
-    if (!transport_security_state_.get())
-      transport_security_state_.reset(new net::TransportSecurityState);
-    ssl_context.transport_security_state = transport_security_state_.get();
-    socket_.reset(factory->CreateSSLClientSocket(
-        handle, host_port_pair_, ssl_config_, ssl_context));
-    if (!socket_.get()) {
-      LOG(WARNING) << "Failed to create an SSL client socket.";
-      OnSSLHandshakeCompleted(net::ERR_UNEXPECTED);
-      return;
-    }
-    result = socket_->Connect(base::Bind(&SSLChan::OnSSLHandshakeCompleted,
-                                         base::Unretained(this)));
-    if (result != net::ERR_IO_PENDING)
-      OnSSLHandshakeCompleted(result);
-  }
-
-  void OnSSLHandshakeCompleted(int result) {
-    if (result) {
-      Shut(result);
-      return;
-    }
-    is_socket_read_pending_ = false;
-    is_socket_write_pending_ = false;
-    is_read_pipe_blocked_ = false;
-    is_write_pipe_blocked_ = false;
-    base::MessageLoopForIO::current()->WatchFileDescriptor(
-        read_pipe_, false, base::MessageLoopForIO::WATCH_READ,
-        &read_pipe_controller_, this);
-    base::MessageLoopForIO::current()->WatchFileDescriptor(
-        write_pipe_, false, base::MessageLoopForIO::WATCH_WRITE,
-        &write_pipe_controller_, this);
-    phase_ = PHASE_RUNNING;
-    Proceed();
-  }
-
-  void OnSocketRead(int result) {
-    DCHECK(is_socket_read_pending_);
-    is_socket_read_pending_ = false;
-    if (result <= 0) {
-      Shut(result);
-      return;
-    }
-    inbound_stream_.DidFill(result);
-    Proceed();
-  }
-
-  void OnSocketWrite(int result) {
-    DCHECK(is_socket_write_pending_);
-    is_socket_write_pending_ = false;
-    if (result < 0) {
-      outbound_stream_.Clear();
-      Shut(result);
-      return;
-    }
-    outbound_stream_.DidProcess(result);
-    Proceed();
-  }
-
-  // MessageLoopForIO::Watcher overrides.
-  virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
-    if (fd != read_pipe_) {
-      NOTREACHED();
-      return;
-    }
-    is_read_pipe_blocked_ = false;
-    Proceed();
-  }
-
-  virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
-    if (fd != write_pipe_) {
-      NOTREACHED();
-      return;
-    }
-    is_write_pipe_blocked_ = false;
-    Proceed();
-  }
-
- private:
-  void Proceed() {
-    if (phase_ != PHASE_RUNNING && phase_ != PHASE_CLOSING)
-      return;
-    for (bool proceed = true; proceed;) {
-      proceed = false;
-      if (!is_read_pipe_blocked_ && phase_ == PHASE_RUNNING) {
-        scoped_refptr<net::IOBufferWithSize> buf =
-            outbound_stream_.GetIOBufferToFill();
-        if (buf.get() && buf->size() > 0) {
-          int rv = read(read_pipe_, buf->data(), buf->size());
-          if (rv > 0) {
-            outbound_stream_.DidFill(rv);
-            proceed = true;
-          } else if (rv == -1 && errno == EAGAIN) {
-            is_read_pipe_blocked_ = true;
-            base::MessageLoopForIO::current()->WatchFileDescriptor(
-                read_pipe_, false, base::MessageLoopForIO::WATCH_READ,
-                &read_pipe_controller_, this);
-          } else if (rv == 0) {
-            Shut(0);
-          } else {
-            DCHECK_LT(rv, 0);
-            Shut(net::ERR_UNEXPECTED);
-            return;
-          }
-        }
-      }
-      if (!is_socket_read_pending_ && phase_ == PHASE_RUNNING) {
-        scoped_refptr<net::IOBufferWithSize> buf =
-            inbound_stream_.GetIOBufferToFill();
-        if (buf.get() && buf->size() > 0) {
-          int rv = socket_->Read(
-              buf.get(),
-              buf->size(),
-              base::Bind(&SSLChan::OnSocketRead, base::Unretained(this)));
-          is_socket_read_pending_ = true;
-          if (rv != net::ERR_IO_PENDING) {
-            base::MessageLoop::current()->PostTask(
-                FROM_HERE, base::Bind(&SSLChan::OnSocketRead,
-                                      weak_factory_.GetWeakPtr(), rv));
-          }
-        }
-      }
-      if (!is_socket_write_pending_) {
-        scoped_refptr<net::IOBufferWithSize> buf =
-            outbound_stream_.GetIOBufferToProcess();
-        if (buf.get() && buf->size() > 0) {
-          int rv = socket_->Write(
-              buf.get(),
-              buf->size(),
-              base::Bind(&SSLChan::OnSocketWrite, base::Unretained(this)));
-          is_socket_write_pending_ = true;
-          if (rv != net::ERR_IO_PENDING) {
-            base::MessageLoop::current()->PostTask(
-                FROM_HERE, base::Bind(&SSLChan::OnSocketWrite,
-                                      weak_factory_.GetWeakPtr(), rv));
-          }
-        } else if (phase_ == PHASE_CLOSING) {
-          Shut(0);
-        }
-      }
-      if (!is_write_pipe_blocked_) {
-        scoped_refptr<net::IOBufferWithSize> buf =
-            inbound_stream_.GetIOBufferToProcess();
-        if (buf.get() && buf->size() > 0) {
-          int rv = write(write_pipe_, buf->data(), buf->size());
-          if (rv > 0) {
-            inbound_stream_.DidProcess(rv);
-            proceed = true;
-          } else if (rv == -1 && errno == EAGAIN) {
-            is_write_pipe_blocked_ = true;
-            base::MessageLoopForIO::current()->WatchFileDescriptor(
-                write_pipe_, false, base::MessageLoopForIO::WATCH_WRITE,
-                &write_pipe_controller_, this);
-          } else {
-            DCHECK_LE(rv, 0);
-            inbound_stream_.Clear();
-            Shut(net::ERR_UNEXPECTED);
-            return;
-          }
-        } else if (phase_ == PHASE_CLOSING) {
-          Shut(0);
-        }
-      }
-    }
-  }
-
-  Phase phase_;
-  scoped_ptr<net::StreamSocket> socket_;
-  net::HostPortPair host_port_pair_;
-  scoped_ptr<net::CertVerifier> cert_verifier_;
-  scoped_ptr<net::TransportSecurityState> transport_security_state_;
-  net::SSLConfig ssl_config_;
-  IOBufferQueue inbound_stream_;
-  IOBufferQueue outbound_stream_;
-  int read_pipe_;
-  int write_pipe_;
-  bool is_socket_read_pending_;
-  bool is_socket_write_pending_;
-  bool is_read_pipe_blocked_;
-  bool is_write_pipe_blocked_;
-  base::WeakPtrFactory<SSLChan> weak_factory_;
-  base::MessageLoopForIO::FileDescriptorWatcher read_pipe_controller_;
-  base::MessageLoopForIO::FileDescriptorWatcher write_pipe_controller_;
-
-  friend class base::DeleteHelper<SSLChan>;
-  DISALLOW_COPY_AND_ASSIGN(SSLChan);
-};
-
-Serv::Serv()
-    : evbase_(NULL),
-      listening_sock_(-1),
-      extra_listening_sock_(-1),
-      shutdown_requested_(false) {
-  control_descriptor_[0] = -1;
-  control_descriptor_[1] = -1;
-}
-
-Serv::~Serv() {
-  CloseAll();
-}
-
-void Serv::Run() {
-  if (evbase_ || shutdown_requested_)
-    return;
-
-  evbase_ = event_init();
-  if (!evbase_) {
-    LOG(ERROR) << "WebSocketProxy: Couldn't create libevent base";
-    return;
-  }
-
-  if (pipe(control_descriptor_) ||
-      !SetNonBlock(control_descriptor_[0]) ||
-      !SetNonBlock(control_descriptor_[1])) {
-    LOG(ERROR) << "WebSocketProxy: Failed to create control pipe";
-    return;
-  }
-
-  listening_sock_ = socket(AF_INET, SOCK_STREAM, 0);
-  if (listening_sock_ < 0) {
-    LOG(ERROR) << "WebSocketProxy: Failed to create socket";
-    return;
-  }
-  {
-    int on = 1;
-    setsockopt(listening_sock_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-  }
-
-  struct sockaddr_in addr;
-  memset(&addr, 0, sizeof(addr));
-  addr.sin_family = AF_INET;
-  // Let the OS allocate a port number.
-  addr.sin_port = base::HostToNet16(0);
-  addr.sin_addr.s_addr = base::HostToNet32(INADDR_LOOPBACK);
-  if (bind(listening_sock_,
-           reinterpret_cast<struct sockaddr*>(&addr),
-           sizeof(addr))) {
-    LOG(ERROR) << "WebSocketProxy: Failed to bind server socket";
-    return;
-  }
-  if (listen(listening_sock_, 12)) {
-    LOG(ERROR) << "WebSocketProxy: Failed to listen server socket";
-    return;
-  }
-  if (!SetNonBlock(listening_sock_)) {
-    LOG(ERROR) << "WebSocketProxy: Failed to go non block";
-    return;
-  }
-
-  connection_event_.reset(new struct event);
-  event_set(connection_event_.get(), listening_sock_, EV_READ | EV_PERSIST,
-            &OnConnect, this);
-  event_base_set(evbase_, connection_event_.get());
-  if (event_add(connection_event_.get(), NULL)) {
-    LOG(ERROR) << "WebSocketProxy: Failed to add listening event";
-    return;
-  }
-
-  {
-    // TODO(dilmah): remove this control block as soon as obsolete
-    // getPassportForTCP function is removed from webSocketProxyPrivate API.
-    // Following block adds extra listening socket on fixed port 10101.
-    extra_listening_sock_ = socket(AF_INET, SOCK_STREAM, 0);
-    if (extra_listening_sock_ < 0) {
-      LOG(ERROR) << "WebSocketProxy: Failed to create socket";
-      return;
-    }
-    {
-      int on = 1;
-      setsockopt(listening_sock_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-    }
-    const int kPort = 10101;
-    memset(&addr, 0, sizeof(addr));
-    addr.sin_family = AF_INET;
-    addr.sin_port = base::HostToNet16(kPort);
-    addr.sin_addr.s_addr = base::HostToNet32(INADDR_LOOPBACK);
-    if (bind(extra_listening_sock_,
-             reinterpret_cast<struct sockaddr*>(&addr),
-             sizeof(addr))) {
-      LOG(ERROR) << "WebSocketProxy: Failed to bind server socket";
-      return;
-    }
-    if (listen(extra_listening_sock_, 12)) {
-      LOG(ERROR) << "WebSocketProxy: Failed to listen server socket";
-      return;
-    }
-    if (!SetNonBlock(extra_listening_sock_)) {
-      LOG(ERROR) << "WebSocketProxy: Failed to go non block";
-      return;
-    }
-    extra_connection_event_.reset(new struct event);
-    event_set(extra_connection_event_.get(), extra_listening_sock_,
-              EV_READ | EV_PERSIST, &OnConnect, this);
-    event_base_set(evbase_, extra_connection_event_.get());
-    if (event_add(extra_connection_event_.get(), NULL)) {
-      LOG(ERROR) << "WebSocketProxy: Failed to add listening event";
-      return;
-    }
-  }
-
-  control_event_.reset(new struct event);
-  event_set(control_event_.get(), control_descriptor_[0], EV_READ | EV_PERSIST,
-            &OnControlRequest, this);
-  event_base_set(evbase_, control_event_.get());
-  if (event_add(control_event_.get(), NULL)) {
-    LOG(ERROR) << "WebSocketProxy: Failed to add control event";
-    return;
-  }
-
-  if (evdns_init())
-    LOG(WARNING) << "WebSocketProxy: Failed to initialize evDNS";
-  if (!IgnoreSigPipe()) {
-    LOG(ERROR) << "WebSocketProxy: Failed to ignore SIGPIPE";
-    return;
-  }
-
-  memset(&addr, 0, sizeof(addr));
-  socklen_t addr_len = sizeof(addr);
-  if (getsockname(
-      listening_sock_, reinterpret_cast<struct sockaddr*>(&addr), &addr_len)) {
-    LOG(ERROR) << "Failed to determine listening port";
-    return;
-  }
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&SendNotification, base::NetToHost16(addr.sin_port)));
-
-  LOG(INFO) << "WebSocketProxy: Starting event dispatch loop.";
-  event_base_dispatch(evbase_);
-  if (shutdown_requested_)
-    LOG(INFO) << "WebSocketProxy: Event dispatch loop terminated upon request";
-  else
-    LOG(ERROR) << "WebSocketProxy: Event dispatch loop terminated unexpectedly";
-  CloseAll();
-}
-
-void Serv::Shutdown() {
-  int r ALLOW_UNUSED =
-      write(control_descriptor_[1], kControlMessageShutdown, 1);
-}
-
-void Serv::OnNetworkChange() {
-  int r ALLOW_UNUSED =
-      write(control_descriptor_[1], kControlMessageNetworkChange, 1);
-}
-
-void Serv::CloseAll() {
-  while (!conn_pool_.empty())
-    ZapConn(conn_pool_.back());
-  if (listening_sock_ >= 0) {
-    shutdown(listening_sock_, SHUT_RDWR);
-    close(listening_sock_);
-  }
-  for (int i = 0; i < 2; ++i) {
-    if (control_descriptor_[i] >= 0) {
-      control_descriptor_[i] = -1;
-      close(control_descriptor_[i]);
-    }
-  }
-  if (control_event_.get()) {
-    event_del(control_event_.get());
-    control_event_.reset();
-  }
-  if (extra_connection_event_.get()) {
-    event_del(extra_connection_event_.get());
-    extra_connection_event_.reset();
-  }
-  if (connection_event_.get()) {
-    event_del(connection_event_.get());
-    connection_event_.reset();
-  }
-  if (evbase_) {
-    event_base_free(evbase_);
-    evbase_ = NULL;
-  }
-}
-
-void Serv::ZapConn(Conn* cs) {
-  RevMap::iterator rit = rev_map_.find(cs);
-  if (rit != rev_map_.end()) {
-    conn_pool_.erase(rit->second);
-    rev_map_.erase(rit);
-    delete cs;
-  }
-}
-
-void Serv::MarkConnImportance(Conn* cs, bool important) {
-  if (conn_pool_.size() < WebSocketProxy::kConnPoolLimit / 4) {
-    // Fast common path.
-    return;
-  }
-  RevMap::iterator rit = rev_map_.find(cs);
-  if (rit != rev_map_.end()) {
-    ConnPool::iterator it = rit->second;
-    CHECK(*it == cs);
-    if (important && it == conn_pool_.begin()) {
-      // Already at the top.  Shortcut.
-      return;
-    }
-    conn_pool_.erase(it);
-  }
-  if (important) {
-    conn_pool_.push_front(cs);
-    rev_map_[cs] = conn_pool_.begin();
-  } else {
-    conn_pool_.push_back(cs);
-    rev_map_[cs] = conn_pool_.end();
-    --rev_map_[cs];
-  }
-}
-
-Conn* Serv::GetFreshConn() {
-  if (conn_pool_.size() > WebSocketProxy::kConnPoolLimit) {
-    // Connections overflow.  Shut those oldest not active.
-    ConnPool::iterator it = conn_pool_.end();
-    --it;
-    for (int i = conn_pool_.size() - WebSocketProxy::kConnPoolLimit; i-- > 0;) {
-      // Shut may invalidate an iterator; hence postdecrement.
-      (*it--)->Shut(WS_CLOSE_GOING_AWAY,
-                    "Flood of new connections, getting rid of old ones");
-    }
-    if (conn_pool_.size() > WebSocketProxy::kConnPoolLimit + 12) {
-      // Connections overflow.  Zap the oldest not active.
-      ZapConn(conn_pool_.back());
-    }
-  }
-  Conn* cs = new Conn(this);
-  conn_pool_.push_front(cs);
-  rev_map_[cs] = conn_pool_.begin();
-  return cs;
-}
-
-bool Serv::IsConnSane(Conn* cs) {
-  return rev_map_.find(cs) != rev_map_.end();
-}
-
-// static
-void Serv::OnConnect(int listening_sock, short event, void* ctx) {
-  Serv* self = static_cast<Serv*>(ctx);
-  Conn* cs = self->GetFreshConn();
-  int sock = accept(listening_sock, NULL, NULL);
-  if (sock < 0 || !SetNonBlock(sock)) {
-    // Read readiness was triggered on listening socket
-    // yet we failed to accept a connection; definitely weird.
-    NOTREACHED();
-    self->ZapConn(cs);
-    return;
-  }
-  cs->primchan().set_read_fd(sock);
-  cs->primchan().set_write_fd(sock);
-
-  struct bufferevent* bev = bufferevent_new(
-      sock,
-      &Conn::OnPrimchanRead, &Conn::OnPrimchanWrite, &Conn::OnPrimchanError,
-      cs->evkey());
-  if (bev == NULL) {
-    self->ZapConn(cs);
-    return;
-  }
-  cs->primchan().set_read_bev(bev);
-  cs->primchan().set_write_bev(bev);
-  bufferevent_base_set(self->evbase_, bev);
-  bufferevent_setwatermark(bev, EV_READ, 0, WebSocketProxy::kBufferLimit);
-  if (bufferevent_enable(bev, EV_READ | EV_WRITE)) {
-    self->ZapConn(cs);
-    return;
-  }
-}
-
-// static
-void Serv::OnControlRequest(int fd, short event, void* ctx) {
-  Serv* self = static_cast<Serv*>(ctx);
-
-  char c;
-  if (1 == read(fd, &c, 1) && c == *kControlMessageNetworkChange) {
-    // OnNetworkChange request.
-    evdns_clear_nameservers_and_suspend();
-    evdns_init();
-    evdns_resume();
-  } else if (c == *kControlMessageShutdown) {
-    self->shutdown_requested_ = true;
-    event_base_loopbreak(self->evbase_);
-  }
-}
-
-Conn::Conn(Serv* master)
-    : master_(master),
-      phase_(PHASE_WAIT_HANDSHAKE),
-      frame_bytes_remaining_(0),
-      frame_mask_index_(0),
-      primchan_(this),
-      destchan_(this),
-      do_tls_(false),
-      destresolution_ipv4_failed_(false),
-      destresolution_ipv6_failed_(false) {
-  while (evkey_map_.Get().find(last_evkey_) != evkey_map_.Get().end()) {
-    last_evkey_ = reinterpret_cast<EventKey>(reinterpret_cast<size_t>(
-        last_evkey_) + 1);
-  }
-  evkey_ = last_evkey_;
-  evkey_map_.Get()[evkey_] = this;
-  // Schedule timeout for initial phase of connection.
-  destconnect_timeout_event_.reset(new struct event);
-  evtimer_set(destconnect_timeout_event_.get(),
-              &OnDestConnectTimeout, evkey_);
-  event_base_set(master_->evbase(),
-                 destconnect_timeout_event_.get());
-
-  struct timeval tv;
-  tv.tv_sec = 20;
-  tv.tv_usec = 0;
-  evtimer_add(destconnect_timeout_event_.get(), &tv);
-}
-
-Conn::~Conn() {
-  phase_ = PHASE_DEFUNCT;
-  event_del(destconnect_timeout_event_.get());
-  if (evkey_map_.Get()[evkey_] == this)
-    evkey_map_.Get().erase(evkey_);
-  else
-    NOTREACHED();
-}
-
-Conn* Conn::Get(EventKey evkey) {
-  EventKeyMap::iterator it = evkey_map_.Get().find(evkey);
-  if (it == evkey_map_.Get().end())
-    return NULL;
-  Conn* cs = it->second;
-  if (cs == NULL ||
-      cs->evkey_ != evkey ||
-      cs->master_ == NULL ||
-      cs->phase_ < 0 ||
-      cs->phase_ > PHASE_SHUT ||
-      !cs->master_->IsConnSane(cs)) {
-    return NULL;
-  }
-  return cs;
-}
-
-void Conn::Shut(int status, const void* reason) {
-  if (phase_ >= PHASE_SHUT)
-    return;
-  master_->MarkConnImportance(this, false);
-
-  std::vector<uint8> payload(2 + strlen(reason));
-  WriteNetworkInteger(status, vector_as_array(&payload), 2);
-  memcpy(vector_as_array(&payload) + 2, reason, strlen(reason));
-
-  EmitFrame(
-      &primchan_, WS_OPCODE_CLOSE, vector_as_array(&payload), payload.size());
-  primchan_.Shut();
-  destchan_.Shut();
-  phase_ = PHASE_SHUT;
-}
-
-void Conn::ConsiderSuicide() {
-  if (!primchan_.write_pending() && !destchan_.write_pending())
-    master_->ZapConn(this);
-}
-
-Conn::Status Conn::ConsumeHeader(struct evbuffer* evb) {
-  uint8* buf = EVBUFFER_DATA(evb);
-  size_t buf_size = EVBUFFER_LENGTH(evb);
-
-  static const uint8 kGetPrefix[] = "GET " kProxyPath;
-  static const uint8 kKeyValueDelimiter[] = ": ";
-
-  if (buf_size <= 0)
-    return STATUS_INCOMPLETE;
-  if (!buf)
-    return STATUS_ABORT;
-  if (!std::equal(buf, buf + std::min(buf_size, strlen(kGetPrefix)),
-                  kGetPrefix)) {
-    // Data head does not match what is expected.
-    return STATUS_ABORT;
-  }
-
-  if (buf_size >= WebSocketProxy::kHeaderLimit)
-    return STATUS_ABORT;
-  uint8* buf_end = buf + buf_size;
-  // Handshake request must end with double CRLF.
-  uint8* term_pos = std::search(buf, buf_end, kCRLFCRLF,
-                                kCRLFCRLF + strlen(kCRLFCRLF));
-  if (term_pos == buf_end)
-    return STATUS_INCOMPLETE;
-  term_pos += strlen(kCRLFCRLF);
-  // First line is "GET path?query protocol" line.  If query is empty then we
-  // fall back to (obsolete) way of obtaining parameters from first websocket
-  // frame.  Otherwise query contains all required parameters (host, port etc).
-  uint8* get_request_end = std::search(
-      buf, term_pos, kCRLF, kCRLF + strlen(kCRLF));
-  DCHECK(get_request_end != term_pos);
-  uint8* resource_end = std::find(
-      buf + strlen(kGetPrefix), get_request_end, ' ');
-  if (*resource_end != ' ')
-    return STATUS_ABORT;
-  if (resource_end != buf + strlen(kGetPrefix)) {
-    char* piece = reinterpret_cast<char*>(buf) + strlen(kGetPrefix) + 1;
-    url_parse::Component query(
-        0, resource_end - reinterpret_cast<uint8*>(piece));
-    for (url_parse::Component key, value;
-        url_parse::ExtractQueryKeyValue(piece, &query, &key, &value);) {
-      if (key.len > 0) {
-        requested_parameters_[std::string(piece + key.begin, key.len)] =
-            net::UnescapeURLComponent(std::string(piece + value.begin,
-                value.len), net::UnescapeRule::URL_SPECIAL_CHARS);
-      }
-    }
-  }
-  for (uint8* pos = get_request_end;;) {
-    pos += strlen(kCRLF);
-    if (term_pos - pos < static_cast<ptrdiff_t>(strlen(kCRLF)))
-      return STATUS_ABORT;
-    if (term_pos - pos == static_cast<ptrdiff_t>(strlen(kCRLF)))
-      break;
-    uint8* npos = std::search(pos, term_pos, kKeyValueDelimiter,
-                              kKeyValueDelimiter + strlen(kKeyValueDelimiter));
-    if (npos == term_pos)
-      return STATUS_ABORT;
-    std::string key = FetchAsciiSnippet(pos, npos, AsciiFilterLower);
-    pos = std::search(npos += strlen(kKeyValueDelimiter), term_pos,
-                      kCRLF, kCRLF + strlen(kCRLF));
-    if (pos == term_pos)
-      return STATUS_ABORT;
-    if (!key.empty()) {
-      header_fields_[key] = FetchAsciiSnippet(npos, pos,
-          key == "sec-websocket-key" ? AsciiFilterVerbatim : AsciiFilterLower);
-    }
-  }
-
-  // Values of Upgrade and Connection fields are hardcoded in the protocol.
-  if (header_fields_["upgrade"] != "websocket" ||
-      header_fields_["connection"] != "upgrade" ||
-      header_fields_["sec-websocket-key"].size() != 24) {
-    return STATUS_ABORT;
-  }
-  if (header_fields_["sec-websocket-version"] != "8" &&
-      header_fields_["sec-websocket-version"] != "13") {
-    return STATUS_ABORT;
-  }
-  // Normalize origin (e.g. leading slash).
-  GURL origin = GURL(GetOrigin()).GetOrigin();
-  if (!origin.is_valid())
-    return STATUS_ABORT;
-
-  if (!requested_parameters_.empty()) {
-    destname_ = requested_parameters_["hostname"];
-    int port;
-    if (!base::StringToInt(requested_parameters_["port"], &port) ||
-        port < 0 || port >= 1 << 16) {
-      return STATUS_ABORT;
-    }
-    destport_ = port;
-    destaddr_ = requested_parameters_["addr"];
-    do_tls_ = (requested_parameters_["tls"] == "true");
-
-    requested_parameters_["extension_id"] =
-        FetchExtensionIdFromOrigin(GetOrigin());
-    std::string passport(requested_parameters_["passport"]);
-    requested_parameters_.erase("passport");
-    if (!chrome::InternalAuthVerification::VerifyPassport(
-        passport, "web_socket_proxy", requested_parameters_)) {
-      return STATUS_ABORT;
-    }
-  }
-
-  evbuffer_drain(evb, term_pos - buf);
-  return STATUS_OK;
-}
-
-bool Conn::EmitHandshake(Chan* chan) {
-  std::vector<std::string> boilerplate;
-  boilerplate.push_back("HTTP/1.1 101 WebSocket Protocol Handshake");
-  boilerplate.push_back("Upgrade: websocket");
-  boilerplate.push_back("Connection: Upgrade");
-
-  {
-    // Take care of Accept field.
-    std::string word = header_fields_["sec-websocket-key"];
-    word += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
-    std::string accept_token;
-    base::Base64Encode(base::SHA1HashString(word), &accept_token);
-    boilerplate.push_back("Sec-WebSocket-Accept: " + accept_token);
-  }
-
-  boilerplate.push_back("");
-  for (size_t i = 0; i < boilerplate.size(); ++i) {
-    if (!chan->Write(boilerplate[i].c_str(), boilerplate[i].size()) ||
-        !chan->Write(kCRLF, strlen(kCRLF))) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool Conn::EmitFrame(
-    Chan* chan, WebSocketFrameOpcode opcode, const void* buf, size_t size) {
-  uint8 header[10];
-  int header_size = 2;
-  DCHECK(chan);
-  DCHECK(opcode >= 0 && static_cast<int>(opcode) < 16);
-  header[0] = opcode | 0x80;  // FIN bit set.
-  if (size < 126) {
-    header[1] = size;
-  } else if (size < (1 << 16)) {
-    header[1] = 126;
-    WriteNetworkInteger(size, header + 2, 2);
-    header_size += 2;
-  } else {
-    header[1] = 127;
-    WriteNetworkInteger(size, header + 2, 8);
-    header_size += 8;
-  }
-  return chan->Write(header, header_size) && chan->Write(buf, size);
-}
-
-Conn::Status Conn::ConsumeDestframe(struct evbuffer* evb) {
-  if (!requested_parameters_.empty()) {
-    // Parameters were already provided (and verified) in query component of
-    // websocket URL.
-    return STATUS_OK;
-  }
-  if (frame_bytes_remaining_ == 0) {
-    Conn::Status rv = ConsumeFrameHeader(evb);
-    if (rv != STATUS_OK)
-      return rv;
-    if (frame_bytes_remaining_ == 0 ||
-        frame_bytes_remaining_ >= WebSocketProxy::kHeaderLimit) {
-      return STATUS_ABORT;
-    }
-  }
-
-  uint8* buf = EVBUFFER_DATA(evb);
-  size_t buf_size = EVBUFFER_LENGTH(evb);
-  if (buf_size < frame_bytes_remaining_)
-    return STATUS_INCOMPLETE;
-  for (size_t i = 0; i < buf_size; ++i) {
-    buf[i] ^= frame_mask_[frame_mask_index_];
-    frame_mask_index_ = (frame_mask_index_ + 1) % 4;
-  }
-  std::string passport;
-  if (!WebSocketProxyHelper::FetchPassportAddrNamePort(
-      buf, buf + frame_bytes_remaining_,
-      &passport, &destaddr_, &destname_, &destport_)) {
-    return STATUS_ABORT;
-  }
-  std::map<std::string, std::string> map;
-  map["hostname"] = destname_;
-  map["port"] = base::IntToString(destport_);
-  map["extension_id"] = FetchExtensionIdFromOrigin(GetOrigin());
-  if (!destaddr_.empty())
-    map["addr"] = destaddr_;
-  if (!chrome::InternalAuthVerification::VerifyPassport(
-      passport, "web_socket_proxy", map)) {
-    return STATUS_ABORT;
-  }
-
-  evbuffer_drain(evb, frame_bytes_remaining_);
-  frame_bytes_remaining_ = 0;
-  return STATUS_OK;
-}
-
-Conn::Status Conn::ConsumeFrameHeader(struct evbuffer* evb) {
-  uint8* buf = EVBUFFER_DATA(evb);
-  size_t buf_size = EVBUFFER_LENGTH(evb);
-  size_t header_size = 2;
-
-  if (buf_size < header_size)
-    return STATUS_INCOMPLETE;
-  if (buf[0] & 0x70) {
-    // We are not able to handle non-zero reserved bits.
-    NOTIMPLEMENTED();
-    return STATUS_ABORT;
-  }
-  bool fin_flag = buf[0] & 0x80;
-  if (!fin_flag) {
-    NOTIMPLEMENTED();
-    return STATUS_ABORT;
-  }
-  int opcode = buf[0] & 0x0f;
-  switch (opcode) {
-    case WS_OPCODE_TEXT:
-      break;
-    case WS_OPCODE_CLOSE:
-      return STATUS_ABORT;
-    default:
-      NOTIMPLEMENTED();
-      return STATUS_ABORT;
-  }
-
-  bool mask_flag = buf[1] & 0x80;
-  if (!mask_flag) {
-    // Client-to-server frames must be masked.
-    return STATUS_ABORT;
-  }
-  frame_bytes_remaining_ = buf[1] & 0x7f;
-  int extra_size = 0;
-  if (frame_bytes_remaining_ == 126)
-    extra_size = 2;
-  else if (frame_bytes_remaining_ == 127)
-    extra_size = 8;
-  if (buf_size < header_size + extra_size + sizeof(frame_mask_))
-    return STATUS_INCOMPLETE;
-  if (extra_size)
-    frame_bytes_remaining_ = ReadNetworkInteger(buf + header_size, extra_size);
-  header_size += extra_size;
-  memcpy(frame_mask_, buf + header_size, sizeof(frame_mask_));
-  header_size += sizeof(frame_mask_);
-  frame_mask_index_ = 0;
-  evbuffer_drain(evb, header_size);
-  return STATUS_OK;
-}
-
-Conn::Status Conn::ProcessFrameData(struct evbuffer* evb) {
-  uint8* buf = EVBUFFER_DATA(evb);
-  size_t buf_size = EVBUFFER_LENGTH(evb);
-
-  DCHECK_GE(frame_bytes_remaining_, 1u);
-  if (frame_bytes_remaining_ < buf_size)
-    buf_size = frame_bytes_remaining_;
-  // base64 is encoded in chunks of 4 bytes.
-  buf_size = buf_size / 4 * 4;
-  if (buf_size < 1)
-    return STATUS_INCOMPLETE;
-  switch (phase_) {
-    case PHASE_INSIDE_FRAME_BASE64: {
-      for (size_t i = 0; i < buf_size; ++i) {
-        buf[i] ^= frame_mask_[frame_mask_index_];
-        frame_mask_index_ = (frame_mask_index_ + 1) % 4;
-      }
-      std::string out_bytes;
-      base::Base64Decode(std::string(buf, buf + buf_size), &out_bytes);
-      evbuffer_drain(evb, buf_size);
-      DCHECK(destchan_.write_bev());
-      if (!destchan_.Write(out_bytes.c_str(), out_bytes.size()))
-        return STATUS_ABORT;
-      break;
-    }
-    case PHASE_INSIDE_FRAME_SKIP: {
-      evbuffer_drain(evb, buf_size);
-      break;
-    }
-    default: {
-      return STATUS_ABORT;
-    }
-  }
-  frame_bytes_remaining_ -= buf_size;
-  return frame_bytes_remaining_ ? STATUS_INCOMPLETE : STATUS_OK;
-}
-
-bool Conn::TryConnectDest(const struct sockaddr* addr, socklen_t addrlen) {
-  if (destchan_.read_fd() >= 0 || destchan_.read_bev() != NULL)
-    return false;
-  if (do_tls_) {
-    int fd[4];
-    if (pipe(fd) || pipe(fd + 2))
-      return false;
-    destchan_.set_read_fd(fd[0]);
-    destchan_.set_write_fd(fd[3]);
-    for (int i = arraysize(fd); i--;) {
-      if (!SetNonBlock(fd[i]))
-        return false;
-    }
-    destchan_.set_read_bev(bufferevent_new(
-        destchan_.read_fd(),
-        &OnDestchanRead, NULL, &OnDestchanError,
-        evkey_));
-    destchan_.set_write_bev(bufferevent_new(
-        destchan_.write_fd(),
-        NULL, &OnDestchanWrite, &OnDestchanError,
-        evkey_));
-    net::IPEndPoint endpoint;
-    if (!endpoint.FromSockAddr(addr, addrlen))
-      return false;
-    net::AddressList addrlist(endpoint);
-    net::HostPortPair host_port_pair(destname_, destport_);
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE, base::Bind(
-            &SSLChan::Start, addrlist, host_port_pair, fd[2], fd[1]));
-  } else {
-    int sock = socket(addr->sa_family, SOCK_STREAM, 0);
-    if (sock < 0)
-      return false;
-    destchan_.set_read_fd(sock);
-    destchan_.set_write_fd(sock);
-    if (!SetNonBlock(sock))
-      return false;
-    if (connect(sock, addr, addrlen)) {
-      if (errno != EINPROGRESS)
-        return false;
-    }
-    destchan_.set_read_bev(bufferevent_new(
-        sock,
-        &OnDestchanRead, &OnDestchanWrite, &OnDestchanError,
-        evkey_));
-    destchan_.set_write_bev(destchan_.read_bev());
-  }
-  if (destchan_.read_bev() == NULL || destchan_.write_bev() == NULL)
-    return false;
-  if (bufferevent_base_set(master_->evbase(), destchan_.read_bev()) ||
-      bufferevent_base_set(master_->evbase(), destchan_.write_bev())) {
-    return false;
-  }
-  bufferevent_setwatermark(
-      destchan_.read_bev(), EV_READ, 0, WebSocketProxy::kBufferLimit);
-  if (bufferevent_enable(destchan_.read_bev(), EV_READ) ||
-      bufferevent_enable(destchan_.write_bev(), EV_WRITE)) {
-    return false;
-  }
-  return true;
-}
-
-const std::string& Conn::GetOrigin() {
-  return header_fields_[header_fields_["sec-websocket-version"] == "8" ?
-      "sec-websocket-origin" : "origin"];
-}
-
-// static
-void Conn::OnPrimchanRead(struct bufferevent* bev, EventKey evkey) {
-  Conn* cs = Conn::Get(evkey);
-  if (bev == NULL ||
-      cs == NULL ||
-      bev != cs->primchan_.read_bev()) {
-    NOTREACHED();
-    return;
-  }
-  if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) <= 0)
-    return;
-  cs->master_->MarkConnImportance(cs, true);
-  for (;;) {
-    switch (cs->phase_) {
-      case PHASE_WAIT_HANDSHAKE: {
-        switch (cs->ConsumeHeader(EVBUFFER_INPUT(bev))) {
-          case STATUS_OK: {
-            break;
-          }
-          case STATUS_INCOMPLETE: {
-            return;
-          }
-          case STATUS_ABORT:
-          default: {
-            cs->master_->ZapConn(cs);
-            return;
-          }
-        }
-        // Header consumed OK.  Do respond.
-        if (!cs->EmitHandshake(&cs->primchan_)) {
-          cs->master_->ZapConn(cs);
-          return;
-        }
-        cs->phase_ = PHASE_WAIT_DESTFRAME;
-      }
-      case PHASE_WAIT_DESTFRAME: {
-        switch (cs->ConsumeDestframe(EVBUFFER_INPUT(bev))) {
-          case STATUS_OK: {
-            {
-              // Unfortunately libevent as of 1.4 does not look into /etc/hosts.
-              // There seems to be no easy API to perform only "local" part of
-              // getaddrinfo resolution.  Hence this hack for "localhost".
-              if (cs->destname_ == "localhost")
-                cs->destname_ = "127.0.0.1";
-            }
-            if (cs->destaddr_.empty())
-              cs->destaddr_ = cs->destname_;
-            {
-              struct sockaddr_in sa;
-              memset(&sa, 0, sizeof(sa));
-              sa.sin_port = base::HostToNet16(cs->destport_);
-              if (inet_pton(sa.sin_family = AF_INET,
-                            cs->destaddr_.c_str(),
-                            &sa.sin_addr) == 1) {
-                // valid IPv4 address supplied.
-                if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa))) {
-                  cs->phase_ = PHASE_WAIT_DESTCONNECT;
-                  return;
-                }
-              }
-            }
-            {
-              if (cs->destaddr_.size() >= 2 &&
-                  cs->destaddr_[0] == '[' &&
-                  cs->destaddr_[cs->destaddr_.size() - 1] == ']') {
-                // Literal IPv6 address in brackets.
-                cs->destaddr_ =
-                    cs->destaddr_.substr(1, cs->destaddr_.size() - 2);
-              }
-              struct sockaddr_in6 sa;
-              memset(&sa, 0, sizeof(sa));
-              sa.sin6_port = base::HostToNet16(cs->destport_);
-              if (inet_pton(sa.sin6_family = AF_INET6,
-                            cs->destaddr_.c_str(),
-                            &sa.sin6_addr) == 1) {
-                // valid IPv6 address supplied.
-                if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa))) {
-                  cs->phase_ = PHASE_WAIT_DESTCONNECT;
-                  return;
-                }
-              }
-            }
-            // Asynchronous DNS resolution.
-            if (evdns_count_nameservers() < 1) {
-              evdns_clear_nameservers_and_suspend();
-              evdns_init();
-              evdns_resume();
-            }
-            evdns_resolve_ipv4(cs->destname_.c_str(), 0,
-                               &OnDestResolutionIPv4, evkey);
-            evdns_resolve_ipv6(cs->destname_.c_str(), 0,
-                               &OnDestResolutionIPv6, evkey);
-            cs->phase_ = PHASE_WAIT_DESTCONNECT;
-            return;
-          }
-          case STATUS_INCOMPLETE: {
-            return;
-          }
-          case STATUS_ABORT:
-          default: {
-            cs->Shut(WS_CLOSE_DESTINATION_ERROR,
-                     "Incorrect destination specification in first frame");
-            return;
-          }
-        }
-      }
-      case PHASE_WAIT_DESTCONNECT: {
-        if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) >=
-            WebSocketProxy::kBufferLimit) {
-          cs->Shut(WS_CLOSE_LIMIT_VIOLATION, "Read buffer overflow");
-        }
-        return;
-      }
-      case PHASE_OUTSIDE_FRAME: {
-        switch (cs->ConsumeFrameHeader(EVBUFFER_INPUT(bev))) {
-          case STATUS_OK: {
-            if (cs->frame_bytes_remaining_ % 4) {
-              // We expect base64 encoded data (encoded in 4-bytes chunks).
-              cs->Shut(WS_CLOSE_UNACCEPTABLE_DATA,
-                       "Frame payload size is not multiple of 4");
-              return;
-            }
-            cs->phase_ = PHASE_INSIDE_FRAME_BASE64;
-            // Process remaining data if any.
-            break;
-          }
-          case STATUS_SKIP: {
-            cs->phase_ = PHASE_INSIDE_FRAME_SKIP;
-            // Process remaining data if any.
-            break;
-          }
-          case STATUS_INCOMPLETE: {
-            return;
-          }
-          case STATUS_ABORT:
-          default: {
-            cs->Shut(WS_CLOSE_PROTOCOL_ERROR, "Invalid frame header");
-            return;
-          }
-        }
-        break;
-      }
-      case PHASE_INSIDE_FRAME_BASE64:
-      case PHASE_INSIDE_FRAME_SKIP: {
-        switch (cs->ProcessFrameData(EVBUFFER_INPUT(bev))) {
-          case STATUS_OK: {
-            cs->phase_ = PHASE_OUTSIDE_FRAME;
-            // Handle remaining data if any.
-            break;
-          }
-          case STATUS_INCOMPLETE: {
-            return;
-          }
-          case STATUS_ABORT:
-          default: {
-            cs->Shut(WS_CLOSE_UNACCEPTABLE_DATA, "Invalid frame data");
-            return;
-          }
-        }
-        break;
-      }
-      case PHASE_SHUT: {
-        evbuffer_drain(EVBUFFER_INPUT(bev),
-                       EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)));
-        return;
-      }
-      case PHASE_DEFUNCT:
-      default: {
-        NOTREACHED();
-        cs->master_->ZapConn(cs);
-        return;
-      }
-    }
-  }
-}
-
-// static
-void Conn::OnPrimchanWrite(struct bufferevent* bev, EventKey evkey) {
-  Conn* cs = Conn::Get(evkey);
-  if (bev == NULL ||
-      cs == NULL ||
-      bev != cs->primchan_.write_bev()) {
-    NOTREACHED();
-    return;
-  }
-  // Write callback is called when low watermark is reached, 0 by default.
-  cs->primchan_.set_write_pending(false);
-  if (cs->phase_ >= PHASE_SHUT) {
-    cs->master_->ZapConn(cs);
-    return;
-  }
-  if (cs->phase_ > PHASE_WAIT_DESTCONNECT)
-    OnDestchanRead(cs->destchan_.read_bev(), evkey);
-  if (cs->phase_ >= PHASE_SHUT)
-    cs->primchan_.Zap();
-}
-
-// static
-void Conn::OnPrimchanError(struct bufferevent* bev,
-                           short what, EventKey evkey) {
-  Conn* cs = Conn::Get(evkey);
-  if (bev == NULL ||
-      cs == NULL ||
-      (bev != cs->primchan_.read_bev() && bev != cs->primchan_.write_bev())) {
-    return;
-  }
-  cs->primchan_.set_write_pending(false);
-  if (cs->phase_ >= PHASE_SHUT)
-    cs->master_->ZapConn(cs);
-  else
-    cs->Shut(WS_CLOSE_NORMAL, "Error reported on websocket channel");
-}
-
-// static
-void Conn::OnDestResolutionIPv4(int result, char type,
-                                int count, int ttl,
-                                void* addr_list, EventKey evkey) {
-  Conn* cs = Conn::Get(evkey);
-  if (cs == NULL)
-    return;
-  if (cs->phase_ != PHASE_WAIT_DESTCONNECT)
-    return;
-  if (result == DNS_ERR_NONE &&
-      count >= 1 &&
-      addr_list != NULL &&
-      type == DNS_IPv4_A) {
-    for (int i = 0; i < count; ++i) {
-      struct sockaddr_in sa;
-      memset(&sa, 0, sizeof(sa));
-      sa.sin_family = AF_INET;
-      sa.sin_port = base::HostToNet16(cs->destport_);
-      DCHECK(sizeof(sa.sin_addr) == sizeof(struct in_addr));
-      memcpy(&sa.sin_addr,
-             static_cast<struct in_addr*>(addr_list) + i,
-             sizeof(sa.sin_addr));
-      if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa)))
-        return;
-    }
-  }
-  cs->destresolution_ipv4_failed_ = true;
-  if (cs->destresolution_ipv4_failed_ && cs->destresolution_ipv6_failed_)
-    cs->Shut(WS_CLOSE_RESOLUTION_FAILED, "DNS resolution failed");
-}
-
-// static
-void Conn::OnDestResolutionIPv6(int result, char type,
-                                int count, int ttl,
-                                void* addr_list, EventKey evkey) {
-  Conn* cs = Conn::Get(evkey);
-  if (cs == NULL)
-    return;
-  if (cs->phase_ != PHASE_WAIT_DESTCONNECT)
-    return;
-  if (result == DNS_ERR_NONE &&
-      count >= 1 &&
-      addr_list != NULL &&
-      type == DNS_IPv6_AAAA) {
-    for (int i = 0; i < count; ++i) {
-      struct sockaddr_in6 sa;
-      memset(&sa, 0, sizeof(sa));
-      sa.sin6_family = AF_INET6;
-      sa.sin6_port = base::HostToNet16(cs->destport_);
-      DCHECK(sizeof(sa.sin6_addr) == sizeof(struct in6_addr));
-      memcpy(&sa.sin6_addr,
-             static_cast<struct in6_addr*>(addr_list) + i,
-             sizeof(sa.sin6_addr));
-      if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa)))
-        return;
-    }
-  }
-  cs->destresolution_ipv6_failed_ = true;
-  if (cs->destresolution_ipv4_failed_ && cs->destresolution_ipv6_failed_)
-    cs->Shut(WS_CLOSE_RESOLUTION_FAILED, "DNS resolution failed");
-}
-
-// static
-void Conn::OnDestConnectTimeout(int, short, EventKey evkey) {
-  Conn* cs = Conn::Get(evkey);
-  if (cs == NULL)
-    return;
-  if (cs->phase_ > PHASE_WAIT_DESTCONNECT)
-    return;
-  cs->Shut(WS_CLOSE_RESOLUTION_FAILED, "DNS resolution timeout");
-}
-
-// static
-void Conn::OnDestchanRead(struct bufferevent* bev, EventKey evkey) {
-  Conn* cs = Conn::Get(evkey);
-  if (bev == NULL ||
-      cs == NULL ||
-      bev != cs->destchan_.read_bev()) {
-    NOTREACHED();
-    return;
-  }
-  if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) <= 0)
-    return;
-  if (cs->primchan_.write_bev() == NULL) {
-    cs->master_->ZapConn(cs);
-    return;
-  }
-  cs->master_->MarkConnImportance(cs, true);
-  std::string out_bytes;
-  base::Base64Encode(
-      std::string(
-          static_cast<const char*>(static_cast<void*>(
-              EVBUFFER_DATA(EVBUFFER_INPUT(bev)))),
-          EVBUFFER_LENGTH(EVBUFFER_INPUT(bev))),
-      &out_bytes);
-  evbuffer_drain(EVBUFFER_INPUT(bev), EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)));
-  if (!cs->EmitFrame(&cs->primchan_, WS_OPCODE_TEXT,
-                     out_bytes.c_str(), out_bytes.size())) {
-    cs->Shut(WS_CLOSE_UNEXPECTED, "Failed to write websocket frame");
-  }
-}
-
-// static
-void Conn::OnDestchanWrite(struct bufferevent* bev, EventKey evkey) {
-  Conn* cs = Conn::Get(evkey);
-  if (bev == NULL ||
-      cs == NULL ||
-      bev != cs->destchan_.write_bev()) {
-    NOTREACHED();
-    return;
-  }
-  // Write callback is called when low watermark is reached, 0 by default.
-  cs->destchan_.set_write_pending(false);
-  if (cs->phase_ == PHASE_WAIT_DESTCONNECT)
-    cs->phase_ = PHASE_OUTSIDE_FRAME;
-  if (cs->phase_ < PHASE_SHUT)
-    OnPrimchanRead(cs->primchan_.read_bev(), evkey);
-  else
-    cs->destchan_.Zap();
-}
-
-// static
-void Conn::OnDestchanError(struct bufferevent* bev,
-                           short what, EventKey evkey) {
-  Conn* cs = Conn::Get(evkey);
-  if (bev == NULL ||
-      cs == NULL ||
-      (bev != cs->destchan_.read_bev() && bev != cs->destchan_.write_bev())) {
-    return;
-  }
-  cs->destchan_.set_write_pending(false);
-  if (cs->phase_ >= PHASE_SHUT)
-    cs->master_->ZapConn(cs);
-  else
-    cs->Shut(WS_CLOSE_DESTINATION_ERROR,
-             "Failure reported on destination channel");
-}
-
-// static
-Conn::EventKey Conn::last_evkey_ = 0;
-
-// static
-base::LazyInstance<Conn::EventKeyMap>::Leaky
-    Conn::evkey_map_ = LAZY_INSTANCE_INITIALIZER;
-
-}  // namespace
-
-WebSocketProxy::WebSocketProxy() : impl_(new Serv()) {
-}
-
-WebSocketProxy::~WebSocketProxy() {
-  delete static_cast<Serv*>(impl_);
-  impl_ = NULL;
-}
-
-void WebSocketProxy::Run() {
-  static_cast<Serv*>(impl_)->Run();
-}
-
-void WebSocketProxy::Shutdown() {
-  static_cast<Serv*>(impl_)->Shutdown();
-}
-
-void WebSocketProxy::OnNetworkChange() {
-  static_cast<Serv*>(impl_)->OnNetworkChange();
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/web_socket_proxy.h b/chrome/browser/chromeos/web_socket_proxy.h
deleted file mode 100644
index 08436f5..0000000
--- a/chrome/browser/chromeos/web_socket_proxy.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_WEB_SOCKET_PROXY_H_
-#define CHROME_BROWSER_CHROMEOS_WEB_SOCKET_PROXY_H_
-
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-
-namespace chromeos {
-
-class WebSocketProxy {
- public:
-  static const size_t kBufferLimit = 12 * 1024 * 1024;
-
-  // Limits incoming websocket headers in initial stage of connection.
-  static const size_t kHeaderLimit = 32 * 1024;
-
-  // Limits number of simultaneously open connections.
-  static const size_t kConnPoolLimit = 40;
-
-  WebSocketProxy();
-  ~WebSocketProxy();
-
-  // Do not call it twice.
-  void Run();
-
-  // Terminates running server (should be called on a different thread).
-  void Shutdown();
-
-  // Call this on network change.
-  void OnNetworkChange();
-
- private:
-  void* impl_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebSocketProxy);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_WEB_SOCKET_PROXY_H_
diff --git a/chrome/browser/chromeos/web_socket_proxy_controller.cc b/chrome/browser/chromeos/web_socket_proxy_controller.cc
deleted file mode 100644
index 503b58d..0000000
--- a/chrome/browser/chromeos/web_socket_proxy_controller.cc
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/chromeos/web_socket_proxy_controller.h"
-
-#include <algorithm>
-
-#include <netinet/in.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/command_line.h"
-#include "base/lazy_instance.h"
-#include "base/message_loop/message_loop.h"
-#include "base/threading/thread.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/web_socket_proxy.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/url_constants.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/common/url_constants.h"
-#include "net/base/network_change_notifier.h"
-#include "url/gurl.h"
-
-namespace {
-
-class ProxyLifetime
-    : public net::NetworkChangeNotifier::ConnectionTypeObserver,
-      public content::NotificationObserver {
- public:
-  ProxyLifetime()
-      : delay_ms_(1000),
-        port_(-1),
-        shutdown_requested_(false),
-        web_socket_proxy_thread_("Chrome_WebSocketproxyThread") {
-    DLOG(INFO) << "WebSocketProxyController initiation";
-    base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
-    web_socket_proxy_thread_.StartWithOptions(options);
-    web_socket_proxy_thread_.message_loop()->PostTask(
-        FROM_HERE,
-        base::Bind(&ProxyLifetime::ProxyCallback, base::Unretained(this)));
-    net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
-    registrar_.Add(
-        this, chrome::NOTIFICATION_WEB_SOCKET_PROXY_STARTED,
-        content::NotificationService::AllSources());
-  }
-
-  virtual ~ProxyLifetime() {
-    net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
-  }
-
-  virtual void Observe(int type, const content::NotificationSource& source,
-                       const content::NotificationDetails& details) OVERRIDE {
-    base::AutoLock alk(lock_);
-    port_ = *content::Details<int>(details).ptr();
-  }
-
-  int GetPort() {
-    base::AutoLock alk(lock_);
-    return port_;
-  }
-
- private:
-  // net::NetworkChangeNotifier::ConnectionTypeObserver implementation.
-  virtual void OnConnectionTypeChanged(
-      net::NetworkChangeNotifier::ConnectionType type) OVERRIDE {
-    DCHECK(chromeos::WebSocketProxyController::IsInitiated());
-    base::AutoLock alk(lock_);
-    if (server_)
-      server_->OnNetworkChange();
-  }
-
-  void ProxyCallback() {
-    LOG(INFO) << "Attempt to run web socket proxy task";
-    chromeos::WebSocketProxy* server = new chromeos::WebSocketProxy();
-    {
-      base::AutoLock alk(lock_);
-      if (shutdown_requested_)
-        return;
-      delete server_;
-      server_ = server;
-    }
-    server->Run();
-    {
-      base::AutoLock alk(lock_);
-      delete server;
-      server_ = NULL;
-      if (!shutdown_requested_) {
-        // Proxy terminated unexpectedly or failed to start (it can happen due
-        // to a network problem). Keep trying.
-        if (delay_ms_ < 100 * 1000)
-          (delay_ms_ *= 3) /= 2;
-
-        base::MessageLoop::current()->PostDelayedTask(
-            FROM_HERE,
-            base::Bind(&ProxyLifetime::ProxyCallback, base::Unretained(this)),
-            base::TimeDelta::FromMilliseconds(delay_ms_));
-      }
-    }
-  }
-
-  // Delay in milliseconds between next attempt to run proxy.
-  int volatile delay_ms_;
-
-  // Proxy listens for incoming websocket connections on this port.
-  int volatile port_;
-
-  chromeos::WebSocketProxy* volatile server_;
-  volatile bool shutdown_requested_;
-  base::Lock lock_;
-  content::NotificationRegistrar registrar_;
-  friend class chromeos::WebSocketProxyController;
-  base::Thread web_socket_proxy_thread_;
-};
-
-base::LazyInstance<ProxyLifetime> g_proxy_lifetime = LAZY_INSTANCE_INITIALIZER;
-
-}  // namespace
-
-namespace chromeos {
-
-// static
-void WebSocketProxyController::Initiate() {
-  g_proxy_lifetime.Get();
-}
-
-// static
-bool WebSocketProxyController::IsInitiated() {
-  return !(g_proxy_lifetime == NULL);
-}
-
-// static
-int WebSocketProxyController::GetPort() {
-  int port = g_proxy_lifetime.Get().GetPort();
-  DCHECK(IsInitiated());
-  return port;
-}
-
-// static
-void WebSocketProxyController::Shutdown() {
-  if (!IsInitiated())
-    return;
-
-  DLOG(INFO) << "WebSocketProxyController shutdown";
-  {
-    base::AutoLock alk(g_proxy_lifetime.Get().lock_);
-    g_proxy_lifetime.Get().shutdown_requested_ = true;
-    if (g_proxy_lifetime.Get().server_)
-      g_proxy_lifetime.Get().server_->Shutdown();
-  }
-  g_proxy_lifetime.Get().web_socket_proxy_thread_.Stop();
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/web_socket_proxy_controller.h b/chrome/browser/chromeos/web_socket_proxy_controller.h
deleted file mode 100644
index 4f233f7..0000000
--- a/chrome/browser/chromeos/web_socket_proxy_controller.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_WEB_SOCKET_PROXY_CONTROLLER_H_
-#define CHROME_BROWSER_CHROMEOS_WEB_SOCKET_PROXY_CONTROLLER_H_
-
-#include <string>
-#include <vector>
-
-namespace chromeos {
-
-// Controls webproxy to TCP service.
-class WebSocketProxyController {
- public:
-  enum ConnectionFlags {
-    PLAIN_TCP    = 0,
-    TLS_OVER_TCP = 1 << 0
-  };
-
-  // Can be called on any thread. Subsequent calls are cheap and do nothing.
-  static void Initiate();
-
-  // All methods can be called on any thread.
-  static void Shutdown();
-  static bool IsInitiated();
-  static int GetPort();  // Returns port listening websocket connections.
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_WEB_SOCKET_PROXY_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/web_socket_proxy_helper.cc b/chrome/browser/chromeos/web_socket_proxy_helper.cc
deleted file mode 100644
index c9f9f0a..0000000
--- a/chrome/browser/chromeos/web_socket_proxy_helper.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2011 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.
-
-#include "chrome/browser/chromeos/web_socket_proxy_helper.h"
-
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-
-namespace chromeos {
-
-// static
-bool WebSocketProxyHelper::FetchPassportAddrNamePort(
-      uint8* begin, uint8* end,
-      std::string* passport, std::string* addr,
-      std::string* hostname, int* port) {
-  std::string input(begin, end);
-
-  // At first, get rid of a colon at the end.
-  if (input.empty() || input[input.length() - 1] != ':')
-    return false;
-  input.erase(input.length() - 1);
-
-  // Fetch the passport from the start.
-  if (!FetchToken(true, false, &input, passport) || passport->empty())
-    return false;
-
-  // Fetch the IP address from the start. Match '['-brackets if presented.
-  if (!FetchToken(true, true, &input, addr))
-    return false;
-
-  // Fetch the port number from the end.
-  std::string port_str;
-  if (!FetchToken(false, false, &input, &port_str) || port_str.empty() ||
-      !base::StringToInt(port_str, port) ||
-      *port <= 0 || *port >= (1<<16))
-    return false;
-
-  // The hostname is everything remained.
-  if (input.length() < 2 || input[input.length() - 1] != ':')
-    return false;
-  hostname->assign(input, 0, input.length() - 1);
-
-  return true;
-}
-
-// static
-bool WebSocketProxyHelper::FetchToken(bool forward,
-                                      bool match_brackets,
-                                      std::string* input,
-                                      std::string* token ) {
-  if (input->empty()) {
-    token->clear();
-  } else {
-    std::string separator =
-        (forward && match_brackets && input->at(0) == '[') ? "]:" : ":";
-    size_t pos = forward ? input->find(separator) : input->rfind(separator);
-    if (pos != std::string::npos) {
-      size_t start = forward ? 0 : pos + 1;
-      size_t end = forward ? pos : input->length();
-      token->assign(*input, start, end - start + separator.length() - 1);
-      input->erase(start, end - start + separator.length());
-    } else {
-      return false;
-    }
-  }
-  return true;
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/web_socket_proxy_helper.h b/chrome/browser/chromeos/web_socket_proxy_helper.h
deleted file mode 100644
index 5aac3c1..0000000
--- a/chrome/browser/chromeos/web_socket_proxy_helper.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2011 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.
-
-#ifndef CHROME_BROWSER_CHROMEOS_WEB_SOCKET_PROXY_HELPER_H_
-#define CHROME_BROWSER_CHROMEOS_WEB_SOCKET_PROXY_HELPER_H_
-
-#include <string>
-#include "base/basictypes.h"
-
-namespace chromeos {
-
-// Helper class for WebSocketProxy.
-class WebSocketProxyHelper {
- public:
-  // Parses "passport:addr:hostname:port:" string.  Returns true on success.
-  static bool FetchPassportAddrNamePort(
-      uint8* begin, uint8* end,
-      std::string* passport, std::string* addr,
-      std::string* hostname, int* port);
-
-  // Fetches a token from the string and erases it. Token separtor is
-  // either ':' or ']:' if the token is started with '[' and
-  // |match_brackets|. Fetching position (start or end) is determined by
-  // |forward|. Returns whether the token was successfully fetched.
-  static bool FetchToken(bool forward, bool match_brackets,
-                         std::string* input,
-                         std::string* token);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_WEB_SOCKET_PROXY_HELPER_H_
diff --git a/chrome/browser/chromeos/web_socket_proxy_helper_unittest.cc b/chrome/browser/chromeos/web_socket_proxy_helper_unittest.cc
deleted file mode 100644
index 985616e..0000000
--- a/chrome/browser/chromeos/web_socket_proxy_helper_unittest.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2011 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.
-
-#include <vector>
-#include "base/strings/string_number_conversions.h"
-#include "chrome/browser/chromeos/web_socket_proxy_helper.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chromeos {
-
-class WebSocketProxyHelperTest : public testing::Test {
- public:
-  void FetchAndTest(const std::string& input,
-                    const std::string& passport,
-                    const std::string& ip,
-                    const std::string& hostname,
-                    int port, bool success) {
-    std::string passport_out;
-    std::string ip_out;
-    std::string hostname_out;
-    int port_out;
-    bool result = WebSocketProxyHelper::FetchPassportAddrNamePort(
-        (uint8*)input.data(), (uint8*)input.data() + input.length(),
-        &passport_out, &ip_out, &hostname_out, &port_out);
-    ASSERT_EQ(success, result) << "Input was: " << input;
-    if (success) {
-      EXPECT_EQ(passport, passport_out);
-      EXPECT_EQ(ip, ip_out);
-      EXPECT_EQ(hostname, hostname_out);
-      EXPECT_EQ(port, port_out);
-    }
-  }
-};
-
-TEST_F(WebSocketProxyHelperTest, FetchPassportAddrNamePortSuccess) {
-  std::vector<std::string> ips;
-  ips.push_back("127.0.0.1");
-  ips.push_back("[ab:ab:ab:00:ed:78]");
-  std::vector<std::string> hostnames = ips;
-  hostnames.push_back("www.site.com");
-  hostnames.push_back("localhost");
-  hostnames.push_back("ab:ab:ab:ab:ab:ab");
-  ips.push_back("");  // Also valid ip, but not hostname.
-  std::vector<int> ports;
-  ports.push_back(1);
-  ports.push_back(65535);
-  for (size_t i = 0; i < ips.size(); ++i) {
-    for (size_t j = 0; j < hostnames.size(); ++j) {
-      for (size_t k = 0; k < ports.size(); ++k) {
-        std::string input = "passport:" + ips[i] + ":" +
-            hostnames[j] + ":" + base::IntToString(ports[k]) + ":";
-        FetchAndTest(input, "passport", ips[i], hostnames[j], ports[k], true);
-      }
-    }
-  }
-}
-
-TEST_F(WebSocketProxyHelperTest, FetchPassportAddrNamePortEmptyPassport) {
-  FetchAndTest("::localhost:1:", "", "", "", 0, false);
-}
-
-TEST_F(WebSocketProxyHelperTest, FetchPassportAddrNamePortBadIpv6) {
-  FetchAndTest("passport:[12:localhost:1:", "", "", "", 0, false);
-}
-
-TEST_F(WebSocketProxyHelperTest, FetchPassportAddrNameEmptyHostname) {
-  FetchAndTest("passport:::1:", "", "", "", 0, false);
-}
-
-TEST_F(WebSocketProxyHelperTest, FetchPassportAddrNameSmallPort) {
-  FetchAndTest("passport:::0:", "", "", "", 0, false);
-}
-
-TEST_F(WebSocketProxyHelperTest, FetchPassportAddrNameBigPort) {
-  FetchAndTest("passport:::65536:", "", "", "", 0, false);
-}
-
-TEST_F(WebSocketProxyHelperTest, FetchPassportAddrNoLastColon) {
-  FetchAndTest("passport::localhost:1", "", "", "", 0, false);
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/component_updater/component_unpacker.cc b/chrome/browser/component_updater/component_unpacker.cc
index 744d90f..d47bce8 100644
--- a/chrome/browser/component_updater/component_unpacker.cc
+++ b/chrome/browser/component_updater/component_unpacker.cc
@@ -104,20 +104,6 @@
   return static_cast<base::DictionaryValue*>(root.release());
 }
 
-// Deletes a path if it exists, and then creates a directory there.
-// Returns true if and only if these operations were successful.
-// This method doesn't take any special steps to prevent files from
-// being inserted into the target directory by another process or thread.
-bool MakeEmptyDirectory(const base::FilePath& path) {
-  if (base::PathExists(path)) {
-    if (!base::DeleteFile(path, true))
-      return false;
-  }
-  if (!file_util::CreateDirectory(path))
-    return false;
-  return true;
-}
-
 }  // namespace.
 
 ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash,
@@ -157,23 +143,17 @@
     error_ = kInvalidId;
     return;
   }
-  // We want the temporary directory to be unique and yet predictable, so
-  // we can easily find the package in an end user machine.
-  const std::string dir(
-      base::StringPrintf("CRX_%s", base::HexEncode(hash, 6).c_str()));
-  unpack_path_ = path.DirName().AppendASCII(dir.c_str());
-  if (!MakeEmptyDirectory(unpack_path_)) {
-    unpack_path_.clear();
+  if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""),
+                                         &unpack_path_)) {
     error_ = kUnzipPathError;
     return;
   }
   if (validator.delta()) {  // Package is a diff package.
     // We want a different temp directory for the delta files; we'll put the
     // patch output into unpack_path_.
-    std::string dir(
-        base::StringPrintf("CRX_%s_diff", base::HexEncode(hash, 6).c_str()));
-    base::FilePath unpack_diff_path = path.DirName().AppendASCII(dir.c_str());
-    if (!MakeEmptyDirectory(unpack_diff_path)) {
+    base::FilePath unpack_diff_path;
+    if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""),
+                                           &unpack_diff_path)) {
       error_ = kUnzipPathError;
       return;
     }
diff --git a/chrome/browser/component_updater/component_updater_service.cc b/chrome/browser/component_updater/component_updater_service.cc
index 1a5bc76..f595ba9 100644
--- a/chrome/browser/component_updater/component_updater_service.cc
+++ b/chrome/browser/component_updater/component_updater_service.cc
@@ -219,7 +219,8 @@
 }
 
 CrxComponent::CrxComponent()
-    : installer(NULL) {
+    : installer(NULL),
+      observer(NULL) {
 }
 
 CrxComponent::~CrxComponent() {
@@ -340,6 +341,9 @@
 
   CrxUpdateItem* FindUpdateItemById(const std::string& id);
 
+  void NotifyComponentObservers(ComponentObserver::Events event,
+                                int extra) const;
+
   scoped_ptr<ComponentUpdateService::Configurator> config_;
 
   scoped_ptr<ComponentPatcher> component_patcher_;
@@ -392,6 +396,8 @@
   if (work_items_.empty())
     return kOk;
 
+  NotifyComponentObservers(ComponentObserver::COMPONENT_UPDATER_STARTED, 0);
+
   content::NotificationService::current()->Notify(
     chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
     content::Source<ComponentUpdateService>(this),
@@ -431,6 +437,8 @@
       ? config_->StepDelay() : config_->NextCheckDelay();
 
   if (!step_delay) {
+    NotifyComponentObservers(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0);
+
     content::NotificationService::current()->Notify(
         chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
         content::Source<ComponentUpdateService>(this),
@@ -779,6 +787,11 @@
     crx->next_fp = it->package_fingerprint;
     ++update_pending;
 
+    if (crx->component.observer) {
+      crx->component.observer->OnEvent(
+          ComponentObserver::COMPONENT_UPDATE_FOUND, 0);
+    }
+
     content::NotificationService::current()->Notify(
         chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
         content::Source<std::string>(&crx->id),
@@ -855,6 +868,11 @@
 
     url_fetcher_.reset();
 
+    if (crx->component.observer) {
+      crx->component.observer->OnEvent(
+          ComponentObserver::COMPONENT_UPDATE_READY, 0);
+    }
+
     content::NotificationService::current()->Notify(
         chrome::NOTIFICATION_COMPONENT_UPDATE_READY,
         content::Source<std::string>(&context->id),
@@ -948,6 +966,16 @@
   ScheduleNextRun(false);
 }
 
+void CrxUpdateService::NotifyComponentObservers(
+    ComponentObserver::Events event, int extra) const {
+  for (UpdateItems::const_iterator it = work_items_.begin();
+       it != work_items_.end(); ++it) {
+    ComponentObserver* observer = (*it)->component.observer;
+    if (observer)
+      observer->OnEvent(event, 0);
+  }
+}
+
 // The component update factory. Using the component updater as a singleton
 // is the job of the browser process.
 ComponentUpdateService* ComponentUpdateServiceFactory(
diff --git a/chrome/browser/component_updater/component_updater_service.h b/chrome/browser/component_updater/component_updater_service.h
index 7125ff4..4a02157 100644
--- a/chrome/browser/component_updater/component_updater_service.h
+++ b/chrome/browser/component_updater/component_updater_service.h
@@ -50,14 +50,41 @@
   virtual ~ComponentInstaller() {}
 };
 
+// Defines an interface to observe a CrxComponent.
+class ComponentObserver {
+ public:
+  enum Events {
+    // Sent when the component updater starts doing update checks.
+    COMPONENT_UPDATER_STARTED,
+
+    // Sent when the component updater is going to take a long nap.
+    COMPONENT_UPDATER_SLEEPING,
+
+    // Sent when there is a new version of a registered component. After
+    // the notification is sent the component will be downloaded.
+    COMPONENT_UPDATE_FOUND,
+
+    // Sent when the new component has been downloaded and an installation
+    // or upgrade is about to be attempted.
+    COMPONENT_UPDATE_READY,
+  };
+
+  virtual ~ComponentObserver() {}
+
+  // The component updater service will call this function when an interesting
+  // event happens for a specific component. |extra| is |event| dependent.
+  virtual void OnEvent(Events event, int extra) = 0;
+};
+
 // Describes a particular component that can be installed or updated. This
 // structure is required to register a component with the component updater.
 // |pk_hash| is the SHA256 hash of the component's public key. If the component
 // is to be installed then version should be "0" or "0.0", else it should be
-// the current version. |fingerprint| and |name| are optional.
+// the current version. |observer|, |fingerprint|, and |name| are optional.
 struct CrxComponent {
   std::vector<uint8> pk_hash;
   ComponentInstaller* installer;
+  ComponentObserver* observer;
   Version version;
   std::string fingerprint;
   std::string name;
diff --git a/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc b/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc
index 0e47304..30d2456 100644
--- a/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc
+++ b/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc
@@ -419,6 +419,22 @@
   }
 }
 
+// Remove old per-profile copies of PNaCl (was for ChromeOS).
+// TODO(jvoung): Delete this code once most ChromeOS users have reaped
+// their old per-profile copies of PNaCl.
+void ReapOldChromeOSPnaclFiles(PnaclComponentInstaller* pci) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  base::FilePath path = pci->GetPnaclBaseDirectory();
+  if (!base::PathExists(path))
+    return;
+
+  // Do a basic sanity check first.
+  if (pci->per_user()
+      && path.BaseName().value().compare(FILE_PATH_LITERAL("pnacl")) == 0)
+    base::DeleteFile(path, true);
+}
+
+
 void GetProfileInformation(PnaclComponentInstaller* pci) {
   // Bail if not logged in yet.
   if (!g_browser_process->profile_manager()->IsLoggedIn()) {
@@ -427,12 +443,13 @@
 
   pci->OnProfileChange();
 
-  BrowserThread::PostTask(
-     BrowserThread::FILE, FROM_HERE,
-     base::Bind(&StartPnaclUpdateRegistration, pci));
+  // Do not actually register PNaCl for component updates, for CHROMEOS.
+  // Just get the profile information and delete the per-profile files
+  // if they exist.
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+                         base::Bind(&ReapOldChromeOSPnaclFiles, pci));
 }
 
-
 }  // namespace
 
 void PnaclComponentInstaller::RegisterPnaclComponent(
diff --git a/chrome/browser/component_updater/test/component_updater_service_unittest.cc b/chrome/browser/component_updater/test/component_updater_service_unittest.cc
index b1cf78b..82165b6 100644
--- a/chrome/browser/component_updater/test/component_updater_service_unittest.cc
+++ b/chrome/browser/component_updater/test/component_updater_service_unittest.cc
@@ -24,6 +24,16 @@
 using content::BrowserThread;
 using content::TestNotificationTracker;
 
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Mock;
+
+MockComponentObserver::MockComponentObserver() {
+}
+
+MockComponentObserver::~MockComponentObserver() {
+}
+
 TestConfigurator::TestConfigurator()
     : times_(1),
       recheck_time_(0),
@@ -264,8 +274,11 @@
 TEST_F(ComponentUpdaterTest, CheckCrxSleep) {
   content::URLLocalHostRequestPrepackagedInterceptor interceptor;
 
+  MockComponentObserver observer;
+
   TestInstaller installer;
   CrxComponent com;
+  com.observer = &observer;
   EXPECT_EQ(ComponentUpdateService::kOk,
             RegisterComponent(&com,
                               kTestComponent_abag,
@@ -281,12 +294,19 @@
 
   // We loop twice, but there are no updates so we expect two sleep messages.
   test_configurator()->SetLoopCount(2);
+
+  EXPECT_CALL(observer,
+              OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+              .Times(1);
   component_updater()->Start();
 
   ASSERT_EQ(1ul, notification_tracker().size());
   TestNotificationTracker::Event ev1 = notification_tracker().at(0);
   EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev1.type);
 
+  EXPECT_CALL(observer,
+              OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+              .Times(2);
   message_loop_.Run();
 
   ASSERT_EQ(3ul, notification_tracker().size());
@@ -309,8 +329,15 @@
 
   notification_tracker().Reset();
   test_configurator()->SetLoopCount(2);
+
+  EXPECT_CALL(observer,
+              OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+              .Times(1);
   component_updater()->Start();
 
+  EXPECT_CALL(observer,
+              OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+              .Times(2);
   message_loop_.Run();
 
   ASSERT_EQ(3ul, notification_tracker().size());
@@ -347,11 +374,41 @@
   URLRequestPostInterceptor post_interceptor(&ping_checker);
   content::URLLocalHostRequestPrepackagedInterceptor interceptor;
 
+  MockComponentObserver observer1;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATE_FOUND, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATE_READY, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(2);
+  }
+
+  MockComponentObserver observer2;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(2);
+  }
+
   TestInstaller installer1;
   CrxComponent com1;
+  com1.observer = &observer1;
   RegisterComponent(&com1, kTestComponent_jebg, Version("0.9"), &installer1);
   TestInstaller installer2;
   CrxComponent com2;
+  com2.observer = &observer2;
   RegisterComponent(&com2, kTestComponent_abag, Version("2.2"), &installer2);
 
   const GURL expected_update_url_1(
@@ -387,6 +444,9 @@
 
   ASSERT_EQ(5ul, notification_tracker().size());
 
+  TestNotificationTracker::Event ev0 = notification_tracker().at(0);
+  EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED, ev0.type);
+
   TestNotificationTracker::Event ev1 = notification_tracker().at(1);
   EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND, ev1.type);
 
@@ -454,11 +514,47 @@
   URLRequestPostInterceptor post_interceptor(&ping_checker);
   content::URLLocalHostRequestPrepackagedInterceptor interceptor;
 
+  MockComponentObserver observer1;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+  }
+
+  MockComponentObserver observer2;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATE_FOUND, 0))
+                .Times(1);
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATE_READY, 0))
+                .Times(1);
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+  }
+
   TestInstaller installer1;
   CrxComponent com1;
+  com1.observer = &observer1;
   RegisterComponent(&com1, kTestComponent_abag, Version("2.2"), &installer1);
   TestInstaller installer2;
   CrxComponent com2;
+  com2.observer = &observer2;
   RegisterComponent(&com2, kTestComponent_jebg, Version("0.9"), &installer2);
 
   const GURL expected_update_url_1(
@@ -518,6 +614,27 @@
 
   // Test a few error cases. NOTE: We don't have callbacks for
   // when the updates failed yet.
+  EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer1));
+  {
+    InSequence seq;
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+  }
+  EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer2));
+  {
+    InSequence seq;
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+  }
+
   const GURL expected_update_url_3(
       "http://localhost/upd?extra=foo"
       "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26fp%3D%26uc"
@@ -542,6 +659,27 @@
   component_updater()->Stop();
 
   // No update: already updated to 1.0 so nothing new
+  EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer1));
+  {
+    InSequence seq;
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+  }
+  EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer2));
+  {
+    InSequence seq;
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+  }
+
   interceptor.SetResponse(expected_update_url_3,
                           test_file("updatecheck_reply_1.xml"));
   notification_tracker().Reset();
@@ -575,11 +713,47 @@
   URLRequestPostInterceptor post_interceptor(&ping_checker);
   content::URLLocalHostRequestPrepackagedInterceptor interceptor;
 
+  MockComponentObserver observer1;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATE_FOUND, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATE_READY, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+  }
+
+  MockComponentObserver observer2;
+  {
+    InSequence seq;
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+  }
+
   TestInstaller installer1;
   CrxComponent com1;
+  com1.observer = &observer1;
   RegisterComponent(&com1, kTestComponent_jebg, Version("0.9"), &installer1);
   TestInstaller installer2;
   CrxComponent com2;
+  com2.observer = &observer2;
   RegisterComponent(&com2, kTestComponent_abag, Version("2.2"), &installer2);
 
   // Start with 0.9, and update to 1.0
@@ -633,9 +807,32 @@
   TestNotificationTracker::Event ev4 = notification_tracker().at(4);
   EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev4.type);
 
-  // Now re-register, pretending to be an even newer version (2.2)
-  TestInstaller installer3;
   component_updater()->Stop();
+
+  // Now re-register, pretending to be an even newer version (2.2)
+  EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer1));
+  {
+    InSequence seq;
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer1,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+  }
+
+  EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer2));
+  {
+    InSequence seq;
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_STARTED, 0))
+                .Times(1);
+    EXPECT_CALL(observer2,
+                OnEvent(ComponentObserver::COMPONENT_UPDATER_SLEEPING, 0))
+                .Times(1);
+  }
+
+  TestInstaller installer3;
   EXPECT_EQ(ComponentUpdateService::kReplaced,
             RegisterComponent(&com1,
                               kTestComponent_jebg,
@@ -755,14 +952,8 @@
 // 3- download full crx
 // 4- update check (loop 2 - no update available)
 // There should be one ping for the first attempted update.
-// Fails on Window. http://crbug.com/265840
-#if defined(OS_WIN)
-#define MAYBE_DifferentialUpdateFails DISABLED_DifferentialUpdateFails
-#else
-#define MAYBE_DifferentialUpdateFails DifferentialUpdateFails
-#endif
 
-TEST_F(ComponentUpdaterTest, MAYBE_DifferentialUpdateFails) {
+TEST_F(ComponentUpdaterTest, DifferentialUpdateFails) {
 
   std::map<std::string, std::string> map;
   map.insert(std::pair<std::string, std::string>("eventtype", "\"3\""));
diff --git a/chrome/browser/component_updater/test/component_updater_service_unittest.h b/chrome/browser/component_updater/test/component_updater_service_unittest.h
index ca3a7b8..4169c0a 100644
--- a/chrome/browser/component_updater/test/component_updater_service_unittest.h
+++ b/chrome/browser/component_updater/test/component_updater_service_unittest.h
@@ -21,6 +21,7 @@
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_notification_tracker.h"
 #include "net/url_request/url_request_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class TestInstaller;
@@ -164,4 +165,11 @@
   virtual bool Test(net::URLRequest* request);
 };
 
+class MockComponentObserver : public ComponentObserver {
+ public:
+  MockComponentObserver();
+  ~MockComponentObserver();
+  MOCK_METHOD2(OnEvent, void(Events event, int extra));
+};
+
 #endif  // CHROME_BROWSER_COMPONENT_UPDATER_TEST_COMPONENT_UPDATER_SERVICE_UNITTEST_H_
diff --git a/chrome/browser/content_settings/content_settings_browsertest.cc b/chrome/browser/content_settings/content_settings_browsertest.cc
index f13eea9..799fb73 100644
--- a/chrome/browser/content_settings/content_settings_browsertest.cc
+++ b/chrome/browser/content_settings/content_settings_browsertest.cc
@@ -19,6 +19,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/plugin_service.h"
@@ -37,10 +38,6 @@
 #include "base/mac/scoped_nsautorelease_pool.h"
 #endif
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::BrowserThread;
 using content::URLRequestMockHTTPJob;
 
@@ -568,7 +565,7 @@
 IN_PROC_BROWSER_TEST_F(PepperContentSettingsTest, PluginSpecialCases) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/content_settings/permission_queue_controller.cc b/chrome/browser/content_settings/permission_queue_controller.cc
index 4b471b8..3a8dff9 100644
--- a/chrome/browser/content_settings/permission_queue_controller.cc
+++ b/chrome/browser/content_settings/permission_queue_controller.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/geolocation/geolocation_infobar_delegate.h"
 #include "chrome/browser/infobars/infobar.h"
 #include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/media/midi_permission_infobar_delegate.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "chrome/common/content_settings.h"
@@ -34,7 +35,8 @@
 
 class PermissionQueueController::PendingInfoBarRequest {
  public:
-  PendingInfoBarRequest(const PermissionRequestID& id,
+  PendingInfoBarRequest(ContentSettingsType type,
+                        const PermissionRequestID& id,
                         const GURL& requesting_frame,
                         const GURL& embedder,
                         PermissionDecidedCallback callback);
@@ -53,6 +55,7 @@
                      const std::string& display_languages);
 
  private:
+  ContentSettingsType type_;
   PermissionRequestID id_;
   GURL requesting_frame_;
   GURL embedder_;
@@ -63,11 +66,13 @@
 };
 
 PermissionQueueController::PendingInfoBarRequest::PendingInfoBarRequest(
+    ContentSettingsType type,
     const PermissionRequestID& id,
     const GURL& requesting_frame,
     const GURL& embedder,
     PermissionDecidedCallback callback)
-    : id_(id),
+    : type_(type),
+      id_(id),
       requesting_frame_(requesting_frame),
       embedder_(embedder),
       callback_(callback),
@@ -91,10 +96,24 @@
 void PermissionQueueController::PendingInfoBarRequest::CreateInfoBar(
     PermissionQueueController* controller,
     const std::string& display_languages) {
-  // TODO(toyoshim): Remove following dependency on geolocation.
-  infobar_ = GeolocationInfoBarDelegate::Create(
-      GetInfoBarService(id_), controller, id_, requesting_frame_,
-      display_languages);
+  // TODO(toyoshim): Remove following ContentType dependent code.
+  // Also these InfoBarDelegate can share much more code each other.
+  // http://crbug.com/266743
+  switch (type_) {
+    case CONTENT_SETTINGS_TYPE_GEOLOCATION:
+      infobar_ = GeolocationInfoBarDelegate::Create(
+          GetInfoBarService(id_), controller, id_, requesting_frame_,
+          display_languages);
+      break;
+    case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
+      infobar_ = MIDIPermissionInfoBarDelegate::Create(
+          GetInfoBarService(id_), controller, id_, requesting_frame_,
+          display_languages);
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
 }
 
 
@@ -126,7 +145,7 @@
     DCHECK(!i->id().Equals(id));
 
   pending_infobar_requests_.push_back(PendingInfoBarRequest(
-      id, requesting_frame, embedder, callback));
+      type_, id, requesting_frame, embedder, callback));
   if (!AlreadyShowingInfoBarForTab(id))
     ShowQueuedInfoBarForTab(id);
 }
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.cc b/chrome/browser/content_settings/tab_specific_content_settings.cc
index 7b6d95b..f16589e 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings.cc
@@ -80,6 +80,7 @@
       allowed_local_shared_objects_(profile_),
       blocked_local_shared_objects_(profile_),
       geolocation_usages_state_(profile_, CONTENT_SETTINGS_TYPE_GEOLOCATION),
+      midi_usages_state_(profile_, CONTENT_SETTINGS_TYPE_MIDI_SYSEX),
       pending_protocol_handler_(ProtocolHandler::EmptyProtocolHandler()),
       previous_protocol_handler_(ProtocolHandler::EmptyProtocolHandler()),
       pending_protocol_handler_setting_(CONTENT_SETTING_DEFAULT),
@@ -211,7 +212,8 @@
       content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
       content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA ||
       content_type == CONTENT_SETTINGS_TYPE_PPAPI_BROKER ||
-      content_type == CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS) {
+      content_type == CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS ||
+      content_type == CONTENT_SETTINGS_TYPE_MIDI_SYSEX) {
     return content_blocked_[content_type];
   }
 
@@ -237,7 +239,8 @@
       content_type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC &&
       content_type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA &&
       content_type != CONTENT_SETTINGS_TYPE_PPAPI_BROKER &&
-      content_type != CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS) {
+      content_type != CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS &&
+      content_type != CONTENT_SETTINGS_TYPE_MIDI_SYSEX) {
     return false;
   }
 
@@ -496,6 +499,22 @@
   OnContentBlocked(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string());
 }
 
+void TabSpecificContentSettings::OnMIDISysExAccessed(
+    const GURL& requesting_origin) {
+  midi_usages_state_.OnPermissionSet(requesting_origin, true);
+  // TODO(toyoshim): Bubble icon for MIDI is disabled for now.
+  // http://crbug.com/257618
+  // OnContentAllowed(CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
+}
+
+void TabSpecificContentSettings::OnMIDISysExAccessBlocked(
+    const GURL& requesting_origin) {
+  midi_usages_state_.OnPermissionSet(requesting_origin, false);
+  // TODO(toyoshim): Bubble icon for MIDI is disabled for now.
+  // http://crbug.com/257618
+  // OnContentBlocked(CONTENT_SETTINGS_TYPE_MIDI_SYSEX, std::string());
+}
+
 void TabSpecificContentSettings::ClearBlockedContentSettingsExceptForCookies() {
   for (size_t i = 0; i < arraysize(content_blocked_); ++i) {
     if (i == CONTENT_SETTINGS_TYPE_COOKIES)
@@ -549,10 +568,19 @@
   geolocation_usages_state_.DidNavigate(details);
 }
 
+void TabSpecificContentSettings::MIDIDidNavigate(
+    const content::LoadCommittedDetails& details) {
+  midi_usages_state_.DidNavigate(details);
+}
+
 void TabSpecificContentSettings::ClearGeolocationContentSettings() {
   geolocation_usages_state_.ClearStateMap();
 }
 
+void TabSpecificContentSettings::ClearMIDIContentSettings() {
+  midi_usages_state_.ClearStateMap();
+}
+
 void TabSpecificContentSettings::SetPepperBrokerAllowed(bool allowed) {
   if (allowed) {
     OnContentAllowed(CONTENT_SETTINGS_TYPE_PPAPI_BROKER);
@@ -585,6 +613,7 @@
     // Clear "blocked" flags.
     ClearBlockedContentSettingsExceptForCookies();
     GeolocationDidNavigate(details);
+    MIDIDidNavigate(details);
   }
 }
 
@@ -605,6 +634,7 @@
   if (!is_error_page)
     ClearCookieSpecificContentSettings();
   ClearGeolocationContentSettings();
+  ClearMIDIContentSettings();
 }
 
 void TabSpecificContentSettings::AppCacheAccessed(const GURL& manifest_url,
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.h b/chrome/browser/content_settings/tab_specific_content_settings.h
index 1cd3147..072b9da 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.h
+++ b/chrome/browser/content_settings/tab_specific_content_settings.h
@@ -159,6 +159,9 @@
   // Clears the Geolocation settings.
   void ClearGeolocationContentSettings();
 
+  // Clears the MIDI settings.
+  void ClearMIDIContentSettings();
+
   // Changes the |content_blocked_| entry for popups.
   void SetPopupsBlocked(bool blocked);
 
@@ -169,6 +172,9 @@
   void GeolocationDidNavigate(
       const content::LoadCommittedDetails& details);
 
+  // Updates MIDI settings on navigation.
+  void MIDIDidNavigate(const content::LoadCommittedDetails& details);
+
   // Returns whether a particular kind of content has been blocked for this
   // page.
   bool IsContentBlocked(ContentSettingsType content_type) const;
@@ -194,6 +200,12 @@
     return geolocation_usages_state_;
   }
 
+  // Returns the ContentSettingsUsageState that controls the MIDI usage on
+  // this page.
+  const ContentSettingsUsagesState& midi_usages_state() const {
+    return midi_usages_state_;
+  }
+
   // Call to indicate that there is a protocol handler pending user approval.
   void set_pending_protocol_handler(const ProtocolHandler& handler) {
     pending_protocol_handler_ = handler;
@@ -298,7 +310,6 @@
   void OnGeolocationPermissionSet(const GURL& requesting_frame,
                                   bool allowed);
 
-
   // These methods are called to update the status about the microphone and
   // camera stream access.
   void OnMicrophoneAccessed();
@@ -306,6 +317,10 @@
   void OnCameraAccessed();
   void OnCameraAccessBlocked();
 
+  // There methods are called to update the status about MIDI access.
+  void OnMIDISysExAccessed(const GURL& reqesting_origin);
+  void OnMIDISysExAccessBlocked(const GURL& requesting_origin);
+
   // Adds the given |SiteDataObserver|. The |observer| is notified when a
   // locale shared object, like for example a cookie, is accessed.
   void AddSiteDataObserver(SiteDataObserver* observer);
@@ -355,6 +370,9 @@
   // Manages information about Geolocation API usage in this page.
   ContentSettingsUsagesState geolocation_usages_state_;
 
+  // Manages information about MIDI usages in this page.
+  ContentSettingsUsagesState midi_usages_state_;
+
   // The pending protocol handler, if any. This can be set if
   // registerProtocolHandler was invoked without user gesture.
   // The |IsEmpty| method will be true if no protocol handler is
diff --git a/chrome/browser/devtools/adb/android_usb_device.cc b/chrome/browser/devtools/adb/android_usb_device.cc
index 36965ed..c6f537c 100644
--- a/chrome/browser/devtools/adb/android_usb_device.cc
+++ b/chrome/browser/devtools/adb/android_usb_device.cc
@@ -149,11 +149,11 @@
   callback.Run(devices);
 }
 
-static void EnumerateOnFileThread(UsbService* service,
-                                  crypto::RSAPrivateKey* rsa_key,
+static void EnumerateOnFileThread(crypto::RSAPrivateKey* rsa_key,
                                   const AndroidUsbDevicesCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
 
+  UsbService* service = UsbService::GetInstance();
   AndroidUsbDevices& devices = g_devices.Get();
 
   UsbDevices usb_devices;
@@ -207,10 +207,9 @@
 void AndroidUsbDevice::Enumerate(crypto::RSAPrivateKey* rsa_key,
                                  const AndroidUsbDevicesCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  UsbService* service = UsbService::GetInstance();
   BrowserThread::PostTask(
       BrowserThread::FILE, FROM_HERE,
-      base::Bind(&EnumerateOnFileThread, service, rsa_key, callback));
+      base::Bind(&EnumerateOnFileThread, rsa_key, callback));
 }
 
 AndroidUsbDevice::AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key,
diff --git a/chrome/browser/devtools/adb_web_socket.cc b/chrome/browser/devtools/adb_web_socket.cc
new file mode 100644
index 0000000..2d346ef
--- /dev/null
+++ b/chrome/browser/devtools/adb_web_socket.cc
@@ -0,0 +1,179 @@
+// Copyright (c) 2013 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.
+
+#include "chrome/browser/devtools/adb_web_socket.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/net_errors.h"
+#include "net/server/web_socket.h"
+
+using content::BrowserThread;
+using net::WebSocket;
+
+const int kBufferSize = 16 * 1024;
+
+static const char kWebSocketUpgradeRequest[] = "GET %s HTTP/1.1\r\n"
+    "Upgrade: WebSocket\r\n"
+    "Connection: Upgrade\r\n"
+    "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
+    "Sec-WebSocket-Version: 13\r\n"
+    "\r\n";
+
+AdbWebSocket::AdbWebSocket(
+    scoped_refptr<DevToolsAdbBridge::AndroidDevice> device,
+    const std::string& socket_name,
+    const std::string& url,
+    base::MessageLoop* adb_message_loop,
+    Delegate* delegate)
+    : device_(device),
+      socket_name_(socket_name),
+      url_(url),
+      adb_message_loop_(adb_message_loop),
+      delegate_(delegate) {
+  adb_message_loop_->PostTask(
+      FROM_HERE, base::Bind(&AdbWebSocket::ConnectOnHandlerThread, this));
+}
+
+void AdbWebSocket::Disconnect() {
+  adb_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&AdbWebSocket::DisconnectOnHandlerThread, this, false));
+  adb_message_loop_ = NULL;
+}
+
+void AdbWebSocket::SendFrame(const std::string& message) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  adb_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&AdbWebSocket::SendFrameOnHandlerThread, this, message));
+}
+
+AdbWebSocket::~AdbWebSocket() {}
+
+void AdbWebSocket::ConnectOnHandlerThread() {
+  device_->HttpQuery(
+      socket_name_,
+      base::StringPrintf(kWebSocketUpgradeRequest, url_.c_str()),
+      base::Bind(&AdbWebSocket::ConnectedOnHandlerThread, this));
+}
+
+void AdbWebSocket::ConnectedOnHandlerThread(
+  int result, net::StreamSocket* socket) {
+  if (result != net::OK || socket == NULL) {
+    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+        base::Bind(&AdbWebSocket::OnSocketClosed, this, true));
+    return;
+  }
+  socket_.reset(socket);
+  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+      base::Bind(&AdbWebSocket::OnSocketOpened, this));
+  StartListeningOnHandlerThread();
+}
+
+void AdbWebSocket::StartListeningOnHandlerThread() {
+  scoped_refptr<net::IOBuffer> response_buffer =
+      new net::IOBuffer(kBufferSize);
+  int result = socket_->Read(
+      response_buffer.get(),
+      kBufferSize,
+      base::Bind(&AdbWebSocket::OnBytesRead, this, response_buffer));
+  if (result != net::ERR_IO_PENDING)
+    OnBytesRead(response_buffer, result);
+}
+
+void AdbWebSocket::OnBytesRead(
+    scoped_refptr<net::IOBuffer> response_buffer, int result) {
+  if (!socket_)
+    return;
+
+  if (result <= 0) {
+    DisconnectOnHandlerThread(true);
+    return;
+  }
+
+  std::string data = std::string(response_buffer->data(), result);
+  response_buffer_ += data;
+
+  int bytes_consumed;
+  std::string output;
+  WebSocket::ParseResult parse_result = WebSocket::DecodeFrameHybi17(
+      response_buffer_, false, &bytes_consumed, &output);
+
+  while (parse_result == WebSocket::FRAME_OK) {
+    response_buffer_ = response_buffer_.substr(bytes_consumed);
+    if (!delegate_ || !delegate_->ProcessIncomingMessage(output)) {
+      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+          base::Bind(&AdbWebSocket::OnFrameRead, this, output));
+    }
+    parse_result = WebSocket::DecodeFrameHybi17(
+        response_buffer_, false, &bytes_consumed, &output);
+  }
+
+  if (parse_result == WebSocket::FRAME_ERROR ||
+      parse_result == WebSocket::FRAME_CLOSE) {
+    DisconnectOnHandlerThread(true);
+    return;
+  }
+
+  result = socket_->Read(
+      response_buffer.get(),
+      kBufferSize,
+      base::Bind(&AdbWebSocket::OnBytesRead, this, response_buffer));
+  if (result != net::ERR_IO_PENDING)
+    OnBytesRead(response_buffer, result);
+}
+
+void AdbWebSocket::SendFrameOnHandlerThread(const std::string& data) {
+  delegate_->ProcessOutgoingMessage(data);
+  int mask = base::RandInt(0, 0x7FFFFFFF);
+  std::string encoded_frame = WebSocket::EncodeFrameHybi17(data, mask);
+  request_buffer_ += encoded_frame;
+  if (request_buffer_.length() == encoded_frame.length())
+    SendPendingRequests(0);
+}
+
+void AdbWebSocket::SendPendingRequests(int result) {
+  if (!socket_)
+    return;
+  if (result < 0) {
+    DisconnectOnHandlerThread(true);
+    return;
+  }
+  request_buffer_ = request_buffer_.substr(result);
+  if (request_buffer_.empty())
+    return;
+
+  scoped_refptr<net::StringIOBuffer> buffer =
+      new net::StringIOBuffer(request_buffer_);
+  result = socket_->Write(buffer.get(), buffer->size(),
+                          base::Bind(&AdbWebSocket::SendPendingRequests,
+                                     this));
+  if (result != net::ERR_IO_PENDING)
+    SendPendingRequests(result);
+}
+
+void AdbWebSocket::DisconnectOnHandlerThread(bool closed_by_device) {
+  if (!socket_)
+    return;
+  // Wipe out socket_ first since Disconnect can re-enter this method.
+  scoped_ptr<net::StreamSocket> socket(socket_.release());
+  socket->Disconnect();
+  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+      base::Bind(&AdbWebSocket::OnSocketClosed, this, closed_by_device));
+}
+
+void AdbWebSocket::OnSocketOpened() {
+  delegate_->OnSocketOpened();
+}
+
+void AdbWebSocket::OnFrameRead(const std::string& message) {
+  delegate_->OnFrameRead(message);
+}
+
+void AdbWebSocket::OnSocketClosed(bool closed_by_device) {
+  delegate_->OnSocketClosed(closed_by_device);
+}
diff --git a/chrome/browser/devtools/adb_web_socket.h b/chrome/browser/devtools/adb_web_socket.h
new file mode 100644
index 0000000..62536aa
--- /dev/null
+++ b/chrome/browser/devtools/adb_web_socket.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2013 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.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_ADB_WEB_SOCKET_H_
+#define CHROME_BROWSER_DEVTOOLS_ADB_WEB_SOCKET_H_
+
+#include "chrome/browser/devtools/devtools_adb_bridge.h"
+
+class AdbWebSocket : public base::RefCountedThreadSafe<AdbWebSocket> {
+ public:
+  class Delegate {
+   public:
+    virtual void OnSocketOpened() = 0;
+    virtual void OnFrameRead(const std::string& message) = 0;
+    virtual void OnSocketClosed(bool closed_by_device) = 0;
+    virtual bool ProcessIncomingMessage(const std::string& message) = 0;
+    virtual void ProcessOutgoingMessage(const std::string& message) = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  AdbWebSocket(scoped_refptr<DevToolsAdbBridge::AndroidDevice> device,
+               const std::string& socket_name,
+               const std::string& url,
+               base::MessageLoop* adb_message_loop,
+               Delegate* delegate);
+
+  void Disconnect();
+
+  void SendFrame(const std::string& message);
+
+ private:
+  friend class base::RefCountedThreadSafe<AdbWebSocket>;
+
+  virtual ~AdbWebSocket();
+
+  void ConnectOnHandlerThread();
+  void ConnectedOnHandlerThread(int result, net::StreamSocket* socket);
+  void StartListeningOnHandlerThread();
+  void OnBytesRead(scoped_refptr<net::IOBuffer> response_buffer, int result);
+  void SendFrameOnHandlerThread(const std::string& data);
+  void SendPendingRequests(int result);
+  void DisconnectOnHandlerThread(bool closed_by_device);
+
+  void OnSocketOpened();
+  void OnFrameRead(const std::string& message);
+  void OnSocketClosed(bool closed_by_device);
+
+  scoped_refptr<DevToolsAdbBridge::AndroidDevice> device_;
+  std::string socket_name_;
+  std::string url_;
+  base::MessageLoop* adb_message_loop_;
+  scoped_ptr<net::StreamSocket> socket_;
+  Delegate* delegate_;
+  std::string response_buffer_;
+  std::string request_buffer_;
+  DISALLOW_COPY_AND_ASSIGN(AdbWebSocket);
+};
+
+#endif  // CHROME_BROWSER_DEVTOOLS_ADB_WEB_SOCKET_H_
diff --git a/chrome/browser/devtools/devtools_adb_bridge.cc b/chrome/browser/devtools/devtools_adb_bridge.cc
index 00dc3bd..f82655b 100644
--- a/chrome/browser/devtools/devtools_adb_bridge.cc
+++ b/chrome/browser/devtools/devtools_adb_bridge.cc
@@ -16,7 +16,6 @@
 #include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "base/message_loop/message_loop.h"
-#include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -24,6 +23,7 @@
 #include "base/values.h"
 #include "chrome/browser/devtools/adb/android_rsa.h"
 #include "chrome/browser/devtools/adb_client_socket.h"
+#include "chrome/browser/devtools/adb_web_socket.h"
 #include "chrome/browser/devtools/devtools_window.h"
 #include "chrome/browser/devtools/tethering_adb_filter.h"
 #include "chrome/browser/profiles/profile.h"
@@ -36,10 +36,8 @@
 #include "content/public/browser/devtools_manager.h"
 #include "crypto/rsa_private_key.h"
 #include "net/base/net_errors.h"
-#include "net/server/web_socket.h"
 
 using content::BrowserThread;
-using net::WebSocket;
 
 namespace {
 
@@ -53,18 +51,11 @@
 
 static const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n";
 static const char kVersionRequest[] = "GET /json/version HTTP/1.1\r\n\r\n";
-static const char kWebSocketUpgradeRequest[] = "GET %s HTTP/1.1\r\n"
-    "Upgrade: WebSocket\r\n"
-    "Connection: Upgrade\r\n"
-    "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
-    "Sec-WebSocket-Version: 13\r\n"
-    "\r\n";
 const int kAdbPort = 5037;
 const int kBufferSize = 16 * 1024;
 const int kAdbPollingIntervalMs = 1000;
 
 typedef DevToolsAdbBridge::Callback Callback;
-typedef DevToolsAdbBridge::PagesCallback PagesCallback;
 typedef std::vector<scoped_refptr<DevToolsAdbBridge::AndroidDevice> >
     AndroidDevices;
 typedef base::Callback<void(const AndroidDevices&)> AndroidDevicesCallback;
@@ -164,48 +155,59 @@
   scoped_refptr<AndroidUsbDevice> device_;
 };
 
-class AdbQueryCommand : public base::RefCounted<AdbQueryCommand> {
+class AdbDevicesCommand : public base::RefCountedThreadSafe<
+    AdbDevicesCommand,
+    content::BrowserThread::DeleteOnUIThread> {
  public:
-  AdbQueryCommand(const std::string& query,
-                  const Callback& callback)
-      : query_(query),
-        callback_(callback) {
-  }
-
-  void Run() {
-    AdbClientSocket::AdbQuery(kAdbPort, query_,
-                              base::Bind(&AdbQueryCommand::Handle, this));
+  AdbDevicesCommand(DevToolsAdbBridge* bridge,
+                    const AndroidDevicesCallback& callback)
+     : bridge_(bridge),
+       callback_(callback) {
+    bridge_->EnumerateUsbDevices(
+        base::Bind(&AdbDevicesCommand::ReceivedUsbDevices, this));
   }
 
  private:
-  friend class base::RefCounted<AdbQueryCommand>;
-  virtual ~AdbQueryCommand() {}
+  friend struct content::BrowserThread::DeleteOnThread<
+      content::BrowserThread::UI>;
+  friend class base::DeleteHelper<AdbDevicesCommand>;
 
-  void Handle(int result, const std::string& response) {
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::Bind(&AdbQueryCommand::Respond, this, result, response));
+  virtual ~AdbDevicesCommand() {
+    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   }
 
-  void Respond(int result, const std::string& response) {
-    callback_.Run(result, response);
+  void ReceivedUsbDevices(const AndroidDevices& usb_devices) {
+    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    bridge_->GetAdbMessageLoop()->PostTask(FROM_HERE,
+        base::Bind(&DevToolsAdbBridge::EnumerateAdbDevices, bridge_,
+                   base::Bind(&AdbDevicesCommand::ReceivedAdbDevices,
+                              this,
+                              usb_devices)));
   }
 
-  std::string query_;
-  Callback callback_;
+  void ReceivedAdbDevices(const AndroidDevices& usb_devices,
+                          const AndroidDevices& adb_devices) {
+    AndroidDevices devices(usb_devices);
+    devices.insert(devices.end(), adb_devices.begin(), adb_devices.end());
+    callback_.Run(devices);
+  }
+
+  scoped_refptr<DevToolsAdbBridge> bridge_;
+  AndroidDevicesCallback callback_;
 };
 
 class AdbPagesCommand : public base::RefCountedThreadSafe<
     AdbPagesCommand,
     content::BrowserThread::DeleteOnUIThread> {
  public:
-  explicit AdbPagesCommand(DevToolsAdbBridge* bridge,
-                           const PagesCallback& callback)
+  typedef base::Callback<void(DevToolsAdbBridge::RemoteDevices*)> Callback;
+
+  AdbPagesCommand(DevToolsAdbBridge* bridge, const Callback& callback)
      : bridge_(bridge),
        callback_(callback) {
-    pages_.reset(new DevToolsAdbBridge::RemotePages());
-    bridge_->EnumerateUsbDevices(
-        base::Bind(&AdbPagesCommand::ReceivedUsbDevices, this));
+    remote_devices_.reset(new DevToolsAdbBridge::RemoteDevices());
+    new AdbDevicesCommand(bridge,
+        base::Bind(&AdbPagesCommand::ReceivedDevices, this));
   }
 
  private:
@@ -217,16 +219,8 @@
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   }
 
-  void ReceivedUsbDevices(const AndroidDevices& devices) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  void ReceivedDevices(const AndroidDevices& devices) {
     devices_ = devices;
-    bridge_->GetAdbMessageLoop()->PostTask(FROM_HERE,
-        base::Bind(&DevToolsAdbBridge::EnumerateAdbDevices, bridge_,
-                   base::Bind(&AdbPagesCommand::ReceivedAdbDevices, this)));
-  }
-
-  void ReceivedAdbDevices(const AndroidDevices& devices) {
-    devices_.insert(devices_.end(), devices.begin(), devices.end());
     ProcessSerials();
   }
 
@@ -246,6 +240,8 @@
           devices_.back();
       sockets_.push_back(std::string());
       device->set_model(kUnknownModel);
+      remote_devices_->push_back(
+          new DevToolsAdbBridge::RemoteDevice(bridge_, device));
       device->HttpQuery(
           std::string(), kVersionRequest,
           base::Bind(&AdbPagesCommand::ReceivedVersion, this));
@@ -267,6 +263,8 @@
     }
     scoped_refptr<DevToolsAdbBridge::AndroidDevice> device = devices_.back();
     device->set_model(response);
+    remote_devices_->push_back(
+        new DevToolsAdbBridge::RemoteDevice(bridge_, device));
     device->RunCommand(kOpenedUnixSocketsCommand,
                        base::Bind(&AdbPagesCommand::ReceivedSockets, this));
   }
@@ -348,20 +346,25 @@
 
     scoped_refptr<DevToolsAdbBridge::AndroidDevice> device = devices_.back();
     base::Value* item;
+
+    scoped_refptr<DevToolsAdbBridge::RemoteBrowser> remote_browser =
+        new DevToolsAdbBridge::RemoteBrowser(
+            bridge_, device, socket, socket_to_package_[socket]);
+    remote_devices_->back()->AddBrowser(remote_browser);
+
     for (size_t i = 0; i < list_value->GetSize(); ++i) {
       list_value->Get(i, &item);
       base::DictionaryValue* dict;
       if (!item || !item->GetAsDictionary(&dict))
         continue;
-      pages_->push_back(
-          new DevToolsAdbBridge::RemotePage(
-              device, socket_to_package_[socket], socket, *dict));
+      remote_browser->AddPage(new DevToolsAdbBridge::RemotePage(
+          bridge_, device, remote_browser->socket(), *dict));
     }
     ProcessSockets();
   }
 
   void Respond() {
-    callback_.Run(net::OK, pages_.release());
+    callback_.Run(remote_devices_.release());
   }
 
   void ParseSocketsList(const std::string& response) {
@@ -407,11 +410,11 @@
   }
 
   scoped_refptr<DevToolsAdbBridge> bridge_;
-  PagesCallback callback_;
+  Callback callback_;
   AndroidDevices devices_;
   std::vector<std::string> sockets_;
   std::map<std::string, std::string> socket_to_package_;
-  scoped_ptr<DevToolsAdbBridge::RemotePages> pages_;
+  scoped_ptr<DevToolsAdbBridge::RemoteDevices> remote_devices_;
 };
 
 }  // namespace
@@ -510,193 +513,6 @@
   AdbClientSocket::HttpQuery(socket, request, callback);
 }
 
-class AdbWebSocket : public base::RefCountedThreadSafe<AdbWebSocket> {
- public:
-  class Delegate {
-   public:
-    virtual void OnSocketOpened() = 0;
-
-    virtual void OnFrameRead(const std::string& message) = 0;
-
-    virtual void OnSocketClosed(bool closed_by_device) = 0;
-
-    virtual bool ProcessIncomingMessage(const std::string& message) = 0;
-
-    virtual void ProcessOutgoingMessage(const std::string& message) = 0;
-
-   protected:
-    virtual ~Delegate() {}
-  };
-
-  AdbWebSocket(
-      scoped_refptr<DevToolsAdbBridge::AndroidDevice> device,
-      const std::string& socket_name,
-      const std::string& url,
-      scoped_refptr<DevToolsAdbBridge::RefCountedAdbThread> adb_thread,
-      Delegate* delegate)
-      : device_(device),
-        socket_name_(socket_name),
-        url_(url),
-        adb_thread_(adb_thread),
-        delegate_(delegate) {
-  }
-
-  void Connect() {
-    adb_thread_->message_loop()->PostTask(
-        FROM_HERE, base::Bind(&AdbWebSocket::ConnectOnHandlerThread, this));
-  }
-
-  void Disconnect() {
-    adb_thread_->message_loop()->PostTask(
-        FROM_HERE,
-        base::Bind(&AdbWebSocket::DisconnectOnHandlerThread, this, false));
-  }
-
-  void SendFrame(const std::string& message) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-    adb_thread_->message_loop()->PostTask(
-        FROM_HERE,
-        base::Bind(&AdbWebSocket::SendFrameOnHandlerThread, this, message));
-  }
-
- private:
-  friend class base::RefCountedThreadSafe<AdbWebSocket>;
-
-  virtual ~AdbWebSocket() {}
-
-  void ConnectOnHandlerThread() {
-    device_->HttpQuery(
-        socket_name_,
-        base::StringPrintf(kWebSocketUpgradeRequest, url_.c_str()),
-        base::Bind(&AdbWebSocket::ConnectedOnHandlerThread, this));
-  }
-
-  void ConnectedOnHandlerThread(int result, net::StreamSocket* socket) {
-    if (result != net::OK || socket == NULL) {
-      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-          base::Bind(&AdbWebSocket::OnSocketClosed, this, true));
-      return;
-    }
-    socket_.reset(socket);
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-        base::Bind(&AdbWebSocket::OnSocketOpened, this));
-    StartListeningOnHandlerThread();
-  }
-
-  void StartListeningOnHandlerThread() {
-    scoped_refptr<net::IOBuffer> response_buffer =
-        new net::IOBuffer(kBufferSize);
-    int result = socket_->Read(
-        response_buffer.get(),
-        kBufferSize,
-        base::Bind(&AdbWebSocket::OnBytesRead, this, response_buffer));
-    if (result != net::ERR_IO_PENDING)
-      OnBytesRead(response_buffer, result);
-  }
-
-  void OnBytesRead(scoped_refptr<net::IOBuffer> response_buffer, int result) {
-    if (!socket_)
-      return;
-
-    if (result <= 0) {
-      DisconnectOnHandlerThread(true);
-      return;
-    }
-
-    std::string data = std::string(response_buffer->data(), result);
-    response_buffer_ += data;
-
-    int bytes_consumed;
-    std::string output;
-    WebSocket::ParseResult parse_result = WebSocket::DecodeFrameHybi17(
-        response_buffer_, false, &bytes_consumed, &output);
-
-    while (parse_result == WebSocket::FRAME_OK) {
-      response_buffer_ = response_buffer_.substr(bytes_consumed);
-      if (!delegate_ || !delegate_->ProcessIncomingMessage(output)) {
-        BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-            base::Bind(&AdbWebSocket::OnFrameRead, this, output));
-      }
-      parse_result = WebSocket::DecodeFrameHybi17(
-          response_buffer_, false, &bytes_consumed, &output);
-    }
-
-    if (parse_result == WebSocket::FRAME_ERROR ||
-        parse_result == WebSocket::FRAME_CLOSE) {
-      DisconnectOnHandlerThread(true);
-      return;
-    }
-
-    result = socket_->Read(
-        response_buffer.get(),
-        kBufferSize,
-        base::Bind(&AdbWebSocket::OnBytesRead, this, response_buffer));
-    if (result != net::ERR_IO_PENDING)
-      OnBytesRead(response_buffer, result);
-  }
-
-  void SendFrameOnHandlerThread(const std::string& data) {
-    delegate_->ProcessOutgoingMessage(data);
-    int mask = base::RandInt(0, 0x7FFFFFFF);
-    std::string encoded_frame = WebSocket::EncodeFrameHybi17(data, mask);
-    request_buffer_ += encoded_frame;
-    if (request_buffer_.length() == encoded_frame.length())
-      SendPendingRequests(0);
-  }
-
-  void SendPendingRequests(int result) {
-    if (!socket_)
-      return;
-    if (result < 0) {
-      DisconnectOnHandlerThread(true);
-      return;
-    }
-    request_buffer_ = request_buffer_.substr(result);
-    if (request_buffer_.empty())
-      return;
-
-    scoped_refptr<net::StringIOBuffer> buffer =
-        new net::StringIOBuffer(request_buffer_);
-    result = socket_->Write(buffer.get(), buffer->size(),
-                            base::Bind(&AdbWebSocket::SendPendingRequests,
-                                       this));
-    if (result != net::ERR_IO_PENDING)
-      SendPendingRequests(result);
-  }
-
-  void DisconnectOnHandlerThread(bool closed_by_device) {
-    if (!socket_)
-      return;
-    // Wipe out socket_ first since Disconnect can re-enter this method.
-    scoped_ptr<net::StreamSocket> socket(socket_.release());
-    socket->Disconnect();
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-        base::Bind(&AdbWebSocket::OnSocketClosed, this, closed_by_device));
-  }
-
-  void OnSocketOpened() {
-    delegate_->OnSocketOpened();
-  }
-
-  void OnFrameRead(const std::string& message) {
-    delegate_->OnFrameRead(message);
-  }
-
-  void OnSocketClosed(bool closed_by_device) {
-    delegate_->OnSocketClosed(closed_by_device);
-  }
-
-  scoped_refptr<DevToolsAdbBridge::AndroidDevice> device_;
-  std::string socket_name_;
-  std::string url_;
-  scoped_refptr<DevToolsAdbBridge::RefCountedAdbThread> adb_thread_;
-  scoped_ptr<net::StreamSocket> socket_;
-  Delegate* delegate_;
-  std::string response_buffer_;
-  std::string request_buffer_;
-  DISALLOW_COPY_AND_ASSIGN(AdbWebSocket);
-};
-
 class AgentHostDelegate : public content::DevToolsExternalAgentProxyDelegate,
                           public AdbWebSocket::Delegate {
  public:
@@ -706,15 +522,14 @@
       const std::string& socket_name,
       const std::string& debug_url,
       const std::string& frontend_url,
-      scoped_refptr<DevToolsAdbBridge::RefCountedAdbThread> adb_thread,
+      base::MessageLoop* adb_message_loop,
       Profile* profile)
       : id_(id),
         frontend_url_(frontend_url),
         profile_(profile),
         tethering_adb_filter_(kAdbPort, device->serial()) {
     web_socket_ = new AdbWebSocket(
-        device, socket_name, debug_url, adb_thread, this);
-    web_socket_->Connect();
+        device, socket_name, debug_url, adb_message_loop, this);
     g_host_delegates.Get()[id] = this;
   }
 
@@ -773,12 +588,13 @@
   DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
 };
 
-DevToolsAdbBridge::RemotePage::RemotePage(scoped_refptr<AndroidDevice> device,
-                                          const std::string& package,
-                                          const std::string& socket,
-                                          const base::DictionaryValue& value)
-    : device_(device),
-      package_(package),
+DevToolsAdbBridge::RemotePage::RemotePage(
+    scoped_refptr<DevToolsAdbBridge> bridge,
+    scoped_refptr<AndroidDevice> device,
+    const std::string& socket,
+    const base::DictionaryValue& value)
+    : bridge_(bridge),
+      device_(device),
       socket_(socket) {
   value.GetString("id", &id_);
   value.GetString("url", &url_);
@@ -798,11 +614,51 @@
     frontend_url_ = frontend_url_.substr(0, ws_param);
   if (frontend_url_.find("http:") == 0)
     frontend_url_ = "https:" + frontend_url_.substr(5);
+
+  global_id_ = base::StringPrintf(
+      "%s:%s:%s", device->serial().c_str(), socket_.c_str(), id_.c_str());
+}
+
+void DevToolsAdbBridge::RemotePage::Inspect(Profile* profile) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  AgentHostDelegates::iterator it =
+      g_host_delegates.Get().find(global_id());
+  if (it != g_host_delegates.Get().end())
+    it->second->OpenFrontend();
+  else if (!debug_url_.empty())
+    new AgentHostDelegate(
+        global_id_, device_, socket_, debug_url_,
+        frontend_url_, bridge_->GetAdbMessageLoop(), profile);
 }
 
 DevToolsAdbBridge::RemotePage::~RemotePage() {
 }
 
+DevToolsAdbBridge::RemoteBrowser::RemoteBrowser(
+    scoped_refptr<DevToolsAdbBridge> bridge,
+    scoped_refptr<AndroidDevice> device,
+    const std::string& socket,
+    const std::string& name)
+    : bridge_(bridge),
+      device_(device),
+      socket_(socket),
+      name_(name) {
+}
+
+DevToolsAdbBridge::RemoteBrowser::~RemoteBrowser() {
+}
+
+DevToolsAdbBridge::RemoteDevice::RemoteDevice(
+    scoped_refptr<DevToolsAdbBridge> bridge,
+    scoped_refptr<AndroidDevice> device)
+    : bridge_(bridge),
+      device_(device) {
+}
+
+DevToolsAdbBridge::RemoteDevice::~RemoteDevice() {
+}
+
+
 DevToolsAdbBridge::RefCountedAdbThread*
 DevToolsAdbBridge::RefCountedAdbThread::instance_ = NULL;
 
@@ -874,31 +730,10 @@
       base::Bind(&DevToolsAdbBridge::ReceivedAdbDevices, this, callback));
 }
 
-void DevToolsAdbBridge::Attach(const std::string& page_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (!has_message_loop_)
-    return;
-
-  for (RemotePages::iterator it = pages_->begin(); it != pages_->end(); ++it) {
-    scoped_refptr<RemotePage> page = *it;
-    // Assuming page->id() is unique across devices (since it is a GUID).
-    if (page->id() == page_id) {
-      AgentHostDelegates::iterator it = g_host_delegates.Get().find(page_id);
-      if (it != g_host_delegates.Get().end())
-        it->second->OpenFrontend();
-      else if (!page->debug_url().empty())
-        new AgentHostDelegate(
-            page_id, page->device(), page->socket(), page->debug_url(),
-            page->frontend_url(), adb_thread_, profile_);
-      break;
-    }
-  }
-}
-
 void DevToolsAdbBridge::AddListener(Listener* listener) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   if (listeners_.empty())
-    RequestPages();
+    RequestRemoteDevices();
   listeners_.push_back(listener);
 }
 
@@ -956,24 +791,23 @@
   callback.Run(devices);
 }
 
-void DevToolsAdbBridge::RequestPages() {
+void DevToolsAdbBridge::RequestRemoteDevices() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   if (!has_message_loop_)
     return;
 
-  new AdbPagesCommand(this,
-                      base::Bind(&DevToolsAdbBridge::ReceivedPages, this));
+  new AdbPagesCommand(
+      this, base::Bind(&DevToolsAdbBridge::ReceivedRemoteDevices, this));
 }
 
-void DevToolsAdbBridge::ReceivedPages(int result, RemotePages* pages_ptr) {
+void DevToolsAdbBridge::ReceivedRemoteDevices(RemoteDevices* devices_ptr) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  pages_.reset(pages_ptr);
 
-  if (result == net::OK) {
-    Listeners copy(listeners_);
-    for (Listeners::iterator it = copy.begin(); it != copy.end(); ++it)
-      (*it)->RemotePagesChanged(pages_.get());
-  }
+  scoped_ptr<RemoteDevices> devices(devices_ptr);
+
+  Listeners copy(listeners_);
+  for (Listeners::iterator it = copy.begin(); it != copy.end(); ++it)
+    (*it)->RemoteDevicesChanged(devices.get());
 
   if (listeners_.empty())
     return;
@@ -981,6 +815,6 @@
   BrowserThread::PostDelayedTask(
       BrowserThread::UI,
       FROM_HERE,
-      base::Bind(&DevToolsAdbBridge::RequestPages, this),
+      base::Bind(&DevToolsAdbBridge::RequestRemoteDevices, this),
       base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs));
 }
diff --git a/chrome/browser/devtools/devtools_adb_bridge.h b/chrome/browser/devtools/devtools_adb_bridge.h
index f3733a0..70bcb97 100644
--- a/chrome/browser/devtools/devtools_adb_bridge.h
+++ b/chrome/browser/devtools/devtools_adb_bridge.h
@@ -84,29 +84,26 @@
 
   class RemotePage : public base::RefCounted<RemotePage> {
    public:
-    RemotePage(scoped_refptr<AndroidDevice> device,
-               const std::string& package,
+    RemotePage(scoped_refptr<DevToolsAdbBridge> bridge,
+               scoped_refptr<AndroidDevice> device,
                const std::string& socket,
                const base::DictionaryValue& value);
 
-    scoped_refptr<AndroidDevice> device() { return device_; }
-    std::string serial() { return device_->serial(); }
-    std::string model() { return device_->model(); }
-    std::string package() { return package_; }
-    std::string socket() { return socket_; }
     std::string id() { return id_; }
     std::string url() { return url_; }
     std::string title() { return title_; }
     std::string description() { return description_; }
     std::string favicon_url() { return favicon_url_; }
-    std::string debug_url() { return debug_url_; }
-    std::string frontend_url() { return frontend_url_; }
+    std::string global_id() { return global_id_; }
+
+    void Inspect(Profile* profile);
 
    private:
     friend class base::RefCounted<RemotePage>;
     virtual ~RemotePage();
+
+    scoped_refptr<DevToolsAdbBridge> bridge_;
     scoped_refptr<AndroidDevice> device_;
-    std::string package_;
     std::string socket_;
     std::string id_;
     std::string url_;
@@ -115,11 +112,66 @@
     std::string favicon_url_;
     std::string debug_url_;
     std::string frontend_url_;
+    std::string global_id_;
     DISALLOW_COPY_AND_ASSIGN(RemotePage);
   };
 
   typedef std::vector<scoped_refptr<RemotePage> > RemotePages;
-  typedef base::Callback<void(int, RemotePages*)> PagesCallback;
+
+  class RemoteBrowser : public base::RefCounted<RemoteBrowser> {
+   public:
+    RemoteBrowser(scoped_refptr<DevToolsAdbBridge> bridge,
+                  scoped_refptr<AndroidDevice> device,
+                  const std::string& socket,
+                  const std::string& name);
+
+    scoped_refptr<AndroidDevice> device() { return device_; }
+    std::string socket() { return socket_; }
+    std::string name() { return name_; }
+
+    RemotePages& pages() { return pages_; }
+    void AddPage(scoped_refptr<RemotePage> page) { pages_.push_back(page); }
+
+   private:
+    friend class base::RefCounted<RemoteBrowser>;
+    virtual ~RemoteBrowser();
+
+    scoped_refptr<DevToolsAdbBridge> bridge_;
+    scoped_refptr<AndroidDevice> device_;
+    const std::string socket_;
+    const std::string name_;
+    RemotePages pages_;
+
+    DISALLOW_COPY_AND_ASSIGN(RemoteBrowser);
+  };
+
+  typedef std::vector<scoped_refptr<RemoteBrowser> > RemoteBrowsers;
+
+  class RemoteDevice : public base::RefCounted<RemoteDevice> {
+   public:
+    explicit RemoteDevice(scoped_refptr<DevToolsAdbBridge> bridge,
+                          scoped_refptr<AndroidDevice> device);
+
+    std::string serial() { return device_->serial(); }
+    std::string model() { return device_->model(); }
+
+    RemoteBrowsers& browsers() { return browsers_; }
+    void AddBrowser(scoped_refptr<RemoteBrowser> browser) {
+      browsers_.push_back(browser);
+    }
+
+   private:
+    friend class base::RefCounted<RemoteDevice>;
+    virtual ~RemoteDevice();
+
+    scoped_refptr<DevToolsAdbBridge> bridge_;
+    scoped_refptr<AndroidDevice> device_;
+    RemoteBrowsers browsers_;
+
+    DISALLOW_COPY_AND_ASSIGN(RemoteDevice);
+  };
+
+  typedef std::vector<scoped_refptr<RemoteDevice> > RemoteDevices;
 
   class AndroidDevice : public base::RefCounted<AndroidDevice> {
    public:
@@ -166,7 +218,7 @@
 
   class Listener {
    public:
-    virtual void RemotePagesChanged(RemotePages* pages) = 0;
+    virtual void RemoteDevicesChanged(RemoteDevices* devices) = 0;
    protected:
     virtual ~Listener() {}
   };
@@ -176,8 +228,6 @@
   void EnumerateUsbDevices(const AndroidDevicesCallback& callback);
   void EnumerateAdbDevices(const AndroidDevicesCallback& callback);
 
-  void Attach(const std::string& page_id);
-
   void AddListener(Listener* listener);
   void RemoveListener(Listener* listener);
 
@@ -187,8 +237,6 @@
   friend struct content::BrowserThread::DeleteOnThread<
       content::BrowserThread::UI>;
   friend class base::DeleteHelper<DevToolsAdbBridge>;
-  friend class AdbWebSocket;
-  friend class AgentHostDelegate;
 
   class RefCountedAdbThread : public base::RefCounted<RefCountedAdbThread> {
    public:
@@ -212,14 +260,13 @@
                           int result,
                           const std::string& response);
 
-  void RequestPages();
-  void ReceivedPages(int result, RemotePages* pages);
+  void RequestRemoteDevices();
+  void ReceivedRemoteDevices(RemoteDevices* devices);
 
   Profile* profile_;
   scoped_refptr<RefCountedAdbThread> adb_thread_;
   bool has_message_loop_;
   scoped_ptr<crypto::RSAPrivateKey> rsa_key_;
-  scoped_ptr<RemotePages> pages_;
   typedef std::vector<Listener*> Listeners;
   Listeners listeners_;
   DISALLOW_COPY_AND_ASSIGN(DevToolsAdbBridge);
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc
index 4905f6e..bb9e5bb 100644
--- a/chrome/browser/devtools/devtools_sanity_browsertest.cc
+++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -29,6 +29,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/content_browser_client.h"
@@ -44,13 +45,10 @@
 #include "content/public/browser/worker_service_observer.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
+#include "extensions/common/switches.h"
 #include "net/socket/tcp_listen_socket.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::BrowserThread;
 using content::DevToolsManager;
 using content::DevToolsAgentHost;
@@ -270,7 +268,8 @@
 class DevToolsExperimentalExtensionTest : public DevToolsExtensionTest {
  public:
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
+    command_line->AppendSwitch(
+        extensions::switches::kEnableExperimentalExtensionApis);
   }
 };
 
@@ -597,7 +596,7 @@
 IN_PROC_BROWSER_TEST_F(WorkerDevToolsSanityTest, MAYBE_InspectSharedWorker) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -664,7 +663,7 @@
 IN_PROC_BROWSER_TEST_F(RemoteDebuggingTest, RemoteDebugger) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 21fa114..7d2974e 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -103,74 +103,141 @@
 const int kMinContentsSize = 50;
 const int kMinimizedDevToolsHeight = 24;
 
-class DevToolsWindow::InspectedWebContentsObserver
-    : public content::WebContentsObserver {
- public:
-  explicit InspectedWebContentsObserver(content::WebContents* web_contents)
-    : WebContentsObserver(web_contents) {
-  }
 
-  content::WebContents* Get() { return web_contents(); }
-};
-
-class DevToolsWindow::FrontendWebContentsObserver
-    : public content::WebContentsObserver {
- public:
-  explicit FrontendWebContentsObserver(content::WebContents* web_contents)
-    : WebContentsObserver(web_contents) {
-  }
- private:
-  // Overriden from contents::WebContentsObserver.
-  virtual void AboutToNavigateRenderView(
-      RenderViewHost* render_view_host) OVERRIDE {
-    content::DevToolsClientHost::SetupDevToolsFrontendClient(render_view_host);
-  }
-};
+// DevToolsConfirmInfoBarDelegate ---------------------------------------------
 
 typedef Callback<void(bool)> ConfirmInfoBarCallback;
 
 class DevToolsConfirmInfoBarDelegate : public ConfirmInfoBarDelegate {
  public:
-  DevToolsConfirmInfoBarDelegate(
-      InfoBarService* infobar_service,
-      const ConfirmInfoBarCallback& callback,
-      string16 message)
-      : ConfirmInfoBarDelegate(infobar_service),
-        callback_(callback),
-        message_(message) {
-  }
+  DevToolsConfirmInfoBarDelegate(InfoBarService* infobar_service,
+                                 const ConfirmInfoBarCallback& callback,
+                                 string16 message);
 
-  virtual string16 GetMessageText() const OVERRIDE { return message_; }
-
-  virtual bool Accept() OVERRIDE {
-    callback_.Run(true);
-    callback_.Reset();
-    return true;
-  }
-
-  virtual bool Cancel() OVERRIDE {
-    callback_.Run(false);
-    callback_.Reset();
-    return true;
-  }
-
-  virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE {
-    return l10n_util::GetStringUTF16((button == BUTTON_OK)
-                                         ? IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON
-                                         : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON);
-  }
+  virtual string16 GetMessageText() const OVERRIDE;
+  virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
+  virtual bool Accept() OVERRIDE;
+  virtual bool Cancel() OVERRIDE;
 
  private:
-  virtual ~DevToolsConfirmInfoBarDelegate() {
-    if (!callback_.is_null()) {
-      callback_.Run(false);
-    }
-  }
+  virtual ~DevToolsConfirmInfoBarDelegate();
 
   ConfirmInfoBarCallback callback_;
   string16 message_;
 };
 
+DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
+    InfoBarService* infobar_service,
+    const ConfirmInfoBarCallback& callback,
+    string16 message)
+    : ConfirmInfoBarDelegate(infobar_service),
+      callback_(callback),
+      message_(message) {
+}
+
+string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const {
+  return message_;
+}
+
+string16 DevToolsConfirmInfoBarDelegate::GetButtonLabel(
+    InfoBarButton button) const {
+  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
+      IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON);
+}
+
+bool DevToolsConfirmInfoBarDelegate::Accept() {
+  callback_.Run(true);
+  callback_.Reset();
+  return true;
+}
+
+bool DevToolsConfirmInfoBarDelegate::Cancel() {
+  callback_.Run(false);
+  callback_.Reset();
+  return true;
+}
+
+DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
+  if (!callback_.is_null()) {
+    callback_.Run(false);
+  }
+}
+
+
+// DevToolsWindow::InspectedWebContentsObserver -------------------------------
+
+class DevToolsWindow::InspectedWebContentsObserver
+    : public content::WebContentsObserver {
+ public:
+  explicit InspectedWebContentsObserver(content::WebContents* web_contents);
+
+  content::WebContents* web_contents() {
+    return WebContentsObserver::web_contents();
+  }
+};
+
+DevToolsWindow::InspectedWebContentsObserver::InspectedWebContentsObserver(
+    content::WebContents* web_contents)
+    : WebContentsObserver(web_contents) {
+}
+
+
+// DevToolsWindow::FrontendWebContentsObserver --------------------------------
+
+class DevToolsWindow::FrontendWebContentsObserver
+    : public content::WebContentsObserver {
+ public:
+  explicit FrontendWebContentsObserver(content::WebContents* web_contents);
+
+ private:
+  // Overriden from contents::WebContentsObserver.
+  virtual void AboutToNavigateRenderView(
+      RenderViewHost* render_view_host) OVERRIDE;
+};
+
+DevToolsWindow::FrontendWebContentsObserver::FrontendWebContentsObserver(
+    content::WebContents* web_contents)
+    : WebContentsObserver(web_contents) {
+}
+
+void DevToolsWindow::FrontendWebContentsObserver::AboutToNavigateRenderView(
+    content::RenderViewHost* render_view_host) {
+  content::DevToolsClientHost::SetupDevToolsFrontendClient(render_view_host);
+}
+
+
+// DevToolsWindow -------------------------------------------------------------
+
+namespace {
+
+std::string SkColorToRGBAString(SkColor color) {
+  // We convert the alpha using DoubleToString because StringPrintf will use
+  // locale specific formatters (e.g., use , instead of . in German).
+  return base::StringPrintf("rgba(%d,%d,%d,%s)", SkColorGetR(color),
+      SkColorGetG(color), SkColorGetB(color),
+      base::DoubleToString(SkColorGetA(color) / 255.0).c_str());
+}
+
+DictionaryValue* CreateFileSystemValue(
+    DevToolsFileHelper::FileSystem file_system) {
+  DictionaryValue* file_system_value = new DictionaryValue();
+  file_system_value->SetString("fileSystemName", file_system.file_system_name);
+  file_system_value->SetString("rootURL", file_system.root_url);
+  file_system_value->SetString("fileSystemPath", file_system.file_system_path);
+  return file_system_value;
+}
+
+}  // namespace
+
+DevToolsWindow::~DevToolsWindow() {
+  DevToolsWindowList& instances = g_instances.Get();
+  DevToolsWindowList::iterator it = std::find(instances.begin(),
+                                              instances.end(),
+                                              this);
+  DCHECK(it != instances.end());
+  instances.erase(it);
+}
+
 // static
 std::string DevToolsWindow::GetDevToolsWindowPlacementPrefKey() {
   std::string wp_key;
@@ -272,18 +339,6 @@
 }
 
 // static
-void DevToolsWindow::InspectElement(RenderViewHost* inspected_rvh,
-                                    int x,
-                                    int y) {
-  scoped_refptr<DevToolsAgentHost> agent(
-      DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
-  agent->InspectElement(x, y);
-  // TODO(loislo): we should initiate DevTools window opening from within
-  // renderer. Otherwise, we still can hit a race condition here.
-  OpenDevToolsWindow(inspected_rvh);
-}
-
-// static
 void DevToolsWindow::OpenExternalFrontend(
     Profile* profile,
     const std::string& frontend_url,
@@ -299,16 +354,165 @@
 }
 
 // static
-DevToolsWindow* DevToolsWindow::Create(
-    Profile* profile,
-    const GURL& frontend_url,
+DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
     RenderViewHost* inspected_rvh,
-    DevToolsDockSide dock_side,
-    bool shared_worker_frontend) {
-  // Create WebContents with devtools.
-  GURL url = GetDevToolsURL(profile, frontend_url, dock_side,
-                            shared_worker_frontend);
-  return new DevToolsWindow(profile, url, inspected_rvh, dock_side);
+    bool force_open,
+    DevToolsToggleAction action) {
+  scoped_refptr<DevToolsAgentHost> agent(
+      DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
+  DevToolsManager* manager = DevToolsManager::GetInstance();
+  DevToolsWindow* window = FindDevToolsWindow(agent.get());
+  bool do_open = force_open;
+  if (!window) {
+    Profile* profile = Profile::FromBrowserContext(
+        inspected_rvh->GetProcess()->GetBrowserContext());
+    DevToolsDockSide dock_side = GetDockSideFromPrefs(profile);
+    window = Create(profile, GURL(), inspected_rvh, dock_side, false);
+    manager->RegisterDevToolsClientHostFor(agent.get(),
+                                           window->frontend_host_.get());
+    do_open = true;
+  }
+
+  // Update toolbar to reflect DevTools changes.
+  window->UpdateBrowserToolbar();
+
+  // If window is docked and visible, we hide it on toggle. If window is
+  // undocked, we show (activate) it. If window is minimized, we maximize it.
+  if (window->dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED)
+    window->Restore();
+  else if (!window->IsDocked() || do_open)
+    window->Show(action);
+  else
+    window->CloseWindow();
+
+  return window;
+}
+
+// static
+void DevToolsWindow::InspectElement(RenderViewHost* inspected_rvh,
+                                    int x,
+                                    int y) {
+  scoped_refptr<DevToolsAgentHost> agent(
+      DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
+  agent->InspectElement(x, y);
+  // TODO(loislo): we should initiate DevTools window opening from within
+  // renderer. Otherwise, we still can hit a race condition here.
+  OpenDevToolsWindow(inspected_rvh);
+}
+
+// static
+int DevToolsWindow::GetMinimumWidth() {
+  return kMinDevToolsWidth;
+}
+
+// static
+int DevToolsWindow::GetMinimumHeight() {
+  return kMinDevToolsHeight;
+}
+
+// static
+int DevToolsWindow::GetMinimizedHeight() {
+  return kMinimizedDevToolsHeight;
+}
+
+void DevToolsWindow::InspectedContentsClosing() {
+  Hide();
+}
+
+RenderViewHost* DevToolsWindow::GetRenderViewHost() {
+  return web_contents_->GetRenderViewHost();
+}
+
+DevToolsClientHost* DevToolsWindow::GetDevToolsClientHostForTest() {
+  return frontend_host_.get();
+}
+
+int DevToolsWindow::GetWidth(int container_width) {
+  if (width_ == -1) {
+    width_ = profile_->GetPrefs()->
+        GetInteger(prefs::kDevToolsVSplitLocation);
+  }
+
+  // By default, size devtools as 1/3 of the browser window.
+  if (width_ == -1)
+    width_ = container_width / 3;
+
+  // Respect the minimum devtools width preset.
+  width_ = std::max(kMinDevToolsWidth, width_);
+
+  // But it should never compromise the content window size unless the entire
+  // window is tiny.
+  width_ = std::min(container_width - kMinContentsSize, width_);
+  return width_;
+}
+
+int DevToolsWindow::GetHeight(int container_height) {
+  if (height_ == -1) {
+    height_ = profile_->GetPrefs()->
+        GetInteger(prefs::kDevToolsHSplitLocation);
+  }
+
+  // By default, size devtools as 1/3 of the browser window.
+  if (height_ == -1)
+    height_ = container_height / 3;
+
+  // Respect the minimum devtools width preset.
+  height_ = std::max(kMinDevToolsHeight, height_);
+
+  // But it should never compromise the content window size.
+  height_ = std::min(container_height - kMinContentsSize, height_);
+  return height_;
+}
+
+void DevToolsWindow::SetWidth(int width) {
+  width_ = width;
+  profile_->GetPrefs()->SetInteger(prefs::kDevToolsVSplitLocation, width);
+}
+
+void DevToolsWindow::SetHeight(int height) {
+  height_ = height;
+  profile_->GetPrefs()->SetInteger(prefs::kDevToolsHSplitLocation, height);
+}
+
+void DevToolsWindow::Show(DevToolsToggleAction action) {
+  if (IsDocked()) {
+    Browser* inspected_browser;
+    int inspected_tab_index;
+    // Tell inspected browser to update splitter and switch to inspected panel.
+    if (!IsInspectedBrowserPopup() &&
+        FindInspectedBrowserAndTabIndex(&inspected_browser,
+                                        &inspected_tab_index)) {
+      BrowserWindow* inspected_window = inspected_browser->window();
+      web_contents_->SetDelegate(this);
+      inspected_window->UpdateDevTools();
+      web_contents_->GetView()->SetInitialFocus();
+      inspected_window->Show();
+      TabStripModel* tab_strip_model = inspected_browser->tab_strip_model();
+      tab_strip_model->ActivateTabAt(inspected_tab_index, true);
+      PrefsTabHelper::CreateForWebContents(web_contents_);
+      GetRenderViewHost()->SyncRendererPrefs();
+      ScheduleAction(action);
+      return;
+    } else {
+      // Sometimes we don't know where to dock. Stay undocked.
+      dock_side_ = DEVTOOLS_DOCK_SIDE_UNDOCKED;
+    }
+  }
+
+  // Avoid consecutive window switching if the devtools window has been opened
+  // and the Inspect Element shortcut is pressed in the inspected tab.
+  bool should_show_window =
+      !browser_ || action != DEVTOOLS_TOGGLE_ACTION_INSPECT;
+
+  if (!browser_)
+    CreateDevToolsBrowser();
+
+  if (should_show_window) {
+    browser_->window()->Show();
+    web_contents_->GetView()->SetInitialFocus();
+  }
+
+  ScheduleAction(action);
 }
 
 DevToolsWindow::DevToolsWindow(Profile* profile,
@@ -361,361 +565,17 @@
         WebContents::FromRenderViewHost(inspected_rvh)));
 }
 
-DevToolsWindow::~DevToolsWindow() {
-  DevToolsWindowList& instances = g_instances.Get();
-  DevToolsWindowList::iterator it = std::find(instances.begin(),
-                                              instances.end(),
-                                              this);
-  DCHECK(it != instances.end());
-  instances.erase(it);
-}
-
-content::WebContents* DevToolsWindow::GetInspectedWebContents() {
-  if (!inspected_contents_observer_)
-    return NULL;
-  return inspected_contents_observer_->Get();
-}
-
-void DevToolsWindow::InspectedContentsClosing() {
-  Hide();
-}
-
-void DevToolsWindow::Hide() {
-  if (IsDocked()) {
-    // Update dev tools to reflect removed dev tools window.
-    BrowserWindow* inspected_window = GetInspectedBrowserWindow();
-    if (inspected_window)
-      inspected_window->UpdateDevTools();
-    // In case of docked web_contents_, we own it so delete here.
-    delete web_contents_;
-
-    delete this;
-  } else {
-    // First, initiate self-destruct to free all the registrars.
-    // Then close all tabs. Browser will take care of deleting web_contents_
-    // for us.
-    Browser* browser = browser_;
-    delete this;
-    browser->tab_strip_model()->CloseAllTabs();
-  }
-}
-
-void DevToolsWindow::Show(DevToolsToggleAction action) {
-  if (IsDocked()) {
-    Browser* inspected_browser;
-    int inspected_tab_index;
-    // Tell inspected browser to update splitter and switch to inspected panel.
-    if (!IsInspectedBrowserPopup() &&
-        FindInspectedBrowserAndTabIndex(&inspected_browser,
-                                        &inspected_tab_index)) {
-      BrowserWindow* inspected_window = inspected_browser->window();
-      web_contents_->SetDelegate(this);
-      inspected_window->UpdateDevTools();
-      web_contents_->GetView()->SetInitialFocus();
-      inspected_window->Show();
-      TabStripModel* tab_strip_model = inspected_browser->tab_strip_model();
-      tab_strip_model->ActivateTabAt(inspected_tab_index, true);
-      PrefsTabHelper::CreateForWebContents(web_contents_);
-      GetRenderViewHost()->SyncRendererPrefs();
-      ScheduleAction(action);
-      return;
-    } else {
-      // Sometimes we don't know where to dock. Stay undocked.
-      dock_side_ = DEVTOOLS_DOCK_SIDE_UNDOCKED;
-    }
-  }
-
-  // Avoid consecutive window switching if the devtools window has been opened
-  // and the Inspect Element shortcut is pressed in the inspected tab.
-  bool should_show_window =
-      !browser_ || action != DEVTOOLS_TOGGLE_ACTION_INSPECT;
-
-  if (!browser_)
-    CreateDevToolsBrowser();
-
-  if (should_show_window) {
-    browser_->window()->Show();
-    web_contents_->GetView()->SetInitialFocus();
-  }
-
-  ScheduleAction(action);
-}
-
-DevToolsClientHost* DevToolsWindow::GetDevToolsClientHostForTest() {
-  return frontend_host_.get();
-}
-
-int DevToolsWindow::GetWidth(int container_width) {
-  if (width_ == -1) {
-    width_ = profile_->GetPrefs()->
-        GetInteger(prefs::kDevToolsVSplitLocation);
-  }
-
-  // By default, size devtools as 1/3 of the browser window.
-  if (width_ == -1)
-    width_ = container_width / 3;
-
-  // Respect the minimum devtools width preset.
-  width_ = std::max(kMinDevToolsWidth, width_);
-
-  // But it should never compromise the content window size unless the entire
-  // window is tiny.
-  width_ = std::min(container_width - kMinContentsSize, width_);
-  return width_;
-}
-
-int DevToolsWindow::GetHeight(int container_height) {
-  if (height_ == -1) {
-    height_ = profile_->GetPrefs()->
-        GetInteger(prefs::kDevToolsHSplitLocation);
-  }
-
-  // By default, size devtools as 1/3 of the browser window.
-  if (height_ == -1)
-    height_ = container_height / 3;
-
-  // Respect the minimum devtools width preset.
-  height_ = std::max(kMinDevToolsHeight, height_);
-
-  // But it should never compromise the content window size.
-  height_ = std::min(container_height - kMinContentsSize, height_);
-  return height_;
-}
-
-int DevToolsWindow::GetMinimumWidth() {
-  return kMinDevToolsWidth;
-}
-
-int DevToolsWindow::GetMinimumHeight() {
-  return kMinDevToolsHeight;
-}
-
-void DevToolsWindow::SetWidth(int width) {
-  width_ = width;
-  profile_->GetPrefs()->SetInteger(prefs::kDevToolsVSplitLocation, width);
-}
-
-void DevToolsWindow::SetHeight(int height) {
-  height_ = height;
-  profile_->GetPrefs()->SetInteger(prefs::kDevToolsHSplitLocation, height);
-}
-
-int DevToolsWindow::GetMinimizedHeight() {
-  return kMinimizedDevToolsHeight;
-}
-
-RenderViewHost* DevToolsWindow::GetRenderViewHost() {
-  return web_contents_->GetRenderViewHost();
-}
-
-void DevToolsWindow::CreateDevToolsBrowser() {
-  std::string wp_key = GetDevToolsWindowPlacementPrefKey();
-  PrefService* prefs = profile_->GetPrefs();
-  const DictionaryValue* wp_pref = prefs->GetDictionary(wp_key.c_str());
-  if (!wp_pref || wp_pref->empty()) {
-    DictionaryPrefUpdate update(prefs, wp_key.c_str());
-    DictionaryValue* defaults = update.Get();
-    defaults->SetInteger("left", 100);
-    defaults->SetInteger("top", 100);
-    defaults->SetInteger("right", 740);
-    defaults->SetInteger("bottom", 740);
-    defaults->SetBoolean("maximized", false);
-    defaults->SetBoolean("always_on_top", false);
-  }
-
-  chrome::HostDesktopType host_desktop_type =
-      chrome::GetHostDesktopTypeForNativeView(
-          web_contents_->GetView()->GetNativeView());
-
-  browser_ = new Browser(Browser::CreateParams::CreateForDevTools(
-                             profile_, host_desktop_type));
-  browser_->tab_strip_model()->AddWebContents(
-      web_contents_, -1, content::PAGE_TRANSITION_AUTO_TOPLEVEL,
-      TabStripModel::ADD_ACTIVE);
-  GetRenderViewHost()->SyncRendererPrefs();
-}
-
-bool DevToolsWindow::FindInspectedBrowserAndTabIndex(Browser** browser,
-                                                     int* tab) {
-  content::WebContents* inspected_web_contents = GetInspectedWebContents();
-  if (!inspected_web_contents)
-    return false;
-
-  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
-    int tab_index = it->tab_strip_model()->GetIndexOfWebContents(
-        inspected_web_contents);
-    if (tab_index != TabStripModel::kNoTab) {
-      *browser = *it;
-      *tab = tab_index;
-      return true;
-    }
-  }
-  return false;
-}
-
-BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
-  Browser* browser = NULL;
-  int tab;
-  return FindInspectedBrowserAndTabIndex(&browser, &tab) ?
-      browser->window() : NULL;
-}
-
-bool DevToolsWindow::IsInspectedBrowserPopup() {
-  Browser* browser = NULL;
-  int tab;
-  if (!FindInspectedBrowserAndTabIndex(&browser, &tab))
-    return false;
-
-  return browser->is_type_popup();
-}
-
-void DevToolsWindow::UpdateFrontendDockSide() {
-  base::StringValue dock_side(SideToString(dock_side_));
-  CallClientFunction("InspectorFrontendAPI.setDockSide", &dock_side);
-  base::FundamentalValue docked(IsDocked());
-  CallClientFunction("InspectorFrontendAPI.setAttachedWindow", &docked);
-}
-
-
-void DevToolsWindow::AddDevToolsExtensionsToClient() {
-  content::WebContents* inspected_web_contents = GetInspectedWebContents();
-  if (inspected_web_contents) {
-    SessionTabHelper* session_tab_helper =
-        SessionTabHelper::FromWebContents(inspected_web_contents);
-    if (session_tab_helper) {
-      base::FundamentalValue tabId(session_tab_helper->session_id().id());
-      CallClientFunction("WebInspector.setInspectedTabId", &tabId);
-    }
-  }
-  ListValue results;
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
-  const ExtensionService* extension_service = extensions::ExtensionSystem::Get(
-      profile->GetOriginalProfile())->extension_service();
-  if (!extension_service)
-    return;
-
-  const ExtensionSet* extensions = extension_service->extensions();
-
-  for (ExtensionSet::const_iterator extension = extensions->begin();
-       extension != extensions->end(); ++extension) {
-    if (extensions::ManifestURL::GetDevToolsPage(extension->get()).is_empty())
-      continue;
-    DictionaryValue* extension_info = new DictionaryValue();
-    extension_info->Set(
-        "startPage",
-        new StringValue(
-            extensions::ManifestURL::GetDevToolsPage(extension->get()).spec()));
-    extension_info->Set("name", new StringValue((*extension)->name()));
-    bool allow_experimental = (*extension)->HasAPIPermission(
-        extensions::APIPermission::kExperimental);
-    extension_info->Set("exposeExperimentalAPIs",
-        new base::FundamentalValue(allow_experimental));
-    results.Append(extension_info);
-  }
-  CallClientFunction("WebInspector.addExtensions", &results);
-}
-
-WebContents* DevToolsWindow::OpenURLFromTab(WebContents* source,
-                                            const OpenURLParams& params) {
-  if (!params.url.SchemeIs(chrome::kChromeDevToolsScheme)) {
-    content::WebContents* inspected_web_contents = GetInspectedWebContents();
-    if (inspected_web_contents)
-      return inspected_web_contents->OpenURL(params);
-    return NULL;
-  }
-
-  DevToolsManager* manager = DevToolsManager::GetInstance();
-  scoped_refptr<DevToolsAgentHost> agent_host(
-      manager->GetDevToolsAgentHostFor(frontend_host_.get()));
-  if (!agent_host.get())
-    return NULL;
-  manager->ClientHostClosing(frontend_host_.get());
-  manager->RegisterDevToolsClientHostFor(agent_host.get(),
-                                         frontend_host_.get());
-
-  chrome::NavigateParams nav_params(profile_, params.url, params.transition);
-  FillNavigateParamsFromOpenURLParams(&nav_params, params);
-  nav_params.source_contents = source;
-  nav_params.tabstrip_add_types = TabStripModel::ADD_NONE;
-  nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
-  nav_params.user_gesture = params.user_gesture;
-  chrome::Navigate(&nav_params);
-  return nav_params.target_contents;
-}
-
-void DevToolsWindow::CallClientFunction(const std::string& function_name,
-                                        const Value* arg1,
-                                        const Value* arg2) {
-  std::string params;
-  if (arg1) {
-    std::string json;
-    base::JSONWriter::Write(arg1, &json);
-    params.append(json);
-    if (arg2) {
-      base::JSONWriter::Write(arg2, &json);
-      params.append(", " + json);
-    }
-  }
-  string16 javascript = ASCIIToUTF16(function_name + "(" + params + ");");
-  web_contents_->GetRenderViewHost()->
-      ExecuteJavascriptInWebFrame(string16(), javascript);
-}
-
-void DevToolsWindow::Observe(int type,
-                             const content::NotificationSource& source,
-                             const content::NotificationDetails& details) {
-  if (type == content::NOTIFICATION_LOAD_STOP && !is_loaded_) {
-    is_loaded_ = true;
-    UpdateTheme();
-    DoAction();
-    AddDevToolsExtensionsToClient();
-  } else if (type == chrome::NOTIFICATION_TAB_CLOSING) {
-    if (content::Source<NavigationController>(source).ptr() ==
-            &web_contents_->GetController()) {
-      // This happens when browser closes all of its tabs as a result
-      // of window.Close event.
-      // Notify manager that this DevToolsClientHost no longer exists and
-      // initiate self-destuct here.
-      DevToolsManager::GetInstance()->ClientHostClosing(frontend_host_.get());
-      UpdateBrowserToolbar();
-      delete this;
-    }
-  } else if (type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED) {
-    UpdateTheme();
-  }
-}
-
-void DevToolsWindow::ScheduleAction(DevToolsToggleAction action) {
-  action_on_load_ = action;
-  if (is_loaded_)
-    DoAction();
-}
-
-void DevToolsWindow::DoAction() {
-  UpdateFrontendDockSide();
-  switch (action_on_load_) {
-    case DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE:
-      CallClientFunction("InspectorFrontendAPI.showConsole", NULL);
-      break;
-    case DEVTOOLS_TOGGLE_ACTION_INSPECT:
-      CallClientFunction("InspectorFrontendAPI.enterInspectElementMode", NULL);
-    case DEVTOOLS_TOGGLE_ACTION_SHOW:
-    case DEVTOOLS_TOGGLE_ACTION_TOGGLE:
-      // Do nothing.
-      break;
-    default:
-      NOTREACHED();
-  }
-  action_on_load_ = DEVTOOLS_TOGGLE_ACTION_SHOW;
-}
-
-std::string SkColorToRGBAString(SkColor color) {
-  // We convert the alpha using DoubleToString because StringPrintf will use
-  // locale specific formatters (e.g., use , instead of . in German).
-  return base::StringPrintf("rgba(%d,%d,%d,%s)", SkColorGetR(color),
-      SkColorGetG(color), SkColorGetB(color),
-      base::DoubleToString(SkColorGetA(color) / 255.0).c_str());
+// static
+DevToolsWindow* DevToolsWindow::Create(
+    Profile* profile,
+    const GURL& frontend_url,
+    RenderViewHost* inspected_rvh,
+    DevToolsDockSide dock_side,
+    bool shared_worker_frontend) {
+  // Create WebContents with devtools.
+  GURL url = GetDevToolsURL(profile, frontend_url, dock_side,
+                            shared_worker_frontend);
+  return new DevToolsWindow(profile, url, inspected_rvh, dock_side);
 }
 
 // static
@@ -751,20 +611,129 @@
   return GURL(url_string);
 }
 
-void DevToolsWindow::UpdateTheme() {
-  ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_);
-  CHECK(tp);
+// static
+DevToolsWindow* DevToolsWindow::FindDevToolsWindow(
+    DevToolsAgentHost* agent_host) {
+  DevToolsManager* manager = DevToolsManager::GetInstance();
+  DevToolsWindowList& instances = g_instances.Get();
+  for (DevToolsWindowList::iterator it = instances.begin();
+       it != instances.end(); ++it) {
+    if (manager->GetDevToolsAgentHostFor((*it)->frontend_host_.get()) ==
+        agent_host)
+      return *it;
+  }
+  return NULL;
+}
 
-  SkColor color_toolbar =
-      tp->GetColor(ThemeProperties::COLOR_TOOLBAR);
-  SkColor color_tab_text =
-      tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
-  std::string command = base::StringPrintf(
-      "InspectorFrontendAPI.setToolbarColors(\"%s\", \"%s\")",
-      SkColorToRGBAString(color_toolbar).c_str(),
-      SkColorToRGBAString(color_tab_text).c_str());
-  web_contents_->GetRenderViewHost()->
-      ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(command));
+// static
+DevToolsWindow* DevToolsWindow::AsDevToolsWindow(RenderViewHost* window_rvh) {
+  if (g_instances == NULL)
+    return NULL;
+  DevToolsWindowList& instances = g_instances.Get();
+  for (DevToolsWindowList::iterator it = instances.begin();
+       it != instances.end(); ++it) {
+    if ((*it)->web_contents_->GetRenderViewHost() == window_rvh)
+      return *it;
+  }
+  return NULL;
+}
+
+// static
+DevToolsDockSide DevToolsWindow::GetDockSideFromPrefs(Profile* profile) {
+  std::string dock_side =
+      profile->GetPrefs()->GetString(prefs::kDevToolsDockSide);
+
+  // Migrate prefs
+  if (dock_side == kOldPrefBottom || dock_side == kOldPrefRight) {
+    bool docked = profile->GetPrefs()->GetBoolean(prefs::kDevToolsOpenDocked);
+    if (dock_side == kOldPrefBottom)
+      return docked ? DEVTOOLS_DOCK_SIDE_BOTTOM : DEVTOOLS_DOCK_SIDE_UNDOCKED;
+    else
+      return docked ? DEVTOOLS_DOCK_SIDE_RIGHT : DEVTOOLS_DOCK_SIDE_UNDOCKED;
+  }
+
+  if (dock_side == kPrefUndocked)
+    return DEVTOOLS_DOCK_SIDE_UNDOCKED;
+  else if (dock_side == kPrefRight)
+    return DEVTOOLS_DOCK_SIDE_RIGHT;
+  // Default to docked to bottom
+  return DEVTOOLS_DOCK_SIDE_BOTTOM;
+}
+
+// static
+std::string DevToolsWindow::SideToString(DevToolsDockSide dock_side) {
+  std::string dock_side_string;
+  switch (dock_side) {
+    case DEVTOOLS_DOCK_SIDE_UNDOCKED: return kDockSideUndocked;
+    case DEVTOOLS_DOCK_SIDE_RIGHT: return kDockSideRight;
+    case DEVTOOLS_DOCK_SIDE_BOTTOM: return kDockSideBottom;
+    case DEVTOOLS_DOCK_SIDE_MINIMIZED: return kDockSideMinimized;
+  }
+  return kDockSideUndocked;
+}
+
+// static
+DevToolsDockSide DevToolsWindow::SideFromString(
+    const std::string& dock_side) {
+  if (dock_side == kDockSideRight)
+    return DEVTOOLS_DOCK_SIDE_RIGHT;
+  if (dock_side == kDockSideBottom)
+    return DEVTOOLS_DOCK_SIDE_BOTTOM;
+  if (dock_side == kDockSideMinimized)
+    return DEVTOOLS_DOCK_SIDE_MINIMIZED;
+  return DEVTOOLS_DOCK_SIDE_UNDOCKED;
+}
+
+void DevToolsWindow::Observe(int type,
+                             const content::NotificationSource& source,
+                             const content::NotificationDetails& details) {
+  if (type == content::NOTIFICATION_LOAD_STOP && !is_loaded_) {
+    is_loaded_ = true;
+    UpdateTheme();
+    DoAction();
+    AddDevToolsExtensionsToClient();
+  } else if (type == chrome::NOTIFICATION_TAB_CLOSING) {
+    if (content::Source<NavigationController>(source).ptr() ==
+            &web_contents_->GetController()) {
+      // This happens when browser closes all of its tabs as a result
+      // of window.Close event.
+      // Notify manager that this DevToolsClientHost no longer exists and
+      // initiate self-destuct here.
+      DevToolsManager::GetInstance()->ClientHostClosing(frontend_host_.get());
+      UpdateBrowserToolbar();
+      delete this;
+    }
+  } else if (type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED) {
+    UpdateTheme();
+  }
+}
+
+WebContents* DevToolsWindow::OpenURLFromTab(WebContents* source,
+                                            const OpenURLParams& params) {
+  if (!params.url.SchemeIs(chrome::kChromeDevToolsScheme)) {
+    content::WebContents* inspected_web_contents = GetInspectedWebContents();
+    if (inspected_web_contents)
+      return inspected_web_contents->OpenURL(params);
+    return NULL;
+  }
+
+  DevToolsManager* manager = DevToolsManager::GetInstance();
+  scoped_refptr<DevToolsAgentHost> agent_host(
+      manager->GetDevToolsAgentHostFor(frontend_host_.get()));
+  if (!agent_host.get())
+    return NULL;
+  manager->ClientHostClosing(frontend_host_.get());
+  manager->RegisterDevToolsClientHostFor(agent_host.get(),
+                                         frontend_host_.get());
+
+  chrome::NavigateParams nav_params(profile_, params.url, params.transition);
+  FillNavigateParamsFromOpenURLParams(&nav_params, params);
+  nav_params.source_contents = source;
+  nav_params.tabstrip_add_types = TabStripModel::ADD_NONE;
+  nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
+  nav_params.user_gesture = params.user_gesture;
+  chrome::Navigate(&nav_params);
+  return nav_params.target_contents;
 }
 
 void DevToolsWindow::AddNewContents(WebContents* source,
@@ -781,6 +750,9 @@
   }
 }
 
+void DevToolsWindow::CloseContents(content::WebContents* source) {
+}
+
 bool DevToolsWindow::PreHandleKeyboardEvent(
     WebContents* source,
     const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) {
@@ -806,66 +778,33 @@
   }
 }
 
-// static
-DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
-    RenderViewHost* inspected_rvh,
-    bool force_open,
-    DevToolsToggleAction action) {
-  scoped_refptr<DevToolsAgentHost> agent(
-      DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
-  DevToolsManager* manager = DevToolsManager::GetInstance();
-  DevToolsWindow* window = FindDevToolsWindow(agent.get());
-  bool do_open = force_open;
-  if (!window) {
-    Profile* profile = Profile::FromBrowserContext(
-        inspected_rvh->GetProcess()->GetBrowserContext());
-    DevToolsDockSide dock_side = GetDockSideFromPrefs(profile);
-    window = Create(profile, GURL(), inspected_rvh, dock_side, false);
-    manager->RegisterDevToolsClientHostFor(agent.get(),
-                                           window->frontend_host_.get());
-    do_open = true;
+content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager() {
+  content::WebContents* inspected_web_contents = GetInspectedWebContents();
+  if (inspected_web_contents && inspected_web_contents->GetDelegate()) {
+    return inspected_web_contents->GetDelegate()->
+        GetJavaScriptDialogManager();
   }
-
-  // Update toolbar to reflect DevTools changes.
-  window->UpdateBrowserToolbar();
-
-  // If window is docked and visible, we hide it on toggle. If window is
-  // undocked, we show (activate) it. If window is minimized, we maximize it.
-  if (window->dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED)
-    window->Restore();
-  else if (!window->IsDocked() || do_open)
-    window->Show(action);
-  else
-    window->CloseWindow();
-
-  return window;
+  return content::WebContentsDelegate::GetJavaScriptDialogManager();
 }
 
-// static
-DevToolsWindow* DevToolsWindow::FindDevToolsWindow(
-    DevToolsAgentHost* agent_host) {
-  DevToolsManager* manager = DevToolsManager::GetInstance();
-  DevToolsWindowList& instances = g_instances.Get();
-  for (DevToolsWindowList::iterator it = instances.begin();
-       it != instances.end(); ++it) {
-    if (manager->GetDevToolsAgentHostFor((*it)->frontend_host_.get()) ==
-        agent_host)
-      return *it;
-  }
-  return NULL;
+content::ColorChooser* DevToolsWindow::OpenColorChooser(
+    WebContents* web_contents, SkColor initial_color) {
+  return chrome::ShowColorChooser(web_contents, initial_color);
 }
 
-// static
-DevToolsWindow* DevToolsWindow::AsDevToolsWindow(RenderViewHost* window_rvh) {
-  if (g_instances == NULL)
-    return NULL;
-  DevToolsWindowList& instances = g_instances.Get();
-  for (DevToolsWindowList::iterator it = instances.begin();
-       it != instances.end(); ++it) {
-    if ((*it)->web_contents_->GetRenderViewHost() == window_rvh)
-      return *it;
+void DevToolsWindow::RunFileChooser(WebContents* web_contents,
+                                    const FileChooserParams& params) {
+  FileSelectHelper::RunFileChooser(web_contents, params);
+}
+
+void DevToolsWindow::WebContentsFocused(WebContents* contents) {
+  Browser* inspected_browser = NULL;
+  int inspected_tab_index = -1;
+
+  if (IsDocked() && FindInspectedBrowserAndTabIndex(&inspected_browser,
+                                                    &inspected_tab_index)) {
+    inspected_browser->window()->WebContentsFocused(contents);
   }
-  return NULL;
 }
 
 void DevToolsWindow::ActivateWindow() {
@@ -954,11 +893,6 @@
   Show(DEVTOOLS_TOGGLE_ACTION_SHOW);
 }
 
-void DevToolsWindow::Restore() {
-  if (dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED)
-    SetDockSide(SideToString(dock_side_before_minimized_));
-}
-
 void DevToolsWindow::OpenInNewTab(const std::string& url) {
   OpenURLParams params(GURL(url),
                        content::Referrer(),
@@ -1006,19 +940,6 @@
                                           url));
 }
 
-namespace {
-
-DictionaryValue* CreateFileSystemValue(
-    DevToolsFileHelper::FileSystem file_system) {
-  DictionaryValue* file_system_value = new DictionaryValue();
-  file_system_value->SetString("fileSystemName", file_system.file_system_name);
-  file_system_value->SetString("rootURL", file_system.root_url);
-  file_system_value->SetString("fileSystemPath", file_system.file_system_path);
-  return file_system_value;
-}
-
-} // namespace
-
 void DevToolsWindow::RequestFileSystems() {
   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
   file_helper_->RequestFileSystems(
@@ -1092,35 +1013,191 @@
   }
 }
 
-content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager() {
+void DevToolsWindow::CreateDevToolsBrowser() {
+  std::string wp_key = GetDevToolsWindowPlacementPrefKey();
+  PrefService* prefs = profile_->GetPrefs();
+  const DictionaryValue* wp_pref = prefs->GetDictionary(wp_key.c_str());
+  if (!wp_pref || wp_pref->empty()) {
+    DictionaryPrefUpdate update(prefs, wp_key.c_str());
+    DictionaryValue* defaults = update.Get();
+    defaults->SetInteger("left", 100);
+    defaults->SetInteger("top", 100);
+    defaults->SetInteger("right", 740);
+    defaults->SetInteger("bottom", 740);
+    defaults->SetBoolean("maximized", false);
+    defaults->SetBoolean("always_on_top", false);
+  }
+
+  chrome::HostDesktopType host_desktop_type =
+      chrome::GetHostDesktopTypeForNativeView(
+          web_contents_->GetView()->GetNativeView());
+
+  browser_ = new Browser(Browser::CreateParams::CreateForDevTools(
+                             profile_, host_desktop_type));
+  browser_->tab_strip_model()->AddWebContents(
+      web_contents_, -1, content::PAGE_TRANSITION_AUTO_TOPLEVEL,
+      TabStripModel::ADD_ACTIVE);
+  GetRenderViewHost()->SyncRendererPrefs();
+}
+
+bool DevToolsWindow::FindInspectedBrowserAndTabIndex(Browser** browser,
+                                                     int* tab) {
   content::WebContents* inspected_web_contents = GetInspectedWebContents();
-  if (inspected_web_contents && inspected_web_contents->GetDelegate()) {
-    return inspected_web_contents->GetDelegate()->
-        GetJavaScriptDialogManager();
+  if (!inspected_web_contents)
+    return false;
+
+  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
+    int tab_index = it->tab_strip_model()->GetIndexOfWebContents(
+        inspected_web_contents);
+    if (tab_index != TabStripModel::kNoTab) {
+      *browser = *it;
+      *tab = tab_index;
+      return true;
+    }
   }
-  return content::WebContentsDelegate::GetJavaScriptDialogManager();
+  return false;
 }
 
-content::ColorChooser* DevToolsWindow::OpenColorChooser(
-    WebContents* web_contents, SkColor initial_color) {
-  return chrome::ShowColorChooser(web_contents, initial_color);
+BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
+  Browser* browser = NULL;
+  int tab;
+  return FindInspectedBrowserAndTabIndex(&browser, &tab) ?
+      browser->window() : NULL;
 }
 
-void DevToolsWindow::RunFileChooser(WebContents* web_contents,
-                                    const FileChooserParams& params) {
-  FileSelectHelper::RunFileChooser(web_contents, params);
+bool DevToolsWindow::IsInspectedBrowserPopup() {
+  Browser* browser = NULL;
+  int tab;
+  if (!FindInspectedBrowserAndTabIndex(&browser, &tab))
+    return false;
+
+  return browser->is_type_popup();
 }
 
-void DevToolsWindow::WebContentsFocused(WebContents* contents) {
-  Browser* inspected_browser = NULL;
-  int inspected_tab_index = -1;
+void DevToolsWindow::UpdateFrontendDockSide() {
+  base::StringValue dock_side(SideToString(dock_side_));
+  CallClientFunction("InspectorFrontendAPI.setDockSide", &dock_side);
+  base::FundamentalValue docked(IsDocked());
+  CallClientFunction("InspectorFrontendAPI.setAttachedWindow", &docked);
+}
 
-  if (IsDocked() && FindInspectedBrowserAndTabIndex(&inspected_browser,
-                                                    &inspected_tab_index)) {
-    inspected_browser->window()->WebContentsFocused(contents);
+void DevToolsWindow::Hide() {
+  if (IsDocked()) {
+    // Update dev tools to reflect removed dev tools window.
+    BrowserWindow* inspected_window = GetInspectedBrowserWindow();
+    if (inspected_window)
+      inspected_window->UpdateDevTools();
+    // In case of docked web_contents_, we own it so delete here.
+    delete web_contents_;
+
+    delete this;
+  } else {
+    // First, initiate self-destruct to free all the registrars.
+    // Then close all tabs. Browser will take care of deleting web_contents_
+    // for us.
+    Browser* browser = browser_;
+    delete this;
+    browser->tab_strip_model()->CloseAllTabs();
   }
 }
 
+void DevToolsWindow::ScheduleAction(DevToolsToggleAction action) {
+  action_on_load_ = action;
+  if (is_loaded_)
+    DoAction();
+}
+
+void DevToolsWindow::DoAction() {
+  UpdateFrontendDockSide();
+  switch (action_on_load_) {
+    case DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE:
+      CallClientFunction("InspectorFrontendAPI.showConsole", NULL);
+      break;
+    case DEVTOOLS_TOGGLE_ACTION_INSPECT:
+      CallClientFunction("InspectorFrontendAPI.enterInspectElementMode", NULL);
+    case DEVTOOLS_TOGGLE_ACTION_SHOW:
+    case DEVTOOLS_TOGGLE_ACTION_TOGGLE:
+      // Do nothing.
+      break;
+    default:
+      NOTREACHED();
+  }
+  action_on_load_ = DEVTOOLS_TOGGLE_ACTION_SHOW;
+}
+
+void DevToolsWindow::UpdateTheme() {
+  ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_);
+  CHECK(tp);
+
+  SkColor color_toolbar =
+      tp->GetColor(ThemeProperties::COLOR_TOOLBAR);
+  SkColor color_tab_text =
+      tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
+  std::string command = base::StringPrintf(
+      "InspectorFrontendAPI.setToolbarColors(\"%s\", \"%s\")",
+      SkColorToRGBAString(color_toolbar).c_str(),
+      SkColorToRGBAString(color_tab_text).c_str());
+  web_contents_->GetRenderViewHost()->
+      ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(command));
+}
+
+void DevToolsWindow::AddDevToolsExtensionsToClient() {
+  content::WebContents* inspected_web_contents = GetInspectedWebContents();
+  if (inspected_web_contents) {
+    SessionTabHelper* session_tab_helper =
+        SessionTabHelper::FromWebContents(inspected_web_contents);
+    if (session_tab_helper) {
+      base::FundamentalValue tabId(session_tab_helper->session_id().id());
+      CallClientFunction("WebInspector.setInspectedTabId", &tabId);
+    }
+  }
+  ListValue results;
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
+  const ExtensionService* extension_service = extensions::ExtensionSystem::Get(
+      profile->GetOriginalProfile())->extension_service();
+  if (!extension_service)
+    return;
+
+  const ExtensionSet* extensions = extension_service->extensions();
+
+  for (ExtensionSet::const_iterator extension = extensions->begin();
+       extension != extensions->end(); ++extension) {
+    if (extensions::ManifestURL::GetDevToolsPage(extension->get()).is_empty())
+      continue;
+    DictionaryValue* extension_info = new DictionaryValue();
+    extension_info->Set(
+        "startPage",
+        new StringValue(
+            extensions::ManifestURL::GetDevToolsPage(extension->get()).spec()));
+    extension_info->Set("name", new StringValue((*extension)->name()));
+    bool allow_experimental = (*extension)->HasAPIPermission(
+        extensions::APIPermission::kExperimental);
+    extension_info->Set("exposeExperimentalAPIs",
+        new base::FundamentalValue(allow_experimental));
+    results.Append(extension_info);
+  }
+  CallClientFunction("WebInspector.addExtensions", &results);
+}
+
+void DevToolsWindow::CallClientFunction(const std::string& function_name,
+                                        const Value* arg1,
+                                        const Value* arg2) {
+  std::string params;
+  if (arg1) {
+    std::string json;
+    base::JSONWriter::Write(arg1, &json);
+    params.append(json);
+    if (arg2) {
+      base::JSONWriter::Write(arg2, &json);
+      params.append(", " + json);
+    }
+  }
+  string16 javascript = ASCIIToUTF16(function_name + "(" + params + ");");
+  web_contents_->GetRenderViewHost()->
+      ExecuteJavascriptInWebFrame(string16(), javascript);
+}
+
 void DevToolsWindow::UpdateBrowserToolbar() {
   content::WebContents* inspected_web_contents = GetInspectedWebContents();
   if (!inspected_web_contents)
@@ -1134,48 +1211,13 @@
   return dock_side_ != DEVTOOLS_DOCK_SIDE_UNDOCKED;
 }
 
-// static
-DevToolsDockSide DevToolsWindow::GetDockSideFromPrefs(Profile* profile) {
-  std::string dock_side =
-      profile->GetPrefs()->GetString(prefs::kDevToolsDockSide);
-
-  // Migrate prefs
-  if (dock_side == kOldPrefBottom || dock_side == kOldPrefRight) {
-    bool docked = profile->GetPrefs()->GetBoolean(prefs::kDevToolsOpenDocked);
-    if (dock_side == kOldPrefBottom)
-      return docked ? DEVTOOLS_DOCK_SIDE_BOTTOM : DEVTOOLS_DOCK_SIDE_UNDOCKED;
-    else
-      return docked ? DEVTOOLS_DOCK_SIDE_RIGHT : DEVTOOLS_DOCK_SIDE_UNDOCKED;
-  }
-
-  if (dock_side == kPrefUndocked)
-    return DEVTOOLS_DOCK_SIDE_UNDOCKED;
-  else if (dock_side == kPrefRight)
-    return DEVTOOLS_DOCK_SIDE_RIGHT;
-  // Default to docked to bottom
-  return DEVTOOLS_DOCK_SIDE_BOTTOM;
+void DevToolsWindow::Restore() {
+  if (dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED)
+    SetDockSide(SideToString(dock_side_before_minimized_));
 }
 
-// static
-std::string DevToolsWindow::SideToString(DevToolsDockSide dock_side) {
-  std::string dock_side_string;
-  switch (dock_side) {
-    case DEVTOOLS_DOCK_SIDE_UNDOCKED: return kDockSideUndocked;
-    case DEVTOOLS_DOCK_SIDE_RIGHT: return kDockSideRight;
-    case DEVTOOLS_DOCK_SIDE_BOTTOM: return kDockSideBottom;
-    case DEVTOOLS_DOCK_SIDE_MINIMIZED: return kDockSideMinimized;
-  }
-  return kDockSideUndocked;
-}
-
-// static
-DevToolsDockSide DevToolsWindow::SideFromString(
-    const std::string& dock_side) {
-  if (dock_side == kDockSideRight)
-    return DEVTOOLS_DOCK_SIDE_RIGHT;
-  if (dock_side == kDockSideBottom)
-    return DEVTOOLS_DOCK_SIDE_BOTTOM;
-  if (dock_side == kDockSideMinimized)
-    return DEVTOOLS_DOCK_SIDE_MINIMIZED;
-  return DEVTOOLS_DOCK_SIDE_UNDOCKED;
+content::WebContents* DevToolsWindow::GetInspectedWebContents() {
+  if (!inspected_contents_observer_)
+    return NULL;
+  return inspected_contents_observer_->web_contents();
 }
diff --git a/chrome/browser/devtools/devtools_window.h b/chrome/browser/devtools/devtools_window.h
index cce9d81..19765c0 100644
--- a/chrome/browser/devtools/devtools_window.h
+++ b/chrome/browser/devtools/devtools_window.h
@@ -57,12 +57,14 @@
                        private content::DevToolsFrontendHostDelegate {
  public:
   static const char kDevToolsApp[];
+
+  virtual ~DevToolsWindow();
+
   static std::string GetDevToolsWindowPlacementPrefKey();
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
   static DevToolsWindow* GetDockedInstanceForInspectedTab(
       content::WebContents* inspected_tab);
   static bool IsDevToolsWindow(content::RenderViewHost* window_rvh);
-
   static DevToolsWindow* OpenDevToolsWindowForWorker(
       Profile* profile,
       content::DevToolsAgentHost* worker_agent);
@@ -84,18 +86,18 @@
   static void InspectElement(
       content::RenderViewHost* inspected_rvh, int x, int y);
 
-  virtual ~DevToolsWindow();
+  static int GetMinimumWidth();
+  static int GetMinimumHeight();
+  static int GetMinimizedHeight();
 
   // Overridden from DevToolsClientHost.
   virtual void InspectedContentsClosing() OVERRIDE;
-  content::RenderViewHost* GetRenderViewHost();
-
-  void Show(DevToolsToggleAction action);
 
   content::WebContents* web_contents() { return web_contents_; }
   Browser* browser() { return browser_; }  // For tests.
   DevToolsDockSide dock_side() { return dock_side_; }
 
+  content::RenderViewHost* GetRenderViewHost();
   content::DevToolsClientHost* GetDevToolsClientHostForTest();
 
   // Returns preferred devtools window width for given |container_width|. It
@@ -110,56 +112,42 @@
   // Called only for the case when devtools window is docked to bottom.
   int GetHeight(int container_height);
 
-  // Returns the minimum width devtools window needs.
-  int GetMinimumWidth();
-
-  // Returns the minimum height devtools window needs.
-  int GetMinimumHeight();
-
   // Stores preferred devtools window width for this instance.
   void SetWidth(int width);
 
   // Stores preferred devtools window height for this instance.
   void SetHeight(int height);
 
-  // Returns the height in minimized mode.
-  int GetMinimizedHeight();
+  void Show(DevToolsToggleAction action);
 
  private:
   friend class DevToolsControllerTest;
-  static DevToolsWindow* Create(Profile* profile,
-                                const GURL& frontend_url,
-                                content::RenderViewHost* inspected_rvh,
-                                DevToolsDockSide dock_side,
-                                bool shared_worker_frontend);
+
   DevToolsWindow(Profile* profile,
                  const GURL& frontend_url,
                  content::RenderViewHost* inspected_rvh,
                  DevToolsDockSide dock_side);
 
-  void CreateDevToolsBrowser();
-  bool FindInspectedBrowserAndTabIndex(Browser**, int* tab);
-  BrowserWindow* GetInspectedBrowserWindow();
-  bool IsInspectedBrowserPopup();
-  void UpdateFrontendDockSide();
-  void Hide();
+  static DevToolsWindow* Create(Profile* profile,
+                                const GURL& frontend_url,
+                                content::RenderViewHost* inspected_rvh,
+                                DevToolsDockSide dock_side,
+                                bool shared_worker_frontend);
+  static GURL GetDevToolsURL(Profile* profile,
+                             const GURL& base_url,
+                             DevToolsDockSide dock_side,
+                             bool shared_worker_frontend);
+  static DevToolsWindow* FindDevToolsWindow(content::DevToolsAgentHost*);
+  static DevToolsWindow* AsDevToolsWindow(content::RenderViewHost*);
+  static DevToolsDockSide GetDockSideFromPrefs(Profile* profile);
+  static std::string SideToString(DevToolsDockSide dock_side);
+  static DevToolsDockSide SideFromString(const std::string& dock_side);
 
   // Overridden from content::NotificationObserver.
   virtual void Observe(int type,
                        const content::NotificationSource& source,
                        const content::NotificationDetails& details) OVERRIDE;
 
-  void ScheduleAction(DevToolsToggleAction action);
-  void DoAction();
-  static GURL GetDevToolsURL(Profile* profile,
-                             const GURL& base_url,
-                             DevToolsDockSide dock_side,
-                             bool shared_worker_frontend);
-  void UpdateTheme();
-  void AddDevToolsExtensionsToClient();
-  void CallClientFunction(const std::string& function_name,
-                          const base::Value* arg1 = NULL,
-                          const base::Value* arg2 = NULL);
   // Overridden from content::WebContentsDelegate.
   virtual content::WebContents* OpenURLFromTab(
       content::WebContents* source,
@@ -170,7 +158,7 @@
                               const gfx::Rect& initial_pos,
                               bool user_gesture,
                               bool* was_blocked) OVERRIDE;
-  virtual void CloseContents(content::WebContents* source) OVERRIDE {}
+  virtual void CloseContents(content::WebContents* source) OVERRIDE;
   virtual bool PreHandleKeyboardEvent(
       content::WebContents* source,
       const content::NativeWebKeyboardEvent& event,
@@ -188,9 +176,6 @@
       const content::FileChooserParams& params) OVERRIDE;
   virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE;
 
-  static DevToolsWindow* FindDevToolsWindow(content::DevToolsAgentHost*);
-  static DevToolsWindow* AsDevToolsWindow(content::RenderViewHost*);
-
   // content::DevToolsFrontendHostDelegate overrides.
   virtual void ActivateWindow() OVERRIDE;
   virtual void ChangeAttachedWindowHeight(unsigned height) OVERRIDE;
@@ -212,18 +197,27 @@
   void AppendedTo(const std::string& url);
   void FileSystemsLoaded(
       const std::vector<DevToolsFileHelper::FileSystem>& file_systems);
+  void FileSystemAdded(const DevToolsFileHelper::FileSystem& file_system);
   void ShowDevToolsConfirmInfoBar(
       const string16& message,
       const base::Callback<void(bool)>& callback);
-  void FileSystemAdded(const DevToolsFileHelper::FileSystem& file_system);
 
+  void CreateDevToolsBrowser();
+  bool FindInspectedBrowserAndTabIndex(Browser**, int* tab);
+  BrowserWindow* GetInspectedBrowserWindow();
+  bool IsInspectedBrowserPopup();
+  void UpdateFrontendDockSide();
+  void Hide();
+  void ScheduleAction(DevToolsToggleAction action);
+  void DoAction();
+  void UpdateTheme();
+  void AddDevToolsExtensionsToClient();
+  void CallClientFunction(const std::string& function_name,
+                          const base::Value* arg1 = NULL,
+                          const base::Value* arg2 = NULL);
   void UpdateBrowserToolbar();
   bool IsDocked();
   void Restore();
-  static DevToolsDockSide GetDockSideFromPrefs(Profile* profile);
-  static std::string SideToString(DevToolsDockSide dock_side);
-  static DevToolsDockSide SideFromString(const std::string& dock_side);
-
   content::WebContents* GetInspectedWebContents();
 
   class InspectedWebContentsObserver;
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index e1e1f42..a810537 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -422,7 +422,7 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 #if !defined(OS_ANDROID)
   ExtensionDownloadsEventRouter* router =
-      DownloadServiceFactory::GetForProfile(profile_)->
+      DownloadServiceFactory::GetForBrowserContext(profile_)->
       GetExtensionEventRouter();
   if (router) {
     base::Closure original_path_callback =
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index cba4e55..c065b85 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -59,6 +59,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_manager.h"
@@ -83,10 +84,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::BrowserContext;
 using content::BrowserThread;
 using content::DownloadItem;
@@ -389,12 +386,13 @@
       : profile_(profile),
         waiting_(false),
         seen_stored_(false) {
-    DownloadServiceFactory::GetForProfile(profile_)->
+    DownloadServiceFactory::GetForBrowserContext(profile_)->
       GetDownloadHistory()->AddObserver(this);
   }
 
   virtual ~HistoryObserver() {
-    DownloadService* service = DownloadServiceFactory::GetForProfile(profile_);
+    DownloadService* service = DownloadServiceFactory::GetForBrowserContext(
+        profile_);
     if (service && service->GetDownloadHistory())
       service->GetDownloadHistory()->RemoveObserver(this);
   }
@@ -415,7 +413,7 @@
   }
 
   virtual void OnDownloadHistoryDestroyed() OVERRIDE {
-    DownloadServiceFactory::GetForProfile(profile_)->
+    DownloadServiceFactory::GetForBrowserContext(profile_)->
       GetDownloadHistory()->RemoveObserver(this);
   }
 
@@ -1854,7 +1852,7 @@
 IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadHistoryDangerCheck) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -2784,7 +2782,7 @@
 IN_PROC_BROWSER_TEST_F(DownloadTest, TestMultipleDownloadsInfobar) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -3067,9 +3065,9 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadPrefs_SaveFilePath) {
-  DownloadPrefs* on_prefs = DownloadServiceFactory::GetForProfile(
+  DownloadPrefs* on_prefs = DownloadServiceFactory::GetForBrowserContext(
       browser()->profile())->GetDownloadManagerDelegate()->download_prefs();
-  DownloadPrefs* off_prefs = DownloadServiceFactory::GetForProfile(
+  DownloadPrefs* off_prefs = DownloadServiceFactory::GetForBrowserContext(
       browser()->profile()->GetOffTheRecordProfile())
     ->GetDownloadManagerDelegate()->download_prefs();
   base::FilePath dir(on_prefs->SaveFilePath());
diff --git a/chrome/browser/download/download_danger_prompt.cc b/chrome/browser/download/download_danger_prompt.cc
index 7368b0b..fc0948d 100644
--- a/chrome/browser/download/download_danger_prompt.cc
+++ b/chrome/browser/download/download_danger_prompt.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
 #include "content/public/browser/download_danger_type.h"
 #include "content/public/browser/download_item.h"
+#include "grit/chromium_strings.h"
 #include "grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -41,6 +42,7 @@
   virtual string16 GetAcceptButtonTitle() OVERRIDE;
   virtual void OnAccepted() OVERRIDE;
   virtual void OnCanceled() OVERRIDE;
+  virtual void OnClosed() OVERRIDE;
 
   void RunDone(Action action);
 
@@ -117,7 +119,7 @@
           download_->GetFileNameToReportUser().LossyDisplayName());
     case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
       return l10n_util::GetStringFUTF16(
-          IDS_PROMPT_POTENTIALLY_UNWANTED_DOWNLOAD,
+          IDS_PROMPT_DOWNLOAD_CHANGES_SEARCH_SETTINGS,
           download_->GetFileNameToReportUser().LossyDisplayName());
     case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
     case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
@@ -142,6 +144,10 @@
   RunDone(CANCEL);
 }
 
+void DownloadDangerPromptImpl::OnClosed() {
+  RunDone(CANCEL);
+}
+
 void DownloadDangerPromptImpl::RunDone(Action action) {
   // Invoking the callback can cause the download item state to change or cause
   // the constrained window to close, and |callback| refers to a member
diff --git a/chrome/browser/download/download_history.cc b/chrome/browser/download/download_history.cc
index 758dc86..acda0ed 100644
--- a/chrome/browser/download/download_history.cc
+++ b/chrome/browser/download/download_history.cc
@@ -37,6 +37,10 @@
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_manager.h"
 
+#if !defined(OS_ANDROID)
+#include "chrome/browser/extensions/api/downloads/downloads_api.h"
+#endif
+
 namespace {
 
 // Per-DownloadItem data. This information does not belong inside DownloadItem,
@@ -96,6 +100,15 @@
 
 history::DownloadRow GetDownloadRow(
     content::DownloadItem* item) {
+  std::string by_ext_id, by_ext_name;
+#if !defined(OS_ANDROID)
+  DownloadedByExtension* by_ext = DownloadedByExtension::Get(item);
+  if (by_ext) {
+    by_ext_id = by_ext->id();
+    by_ext_name = by_ext->name();
+  }
+#endif
+
   return history::DownloadRow(
       item->GetFullPath(),
       item->GetTargetFilePath(),
@@ -109,7 +122,9 @@
       item->GetDangerType(),
       item->GetLastReason(),
       item->GetId(),
-      item->GetOpened());
+      item->GetOpened(),
+      by_ext_id,
+      by_ext_name);
 }
 
 bool ShouldUpdateHistory(const history::DownloadRow* previous,
@@ -124,7 +139,9 @@
           (previous->state != current.state) ||
           (previous->danger_type != current.danger_type) ||
           (previous->interrupt_reason != current.interrupt_reason) ||
-          (previous->opened != current.opened));
+          (previous->opened != current.opened) ||
+          (previous->by_ext_id != current.by_ext_id) ||
+          (previous->by_ext_name != current.by_ext_name));
 }
 
 typedef std::vector<history::DownloadRow> InfoVector;
@@ -222,6 +239,12 @@
         it->danger_type,
         it->interrupt_reason,
         it->opened);
+#if !defined(OS_ANDROID)
+    if (!it->by_ext_id.empty() && !it->by_ext_name.empty()) {
+      new DownloadedByExtension(item, it->by_ext_id, it->by_ext_name);
+      item->UpdateObservers();
+    }
+#endif
     DCHECK_EQ(DownloadHistoryData::Get(item)->state(),
               DownloadHistoryData::PERSISTED);
     ++history_size_;
diff --git a/chrome/browser/download/download_item_model.cc b/chrome/browser/download/download_item_model.cc
index 22c14ff..ac386be 100644
--- a/chrome/browser/download/download_item_model.cc
+++ b/chrome/browser/download/download_item_model.cc
@@ -327,7 +327,7 @@
 
     case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
       return l10n_util::GetStringFUTF16(
-          IDS_PROMPT_POTENTIALLY_UNWANTED_DOWNLOAD, elided_filename);
+          IDS_PROMPT_DOWNLOAD_CHANGES_SEARCH_SETTINGS, elided_filename);
 
     case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
     case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
diff --git a/chrome/browser/download/download_service.cc b/chrome/browser/download/download_service.cc
index 34e5c49..bb75459 100644
--- a/chrome/browser/download/download_service.cc
+++ b/chrome/browser/download/download_service.cc
@@ -102,9 +102,9 @@
   int count = 0;
   for (std::vector<Profile*>::iterator it = profiles.begin();
        it < profiles.end(); ++it) {
-    count += DownloadServiceFactory::GetForProfile(*it)->DownloadCount();
+    count += DownloadServiceFactory::GetForBrowserContext(*it)->DownloadCount();
     if ((*it)->HasOffTheRecordProfile())
-      count += DownloadServiceFactory::GetForProfile(
+      count += DownloadServiceFactory::GetForBrowserContext(
           (*it)->GetOffTheRecordProfile())->DownloadCount();
   }
 
@@ -125,6 +125,15 @@
   }
 }
 
+bool DownloadService::IsShelfEnabled() {
+#if defined(OS_ANDROID)
+  return true;
+#else
+  return !extension_event_router_ ||
+         extension_event_router_->IsShelfEnabled();
+#endif
+}
+
 void DownloadService::Shutdown() {
   if (download_manager_created_) {
     // Normally the DownloadManager would be shutdown later, after the Profile
diff --git a/chrome/browser/download/download_service.h b/chrome/browser/download/download_service.h
index 30e72e3..3c3f6b6 100644
--- a/chrome/browser/download/download_service.h
+++ b/chrome/browser/download/download_service.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_SERVICE_H_
 #define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_SERVICE_H_
 
-#include <vector>
-
 #include "base/basictypes.h"
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
@@ -62,6 +60,10 @@
   // of Profile shutdown.
   virtual void Shutdown() OVERRIDE;
 
+  // Returns false if at least one extension has disabled the shelf, true
+  // otherwise.
+  bool IsShelfEnabled();
+
  private:
   bool download_manager_created_;
   Profile* profile_;
diff --git a/chrome/browser/download/download_service_factory.cc b/chrome/browser/download/download_service_factory.cc
index 28bab78..29b3d17 100644
--- a/chrome/browser/download/download_service_factory.cc
+++ b/chrome/browser/download/download_service_factory.cc
@@ -10,10 +10,10 @@
 #include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
 
 // static
-DownloadService* DownloadServiceFactory::GetForProfile(
-    Profile* profile) {
+DownloadService* DownloadServiceFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
   return static_cast<DownloadService*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
+      GetInstance()->GetServiceForBrowserContext(context, true));
 }
 
 // static
diff --git a/chrome/browser/download/download_service_factory.h b/chrome/browser/download/download_service_factory.h
index f98803f..ed78587 100644
--- a/chrome/browser/download/download_service_factory.h
+++ b/chrome/browser/download/download_service_factory.h
@@ -10,15 +10,15 @@
 #include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
 
 class DownloadService;
-class Profile;
 
 // Singleton that owns all DownloadServices and associates them with
 // Profiles. Listens for the Profile's destruction notification and cleans up
 // the associated DownloadService.
 class DownloadServiceFactory : public BrowserContextKeyedServiceFactory {
  public:
-  // Returns the DownloadService for |profile|, creating if not yet created.
-  static DownloadService* GetForProfile(Profile* profile);
+  // Returns the DownloadService for |context|, creating if not yet created.
+  static DownloadService* GetForBrowserContext(
+      content::BrowserContext* context);
 
   static DownloadServiceFactory* GetInstance();
 
diff --git a/chrome/browser/download/download_shelf.cc b/chrome/browser/download/download_shelf.cc
index d6b8fb6..f262e0a 100644
--- a/chrome/browser/download/download_shelf.cc
+++ b/chrome/browser/download/download_shelf.cc
@@ -8,6 +8,8 @@
 #include "base/callback.h"
 #include "base/message_loop/message_loop.h"
 #include "chrome/browser/download/download_item_model.h"
+#include "chrome/browser/download/download_service.h"
+#include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/download/download_started_animation.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -96,15 +98,16 @@
 }
 
 content::DownloadManager* DownloadShelf::GetDownloadManager() {
-  DCHECK(browser());
   return content::BrowserContext::GetDownloadManager(browser()->profile());
 }
 
 void DownloadShelf::ShowDownload(DownloadItem* download) {
   if (download->GetState() == DownloadItem::COMPLETE &&
-      DownloadItemModel(download).ShouldRemoveFromShelfWhenComplete()) {
+      DownloadItemModel(download).ShouldRemoveFromShelfWhenComplete())
     return;
-  }
+  if (!DownloadServiceFactory::GetForBrowserContext(
+        download->GetBrowserContext())->IsShelfEnabled())
+    return;
 
   if (is_hidden_)
     Unhide();
diff --git a/chrome/browser/download/download_shelf_unittest.cc b/chrome/browser/download/download_shelf_unittest.cc
index 3a16773..c16701b 100644
--- a/chrome/browser/download/download_shelf_unittest.cc
+++ b/chrome/browser/download/download_shelf_unittest.cc
@@ -6,8 +6,14 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/download_item_model.h"
+#include "chrome/browser/download/download_service.h"
+#include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/download/test_download_shelf.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/notification_service.h"
 #include "content/public/test/mock_download_item.h"
 #include "content/public/test/mock_download_manager.h"
 #include "content/public/test/test_browser_thread.h"
@@ -22,6 +28,11 @@
 
 namespace {
 
+BrowserContextKeyedService* CreateDownloadService(
+    content::BrowserContext* context) {
+  return new DownloadService(Profile::FromBrowserContext(context));
+}
+
 class DownloadShelfTest : public testing::Test {
  public:
   DownloadShelfTest();
@@ -36,6 +47,17 @@
   TestDownloadShelf* shelf() {
     return &shelf_;
   }
+  Profile* profile() { return profile_.get(); }
+
+  virtual void SetUp() OVERRIDE {
+    DownloadServiceFactory::GetInstance()->SetTestingFactory(
+        profile(), &CreateDownloadService);
+  }
+
+  virtual void TearDown() OVERRIDE {
+    DownloadServiceFactory::GetInstance()->SetTestingFactory(
+        profile(), NULL);
+  }
 
  private:
   scoped_ptr<content::MockDownloadItem> GetInProgressMockDownload();
@@ -45,10 +67,12 @@
   scoped_ptr<content::MockDownloadItem> download_item_;
   scoped_ptr<content::MockDownloadManager> download_manager_;
   TestDownloadShelf shelf_;
+  scoped_ptr<TestingProfile> profile_;
 };
 
 DownloadShelfTest::DownloadShelfTest()
-    : ui_thread_(content::BrowserThread::UI, &message_loop_) {
+    : ui_thread_(content::BrowserThread::UI, &message_loop_),
+      profile_(new TestingProfile()) {
   download_item_.reset(new ::testing::NiceMock<content::MockDownloadItem>());
   ON_CALL(*download_item_, GetAutoOpened()).WillByDefault(Return(false));
   ON_CALL(*download_item_, GetMimeType()).WillByDefault(Return("text/plain"));
@@ -62,11 +86,15 @@
   ON_CALL(*download_item_, IsTemporary()).WillByDefault(Return(false));
   ON_CALL(*download_item_, ShouldOpenFileBasedOnExtension())
       .WillByDefault(Return(false));
+  ON_CALL(*download_item_, GetBrowserContext())
+      .WillByDefault(Return(profile()));
 
   download_manager_.reset(
       new ::testing::NiceMock<content::MockDownloadManager>());
   ON_CALL(*download_manager_, GetDownload(_))
       .WillByDefault(Return(download_item_.get()));
+  ON_CALL(*download_manager_, GetBrowserContext())
+      .WillByDefault(Return(profile()));
 
   shelf_.set_download_manager(download_manager_.get());
 }
diff --git a/chrome/browser/download/download_test_file_activity_observer.cc b/chrome/browser/download/download_test_file_activity_observer.cc
index ee14166..5768726 100644
--- a/chrome/browser/download/download_test_file_activity_observer.cc
+++ b/chrome/browser/download/download_test_file_activity_observer.cc
@@ -63,7 +63,7 @@
 DownloadTestFileActivityObserver::DownloadTestFileActivityObserver(
     Profile* profile) {
   test_delegate_ = new MockDownloadManagerDelegate(profile);
-  DownloadServiceFactory::GetForProfile(profile)->
+  DownloadServiceFactory::GetForBrowserContext(profile)->
       SetDownloadManagerDelegateForTesting(test_delegate_.get());
 }
 
diff --git a/chrome/browser/download/save_page_browsertest.cc b/chrome/browser/download/save_page_browsertest.cc
index afaef69..f108f84 100644
--- a/chrome/browser/download/save_page_browsertest.cc
+++ b/chrome/browser/download/save_page_browsertest.cc
@@ -63,12 +63,13 @@
       filter_(filter),
       waiting_(false),
       persisted_(false) {
-    DownloadServiceFactory::GetForProfile(profile_)->
+    DownloadServiceFactory::GetForBrowserContext(profile_)->
       GetDownloadHistory()->AddObserver(this);
   }
 
   virtual ~DownloadPersistedObserver() {
-    DownloadService* service = DownloadServiceFactory::GetForProfile(profile_);
+    DownloadService* service = DownloadServiceFactory::GetForBrowserContext(
+        profile_);
     if (service && service->GetDownloadHistory())
       service->GetDownloadHistory()->RemoveObserver(this);
   }
diff --git a/chrome/browser/drive/drive_api_service.cc b/chrome/browser/drive/drive_api_service.cc
index 38659cc..71e8b92 100644
--- a/chrome/browser/drive/drive_api_service.cc
+++ b/chrome/browser/drive/drive_api_service.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/google_apis/drive_api_requests.h"
 #include "chrome/browser/google_apis/gdata_errorcode.h"
 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
+#include "chrome/browser/google_apis/gdata_wapi_requests.h"
 #include "chrome/browser/google_apis/request_sender.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -42,11 +43,13 @@
 using google_apis::GetFileRequest;
 using google_apis::GetFilelistRequest;
 using google_apis::GetResourceEntryCallback;
+using google_apis::GetResourceEntryRequest;
 using google_apis::GetResourceListCallback;
 using google_apis::GetShareUrlCallback;
 using google_apis::HTTP_NOT_IMPLEMENTED;
 using google_apis::HTTP_SUCCESS;
 using google_apis::InitiateUploadCallback;
+using google_apis::Link;
 using google_apis::ProgressCallback;
 using google_apis::RequestSender;
 using google_apis::ResourceEntry;
@@ -268,11 +271,13 @@
     base::TaskRunner* blocking_task_runner,
     const GURL& base_url,
     const GURL& base_download_url,
+    const GURL& wapi_base_url,
     const std::string& custom_user_agent)
     : oauth2_token_service_(oauth2_token_service),
       url_request_context_getter_(url_request_context_getter),
       blocking_task_runner_(blocking_task_runner),
       url_generator_(base_url, base_download_url),
+      wapi_url_generator_(wapi_base_url, base_download_url),
       custom_user_agent_(custom_user_agent) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
@@ -289,6 +294,11 @@
   std::vector<std::string> scopes;
   scopes.push_back(kDriveScope);
   scopes.push_back(kDriveAppsReadonlyScope);
+
+  // GData WAPI token. These are for GetShareUrl().
+  scopes.push_back(util::kDocsListScope);
+  scopes.push_back(util::kDriveAppsScope);
+
   sender_.reset(new RequestSender(
      new google_apis::AuthService(
          oauth2_token_service_, url_request_context_getter_, scopes),
@@ -470,11 +480,17 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  // TODO(mtomasz): Implement this, once it is supported by the Drive API.
-  NOTIMPLEMENTED();
-  callback.Run(HTTP_NOT_IMPLEMENTED, GURL());
-
-  return CancelCallback();
+  // Unfortunately "share url" is not yet supported on Drive API v2.
+  // So, as a fallback, we use GData WAPI protocol for this method.
+  // TODO(hidehiko): Get rid of this implementation when share url is
+  // supported on Drive API v2.
+  return sender_->StartRequestWithRetry(
+      new GetResourceEntryRequest(sender_.get(),
+                                  wapi_url_generator_,
+                                  resource_id,
+                                  embed_origin,
+                                  base::Bind(&util::ParseShareUrlAndRun,
+                                             callback)));
 }
 
 CancelCallback DriveAPIService::GetAboutResource(
diff --git a/chrome/browser/drive/drive_api_service.h b/chrome/browser/drive/drive_api_service.h
index 66b2de5..8f506da 100644
--- a/chrome/browser/drive/drive_api_service.h
+++ b/chrome/browser/drive/drive_api_service.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/google_apis/auth_service_interface.h"
 #include "chrome/browser/google_apis/auth_service_observer.h"
 #include "chrome/browser/google_apis/drive_api_url_generator.h"
+#include "chrome/browser/google_apis/gdata_wapi_url_generator.h"
 
 class GURL;
 class OAuth2TokenService;
@@ -45,6 +46,9 @@
   // |base_url| is used to generate URLs for communication with the drive API.
   // |base_download_url| is used to generate URLs for downloading file from the
   // drive API.
+  // |wapi_base_url| is used to generate URLs for shared_url. Unfortunately,
+  // the share_url is not yet supported on Drive API v2, so as a fallback,
+  // we use GData WAPI.
   // |custom_user_agent| will be used for the User-Agent header in HTTP
   // requests issues through the service if the value is not empty.
   DriveAPIService(
@@ -53,6 +57,7 @@
       base::TaskRunner* blocking_task_runner,
       const GURL& base_url,
       const GURL& base_download_url,
+      const GURL& wapi_base_url,
       const std::string& custom_user_agent);
   virtual ~DriveAPIService();
 
@@ -179,6 +184,7 @@
   scoped_ptr<google_apis::RequestSender> sender_;
   ObserverList<DriveServiceObserver> observers_;
   google_apis::DriveApiUrlGenerator url_generator_;
+  google_apis::GDataWapiUrlGenerator wapi_url_generator_;
   const std::string custom_user_agent_;
 
   DISALLOW_COPY_AND_ASSIGN(DriveAPIService);
diff --git a/chrome/browser/drive/drive_api_util.cc b/chrome/browser/drive/drive_api_util.cc
index b9a8274..cd11548 100644
--- a/chrome/browser/drive/drive_api_util.cc
+++ b/chrome/browser/drive/drive_api_util.cc
@@ -12,7 +12,10 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
 #include "chrome/browser/drive/drive_switches.h"
+#include "chrome/browser/google_apis/gdata_wapi_parser.h"
+#include "content/public/browser/browser_thread.h"
 #include "net/base/escape.h"
 #include "third_party/re2/re2/re2.h"
 #include "url/gurl.h"
@@ -128,5 +131,31 @@
   return resource_id;
 }
 
+const char kDocsListScope[] = "https://docs.google.com/feeds/";
+const char kDriveAppsScope[] = "https://www.googleapis.com/auth/drive.apps";
+
+void ParseShareUrlAndRun(const google_apis::GetShareUrlCallback& callback,
+                         google_apis::GDataErrorCode error,
+                         scoped_ptr<base::Value> value) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  if (!value) {
+    callback.Run(error, GURL());
+    return;
+  }
+
+  // Parsing ResourceEntry is cheap enough to do on UI thread.
+  scoped_ptr<google_apis::ResourceEntry> entry =
+      google_apis::ResourceEntry::ExtractAndParse(*value);
+  if (!entry) {
+    callback.Run(google_apis::GDATA_PARSE_ERROR, GURL());
+    return;
+  }
+
+  const google_apis::Link* share_link =
+      entry->GetLinkByType(google_apis::Link::LINK_SHARE);
+  callback.Run(error, share_link ? share_link->href() : GURL());
+}
+
 }  // namespace util
 }  // namespace drive
diff --git a/chrome/browser/drive/drive_api_util.h b/chrome/browser/drive/drive_api_util.h
index 2d1d991..118f223 100644
--- a/chrome/browser/drive/drive_api_util.h
+++ b/chrome/browser/drive/drive_api_util.h
@@ -7,8 +7,16 @@
 
 #include <string>
 
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/google_apis/drive_common_callbacks.h"
+#include "chrome/browser/google_apis/gdata_errorcode.h"
+
 class GURL;
 
+namespace base {
+class Value;
+}  // namespace base
+
 namespace drive {
 namespace util {
 
@@ -37,6 +45,23 @@
 // into the new format.
 std::string CanonicalizeResourceId(const std::string& resource_id);
 
+// Note: Following constants and a function are used to support GetShareUrl on
+// Drive API v2. Unfortunately, there is no support on Drive API v2, so we need
+// to fall back to GData WAPI for the GetShareUrl. Thus, these are shared by
+// both GDataWapiService and DriveAPIService.
+// TODO(hidehiko): Remove these from here, when Drive API v2 supports
+// GetShareUrl.
+
+// OAuth2 scopes for the GData WAPI.
+extern const char kDocsListScope[];
+extern const char kDriveAppsScope[];
+
+// Extracts an url to the sharing dialog and returns it via |callback|. If
+// the share url doesn't exist, then an empty url is returned.
+void ParseShareUrlAndRun(const google_apis::GetShareUrlCallback& callback,
+                         google_apis::GDataErrorCode error,
+                         scoped_ptr<base::Value> value);
+
 }  // namespace util
 }  // namespace drive
 
diff --git a/chrome/browser/drive/event_logger.cc b/chrome/browser/drive/event_logger.cc
index 0ba4034..904435d 100644
--- a/chrome/browser/drive/event_logger.cc
+++ b/chrome/browser/drive/event_logger.cc
@@ -9,8 +9,10 @@
 
 namespace drive {
 
-EventLogger::Event::Event(int id, const std::string& what)
+EventLogger::Event::Event(
+    int id, logging::LogSeverity severity, const std::string& what)
     : id(id),
+      severity(severity),
       when(base::Time::Now()),
       what(what) {
 }
@@ -23,18 +25,9 @@
 EventLogger::~EventLogger() {
 }
 
-void EventLogger::Log(const char* format, ...) {
-  std::string what;
-
-  va_list args;
-  va_start(args, format);
-  base::StringAppendV(&what, format, args);
-  va_end(args);
-
-  DVLOG(1) << what;
-
+void EventLogger::Log(logging::LogSeverity severity, const std::string& what) {
   base::AutoLock auto_lock(lock_);
-  history_.push_back(Event(next_event_id_, what));
+  history_.push_back(Event(next_event_id_, severity, what));
   ++next_event_id_;
   if (history_.size() > history_size_)
     history_.pop_front();
diff --git a/chrome/browser/drive/event_logger.h b/chrome/browser/drive/event_logger.h
index 95a10e6..4bf0d78 100644
--- a/chrome/browser/drive/event_logger.h
+++ b/chrome/browser/drive/event_logger.h
@@ -5,13 +5,13 @@
 #ifndef CHROME_BROWSER_DRIVE_EVENT_LOGGER_H_
 #define CHROME_BROWSER_DRIVE_EVENT_LOGGER_H_
 
-#include <stdarg.h>   // va_list
 #include <deque>
 #include <string>
 #include <vector>
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
+#include "base/logging.h"
 #include "base/synchronization/lock.h"
 #include "base/time/time.h"
 
@@ -27,8 +27,9 @@
  public:
   // Represents a single event log.
   struct Event {
-    Event(int id, const std::string& what);
+    Event(int id, logging::LogSeverity severity, const std::string& what);
     int id;  // Monotonically increasing ID starting from 0.
+    logging::LogSeverity severity;  // Severity of the event.
     base::Time when;  // When the event occurred.
     std::string what;  // What happened.
   };
@@ -37,11 +38,9 @@
   EventLogger();
   ~EventLogger();
 
-  // Logs a message using printf format.
+  // Logs a message and its severity.
   // Can be called from any thread as long as the object is alive.
-  // Note that PRINTF_FORMAT should be (2, 3) instead of (1, 2) as this is a
-  // C++ member function.
-  void Log(const char* format, ...) PRINTF_FORMAT(2, 3);
+  void Log(logging::LogSeverity severity, const std::string& what);
 
   // Sets the history size. The existing history is cleared.
   // Can be called from any thread as long as the object is alive.
diff --git a/chrome/browser/drive/event_logger_unittest.cc b/chrome/browser/drive/event_logger_unittest.cc
index c57078f..2d41d2c 100644
--- a/chrome/browser/drive/event_logger_unittest.cc
+++ b/chrome/browser/drive/event_logger_unittest.cc
@@ -13,9 +13,9 @@
   logger.SetHistorySize(3);  // At most 3 events are kept.
   EXPECT_EQ(0U, logger.GetHistory().size());
 
-  logger.Log("first");
-  logger.Log("%dnd", 2);
-  logger.Log("third");
+  logger.Log(logging::LOG_INFO, "first");
+  logger.Log(logging::LOG_INFO, "2nd");
+  logger.Log(logging::LOG_INFO, "third");
 
   // Events are recorded in the chronological order with sequential IDs.
   std::vector<EventLogger::Event> history = logger.GetHistory();
@@ -27,7 +27,7 @@
   EXPECT_EQ(2, history[2].id);
   EXPECT_EQ("third", history[2].what);
 
-  logger.Log("fourth");
+  logger.Log(logging::LOG_INFO, "fourth");
   // It does not log events beyond the specified.
   history = logger.GetHistory();
   ASSERT_EQ(3U, history.size());
diff --git a/chrome/browser/drive/gdata_wapi_service.cc b/chrome/browser/drive/gdata_wapi_service.cc
index b9a2fbf..218eb6e 100644
--- a/chrome/browser/drive/gdata_wapi_service.cc
+++ b/chrome/browser/drive/gdata_wapi_service.cc
@@ -67,10 +67,8 @@
 namespace {
 
 // OAuth2 scopes for the documents API.
-const char kDocsListScope[] = "https://docs.google.com/feeds/";
 const char kSpreadsheetsScope[] = "https://spreadsheets.google.com/feeds/";
 const char kUserContentScope[] = "https://docs.googleusercontent.com/";
-const char kDriveAppsScope[] = "https://www.googleapis.com/auth/drive.apps";
 
 // The resource ID for the root directory for WAPI is defined in the spec:
 // https://developers.google.com/google-apps/documents-list/
@@ -98,30 +96,6 @@
   callback.Run(error, entry.Pass());
 }
 
-// Extracts an url to the sharing dialog and returns it via |callback|. If
-// the share url doesn't exist, then an empty url is returned.
-void ParseShareUrlAndRun(const GetShareUrlCallback& callback,
-                         GDataErrorCode error,
-                         scoped_ptr<base::Value> value) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (!value) {
-    callback.Run(error, GURL());
-    return;
-  }
-
-  // Parsing ResourceEntry is cheap enough to do on UI thread.
-  scoped_ptr<ResourceEntry> entry =
-      google_apis::ResourceEntry::ExtractAndParse(*value);
-  if (!entry) {
-    callback.Run(GDATA_PARSE_ERROR, GURL());
-    return;
-  }
-
-  const Link* share_link = entry->GetLinkByType(Link::LINK_SHARE);
-  callback.Run(error, share_link ? share_link->href() : GURL());
-}
-
 void ParseAboutResourceAndRun(
     const GetAboutResourceCallback& callback,
     GDataErrorCode error,
@@ -180,11 +154,11 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   std::vector<std::string> scopes;
-  scopes.push_back(kDocsListScope);
+  scopes.push_back(util::kDocsListScope);
   scopes.push_back(kSpreadsheetsScope);
   scopes.push_back(kUserContentScope);
   // Drive App scope is required for even WAPI v3 apps access.
-  scopes.push_back(kDriveAppsScope);
+  scopes.push_back(util::kDriveAppsScope);
   sender_.reset(new RequestSender(
       new AuthService(
           oauth2_token_service_, url_request_context_getter_, scopes),
@@ -347,7 +321,7 @@
                                   url_generator_,
                                   resource_id,
                                   embed_origin,
-                                  base::Bind(&ParseShareUrlAndRun,
+                                  base::Bind(&util::ParseShareUrlAndRun,
                                              callback)));
 }
 
diff --git a/chrome/browser/extensions/activity_log/activity_action_constants.cc b/chrome/browser/extensions/activity_log/activity_action_constants.cc
index eb04230..070fab3 100644
--- a/chrome/browser/extensions/activity_log/activity_action_constants.cc
+++ b/chrome/browser/extensions/activity_log/activity_action_constants.cc
@@ -12,6 +12,13 @@
 const char kActionBlockedReason[] = "blocked_reason";
 const char kActionDomVerb[] = "dom_verb";
 const char kActionExtra[] = "extra";
+const char kActionPrerender[] = "prerender";
 const char kActionWebRequest[] = "web_request";
 
+// A string used in place of the real URL when the URL is hidden because it is
+// in an incognito window.  Extension activity logs mentioning kIncognitoUrl
+// let the user know that an extension is manipulating incognito tabs without
+// recording specific data about the pages.
+const char kIncognitoUrl[] = "<incognito>";
+
 }  // namespace activity_log_constants
diff --git a/chrome/browser/extensions/activity_log/activity_action_constants.h b/chrome/browser/extensions/activity_log/activity_action_constants.h
index 9c8d7c6..143069d 100644
--- a/chrome/browser/extensions/activity_log/activity_action_constants.h
+++ b/chrome/browser/extensions/activity_log/activity_action_constants.h
@@ -13,8 +13,15 @@
 extern const char kActionBlockedReason[];
 extern const char kActionDomVerb[];
 extern const char kActionExtra[];
+extern const char kActionPrerender[];
 extern const char kActionWebRequest[];
 
+// A string used in place of the real URL when the URL is hidden because it is
+// in an incognito window.  Extension activity logs mentioning kIncognitoUrl
+// let the user know that an extension is manipulating incognito tabs without
+// recording specific data about the pages.
+extern const char kIncognitoUrl[];
+
 }  // namespace activity_log_constants
 
 #endif  // CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_ACTION_CONSTANTS_H_
diff --git a/chrome/browser/extensions/activity_log/activity_actions.cc b/chrome/browser/extensions/activity_log/activity_actions.cc
index d4603de..1d79225 100644
--- a/chrome/browser/extensions/activity_log/activity_actions.cc
+++ b/chrome/browser/extensions/activity_log/activity_actions.cc
@@ -2,23 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/extensions/activity_log/activity_actions.h"
+
 #include <string>
+
 #include "base/command_line.h"
 #include "base/json/json_string_value_serializer.h"
 #include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
-#include "chrome/browser/extensions/activity_log/activity_actions.h"
+#include "chrome/browser/extensions/activity_log/activity_action_constants.h"
 #include "chrome/browser/extensions/activity_log/api_name_constants.h"
 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
-#include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_switches.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "sql/statement.h"
 
+namespace constants = activity_log_constants;
+
 namespace {
 
 std::string Serialize(const base::Value* value) {
@@ -32,31 +36,6 @@
   return value_as_text;
 }
 
-// Gets the URL for a given tab ID. Helper method for APIAction::LookupTabId.
-std::string GetUrlForTabId(int tab_id, Profile* profile) {
-  content::WebContents* contents = NULL;
-  Browser* browser = NULL;
-  bool found = ExtensionTabUtil::GetTabById(tab_id,
-                                            profile,
-                                            true,  // search incognito tabs too
-                                            &browser,
-                                            NULL,
-                                            &contents,
-                                            NULL);
-  if (found) {
-    // Check whether the profile the tab was found in is a normal or incognito
-    // profile.
-    if (!browser->profile()->IsOffTheRecord()) {
-      GURL url = contents->GetURL();
-      return std::string(url.spec());
-    } else {
-      return std::string(extensions::APIAction::kIncognitoUrl);
-    }
-  } else {
-    return std::string();
-  }
-}
-
 // Sets up the hashmap for mapping extension strings to "ints". The hashmap is
 // only set up once because it's quite long; the value is then cached.
 class APINameMap {
@@ -123,61 +102,6 @@
 using api::activity_log_private::DomActivityDetail;
 using api::activity_log_private::ExtensionActivity;
 
-// We should log the arguments to these API calls, even if argument logging is
-// disabled by default.
-const char* APIAction::kAlwaysLog[] =
-    {"extension.connect", "extension.sendMessage",
-     "tabs.executeScript", "tabs.insertCSS" };
-const int APIAction::kSizeAlwaysLog = arraysize(kAlwaysLog);
-
-// A string used in place of the real URL when the URL is hidden because it is
-// in an incognito window.  Extension activity logs mentioning kIncognitoUrl
-// let the user know that an extension is manipulating incognito tabs without
-// recording specific data about the pages.
-const char* APIAction::kIncognitoUrl = "http://incognito/";
-
-// static
-void APIAction::LookupTabId(const std::string& api_call,
-                            base::ListValue* args,
-                            Profile* profile) {
-  if (api_call == "tabs.get" ||                 // api calls, ID as int
-      api_call == "tabs.connect" ||
-      api_call == "tabs.sendMessage" ||
-      api_call == "tabs.duplicate" ||
-      api_call == "tabs.update" ||
-      api_call == "tabs.reload" ||
-      api_call == "tabs.detectLanguage" ||
-      api_call == "tabs.executeScript" ||
-      api_call == "tabs.insertCSS" ||
-      api_call == "tabs.move" ||                // api calls, IDs in array
-      api_call == "tabs.remove" ||
-      api_call == "tabs.onUpdated" ||           // events, ID as int
-      api_call == "tabs.onMoved" ||
-      api_call == "tabs.onDetached" ||
-      api_call == "tabs.onAttached" ||
-      api_call == "tabs.onRemoved" ||
-      api_call == "tabs.onReplaced") {
-    int tab_id;
-    base::ListValue* id_list;
-    if (args->GetInteger(0, &tab_id)) {
-      std::string url = GetUrlForTabId(tab_id, profile);
-      if (url != std::string())
-        args->Set(0, new base::StringValue(url));
-    } else if ((api_call == "tabs.move" || api_call == "tabs.remove") &&
-               args->GetList(0, &id_list)) {
-      for (int i = 0; i < static_cast<int>(id_list->GetSize()); ++i) {
-        if (id_list->GetInteger(i, &tab_id)) {
-          std::string url = GetUrlForTabId(tab_id, profile);
-          if (url != std::string())
-            id_list->Set(i, new base::StringValue(url));
-        } else {
-          LOG(ERROR) << "The tab ID array is malformed at index " << i;
-        }
-      }
-    }
-  }
-}
-
 Action::Action(const std::string& extension_id,
                const base::Time& time,
                const ActionType action_type,
@@ -185,10 +109,30 @@
     : extension_id_(extension_id),
       time_(time),
       action_type_(action_type),
-      api_name_(api_name) {}
+      api_name_(api_name),
+      page_incognito_(false),
+      arg_incognito_(false) {}
 
 Action::~Action() {}
 
+// TODO(mvrable): As an optimization, we might return this directly if the
+// refcount is one.  However, there are likely to be other stray references in
+// many cases that will prevent this optimization.
+scoped_refptr<Action> Action::Clone() const {
+  scoped_refptr<Action> clone(
+      new Action(extension_id(), time(), action_type(), api_name()));
+  if (args())
+    clone->set_args(make_scoped_ptr(args()->DeepCopy()));
+  clone->set_page_url(page_url());
+  clone->set_page_title(page_title());
+  clone->set_page_incognito(page_incognito());
+  clone->set_arg_url(arg_url());
+  clone->set_arg_incognito(arg_incognito());
+  if (other())
+    clone->set_other(make_scoped_ptr(other()->DeepCopy()));
+  return clone;
+}
+
 void Action::set_args(scoped_ptr<ListValue> args) {
   args_.reset(args.release());
 }
@@ -219,50 +163,6 @@
   return other_.get();
 }
 
-bool Action::Record(sql::Connection* db) {
-  std::string sql_str =
-      "INSERT INTO " + std::string(FullStreamUIPolicy::kTableName) +
-      " (extension_id, time, action_type, api_name, args, "
-      "page_url, page_title, arg_url, other) VALUES (?,?,?,?,?,?,?,?,?)";
-  sql::Statement statement(db->GetCachedStatement(
-      sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
-  statement.BindString(0, extension_id());
-  statement.BindInt64(1, time().ToInternalValue());
-  statement.BindInt(2, static_cast<int>(action_type()));
-  statement.BindString(3, api_name());
-  if (args()) {
-    statement.BindString(4, Serialize(args()));
-  } else {
-    statement.BindNull(4);
-  }
-  if (other()) {
-    statement.BindString(8, Serialize(other()));
-  } else {
-    statement.BindNull(8);
-  }
-
-  url_canon::Replacements<char> url_sanitizer;
-  if (!CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableExtensionActivityLogTesting)) {
-    url_sanitizer.ClearQuery();
-    url_sanitizer.ClearRef();
-  }
-  if (page_url().is_valid()) {
-    statement.BindString(5, page_url().ReplaceComponents(url_sanitizer).spec());
-  }
-  statement.BindString(6, page_title());
-  if (arg_url().is_valid()) {
-    statement.BindString(7, arg_url().ReplaceComponents(url_sanitizer).spec());
-  }
-
-  if (!statement.Run()) {
-    LOG(ERROR) << "Activity log database I/O failed: " << sql_str;
-    statement.Clear();
-    return false;
-  }
-  return true;
-}
-
 scoped_ptr<ExtensionActivity> Action::ConvertToExtensionActivity() {
   scoped_ptr<ExtensionActivity> result(new ExtensionActivity);
 
@@ -305,7 +205,6 @@
     }
 
     case ACTION_DOM_EVENT:
-    case ACTION_DOM_XHR:
     case ACTION_DOM_ACCESS:
     case ACTION_CONTENT_SCRIPT:
     case ACTION_WEB_REQUEST: {
@@ -326,9 +225,13 @@
       details->api_call.reset(new std::string(api_name()));
       details->args.reset(new std::string(Serialize(args())));
       details->extra.reset(new std::string(Serialize(other())));
-      details->url.reset(new std::string(page_url().spec()));
-      if (!page_title().empty())
-        details->url_title.reset(new std::string(page_title()));
+      if (page_incognito()) {
+        details->url.reset(new std::string(constants::kIncognitoUrl));
+      } else {
+        details->url.reset(new std::string(page_url().spec()));
+        if (!page_title().empty())
+          details->url_title.reset(new std::string(page_title()));
+      }
 
       result->activity_type = ExtensionActivity::ACTIVITY_TYPE_DOM;
       result->dom_activity_detail.reset(details);
@@ -364,9 +267,6 @@
     case ACTION_DOM_EVENT:
       result += "dom_event";
       break;
-    case ACTION_DOM_XHR:
-      result += "dom_xhr";
-      break;
     case ACTION_DOM_ACCESS:
       result += "dom_access";
       break;
@@ -379,14 +279,20 @@
     result += " ARGS=" + Serialize(args_.get());
   }
   if (page_url_.is_valid()) {
-    result += " PAGE_URL=" + page_url_.spec();
+    if (page_incognito_)
+      result += " PAGE_URL=(incognito)" + page_url_.spec();
+    else
+      result += " PAGE_URL=" + page_url_.spec();
   }
   if (!page_title_.empty()) {
     StringValue title(page_title_);
     result += " PAGE_TITLE=" + Serialize(&title);
   }
   if (arg_url_.is_valid()) {
-    result += " ARG_URL=" + arg_url_.spec();
+    if (arg_incognito_)
+      result += " ARG_URL=(incognito)" + arg_url_.spec();
+    else
+      result += " ARG_URL=" + arg_url_.spec();
   }
   if (other_.get()) {
     result += " OTHER=" + Serialize(other_.get());
diff --git a/chrome/browser/extensions/activity_log/activity_actions.h b/chrome/browser/extensions/activity_log/activity_actions.h
index ba621af..6782b83 100644
--- a/chrome/browser/extensions/activity_log/activity_actions.h
+++ b/chrome/browser/extensions/activity_log/activity_actions.h
@@ -6,6 +6,8 @@
 #define CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_ACTIONS_H_
 
 #include <string>
+#include <vector>
+
 #include "base/memory/ref_counted_memory.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -31,10 +33,13 @@
     ACTION_CONTENT_SCRIPT = 3,
     ACTION_DOM_ACCESS = 4,
     ACTION_DOM_EVENT = 5,
-    ACTION_DOM_XHR = 6,
-    ACTION_WEB_REQUEST = 7,
+    ACTION_WEB_REQUEST = 6,
   };
 
+  // A useful shorthand for methods that take or return collections of Action
+  // objects.
+  typedef std::vector<scoped_refptr<Action> > ActionVector;
+
   // Creates a new activity log Action object.  The extension_id, time, and
   // type are immutable.  All other fields can be filled in with the
   // accessors/mutators below.
@@ -43,6 +48,9 @@
          const ActionType action_type,
          const std::string& api_name);
 
+  // Creates and returns a mutable copy of an Action.
+  scoped_refptr<Action> Clone() const;
+
   // The extension which caused this record to be generated.
   const std::string& extension_id() const { return extension_id_; }
 
@@ -78,14 +86,18 @@
   const GURL& arg_url() const { return arg_url_; }
   void set_arg_url(const GURL& arg_url);
 
+  // Get or set a flag indicating whether the page or argument values above
+  // refer to incognito pages.
+  bool page_incognito() const { return page_incognito_; }
+  void set_page_incognito(bool incognito) { page_incognito_ = incognito; }
+  bool arg_incognito() const { return arg_incognito_; }
+  void set_arg_incognito(bool incognito) { arg_incognito_ = incognito; }
+
   // A dictionary where any additional data can be stored.
   const DictionaryValue* other() const { return other_.get(); }
   void set_other(scoped_ptr<DictionaryValue> other);
   DictionaryValue* mutable_other();
 
-  // Record the action in the database.
-  bool Record(sql::Connection* db);
-
   // Flatten the activity's type-specific fields into an ExtensionActivity.
   scoped_ptr<api::activity_log_private::ExtensionActivity>
       ConvertToExtensionActivity();
@@ -107,56 +119,14 @@
   scoped_ptr<ListValue> args_;
   GURL page_url_;
   std::string page_title_;
+  bool page_incognito_;
   GURL arg_url_;
+  bool arg_incognito_;
   scoped_ptr<DictionaryValue> other_;
 
   DISALLOW_COPY_AND_ASSIGN(Action);
 };
 
-// Constants defined for various action types.
-// TODO(mvrable): These are here for compatibility but should be moved
-// elsewhere as cleanup progresses.
-class APIAction {
- public:
-  // These values should not be changed. Append any additional values to the
-  // end with sequential numbers.
-  enum Type {
-    CALL = 0,
-    EVENT_CALLBACK = 1,
-    UNKNOWN_TYPE = 2,
-  };
-
-  static const char* kAlwaysLog[];
-  static const int kSizeAlwaysLog;
-
-  static const char* kIncognitoUrl;
-
-  // Used to associate tab IDs with URLs. It will swap out the int in args with
-  // a URL as a string. If the tab is in incognito mode, we leave it alone as
-  // the original int. There is a small chance that the URL translation could
-  // be wrong, if the tab has already been navigated by the time of invocation.
-  static void LookupTabId(const std::string& api_call,
-                          base::ListValue* args,
-                          Profile* profile);
-
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(APIAction);
-};
-
-class BlockedAction {
- public:
-  // These values should not be changed. Append any additional values to the
-  // end with sequential numbers.
-  enum Reason {
-      UNKNOWN = 0,
-      ACCESS_DENIED = 1,
-      QUOTA_EXCEEDED = 2,
-  };
-
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(BlockedAction);
-};
-
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_ACTIONS_H_
diff --git a/chrome/browser/extensions/activity_log/activity_database.cc b/chrome/browser/extensions/activity_log/activity_database.cc
index 3f02b9c..810c758 100644
--- a/chrome/browser/extensions/activity_log/activity_database.cc
+++ b/chrome/browser/extensions/activity_log/activity_database.cc
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/extensions/activity_log/activity_database.h"
+
 #include <string>
+
 #include "base/command_line.h"
-#include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -12,7 +14,6 @@
 #include "base/threading/thread_checker.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
-#include "chrome/browser/extensions/activity_log/activity_database.h"
 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
 #include "chrome/common/chrome_switches.h"
 #include "sql/error_delegate_util.h"
@@ -29,7 +30,6 @@
 
 ActivityDatabase::ActivityDatabase(ActivityDatabase::Delegate* delegate)
     : delegate_(delegate),
-      testing_clock_(NULL),
       valid_db_(false),
       already_closed_(false),
       did_init_(false) {
@@ -68,7 +68,7 @@
   base::mac::SetFileBackupExclusion(db_name);
 #endif
 
-  if (!delegate_->OnDatabaseInit(&db_))
+  if (!delegate_->InitDatabase(&db_))
     return LogInitFailure();
 
   sql::InitStatus stat = committer.Commit() ? sql::INIT_OK : sql::INIT_FAILURE;
@@ -91,27 +91,18 @@
   SoftFailureClose();
 }
 
-void ActivityDatabase::RecordAction(scoped_refptr<Action> action) {
-  if (!valid_db_) return;
-  if (batch_mode_) {
-    batched_actions_.push_back(action);
-  } else {
-    if (!action->Record(&db_)) SoftFailureClose();
+void ActivityDatabase::NotifyAction() {
+  if (valid_db_ && !batch_mode_) {
+    if (!delegate_->FlushDatabase(&db_))
+      SoftFailureClose();
   }
 }
 
 void ActivityDatabase::RecordBatchedActions() {
-  if (!valid_db_) return;
-  bool failure = false;
-  std::vector<scoped_refptr<Action> >::size_type i;
-  for (i = 0; i != batched_actions_.size(); ++i) {
-    if (!batched_actions_.at(i)->Record(&db_)) {
-      failure = true;
-      break;
-    }
+  if (valid_db_) {
+    if (!delegate_->FlushDatabase(&db_))
+      SoftFailureClose();
   }
-  batched_actions_.clear();
-  if (failure) SoftFailureClose();
 }
 
 void ActivityDatabase::SetBatchModeForTesting(bool batch_mode) {
@@ -127,85 +118,15 @@
   batch_mode_ = batch_mode;
 }
 
-scoped_ptr<ActivityDatabase::ActionVector> ActivityDatabase::GetActions(
-    const std::string& extension_id, const int days_ago) {
+sql::Connection* ActivityDatabase::GetSqlConnection() {
   if (BrowserThread::IsMessageLoopValid(BrowserThread::DB))
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
-  DCHECK_GE(days_ago, 0);
-
-  scoped_ptr<ActionVector> actions(new ActionVector());
-  if (!valid_db_)
-    return actions.Pass();
-  // Compute the time bounds for that day.
-  base::Time morning_midnight = testing_clock_ ?
-      testing_clock_->Now().LocalMidnight() :
-      base::Time::Now().LocalMidnight();
-  int64 early_bound = 0;
-  int64 late_bound = 0;
-  if (days_ago == 0) {
-      early_bound = morning_midnight.ToInternalValue();
-      late_bound = base::Time::Max().ToInternalValue();
+  if (valid_db_) {
+    return &db_;
   } else {
-      base::Time early_time = morning_midnight -
-          base::TimeDelta::FromDays(days_ago);
-      base::Time late_time = morning_midnight -
-          base::TimeDelta::FromDays(days_ago-1);
-      early_bound = early_time.ToInternalValue();
-      late_bound = late_time.ToInternalValue();
+    LOG(WARNING) << "Activity log database is not valid";
+    return NULL;
   }
-  std::string query_str = base::StringPrintf(
-      "SELECT time, action_type, api_name, args, page_url, page_title, "
-      "arg_url, other "
-      "FROM %s WHERE extension_id=? AND time>? AND time<=? "
-      "ORDER BY time DESC",
-      FullStreamUIPolicy::kTableName);
-  sql::Statement query(db_.GetCachedStatement(SQL_FROM_HERE,
-                                              query_str.c_str()));
-  query.BindString(0, extension_id);
-  query.BindInt64(1, early_bound);
-  query.BindInt64(2, late_bound);
-  while (query.is_valid() && query.Step()) {
-    scoped_refptr<Action> action =
-        new Action(extension_id,
-                   base::Time::FromInternalValue(query.ColumnInt64(0)),
-                   static_cast<Action::ActionType>(query.ColumnInt(1)),
-                   query.ColumnString(2));
-
-    if (query.ColumnType(3) != sql::COLUMN_TYPE_NULL) {
-      scoped_ptr<Value> parsed_value(
-          base::JSONReader::Read(query.ColumnString(3)));
-      if (parsed_value && parsed_value->IsType(Value::TYPE_LIST)) {
-        action->set_args(
-            make_scoped_ptr(static_cast<ListValue*>(parsed_value.release())));
-      } else {
-        LOG(WARNING) << "Unable to parse args: '" << query.ColumnString(3)
-                     << "'";
-      }
-    }
-
-    GURL page_url(query.ColumnString(4));
-    action->set_page_url(page_url);
-
-    action->set_page_title(query.ColumnString(5));
-
-    GURL arg_url(query.ColumnString(6));
-    action->set_arg_url(arg_url);
-
-    if (query.ColumnType(7) != sql::COLUMN_TYPE_NULL) {
-      scoped_ptr<Value> parsed_value(
-          base::JSONReader::Read(query.ColumnString(7)));
-      if (parsed_value && parsed_value->IsType(Value::TYPE_DICTIONARY)) {
-        action->set_other(make_scoped_ptr(
-            static_cast<DictionaryValue*>(parsed_value.release())));
-      } else {
-        LOG(WARNING) << "Unable to parse other: '" << query.ColumnString(7)
-                     << "'";
-      }
-    }
-
-    actions->push_back(action);
-  }
-  return actions.Pass();
 }
 
 void ActivityDatabase::Close() {
@@ -228,12 +149,14 @@
   timer_.Stop();
   db_.reset_error_callback();
   db_.RazeAndClose();
+  delegate_->OnDatabaseFailure();
   already_closed_ = true;
 }
 
 void ActivityDatabase::SoftFailureClose() {
   valid_db_ = false;
   timer_.Stop();
+  delegate_->OnDatabaseFailure();
 }
 
 void ActivityDatabase::DatabaseErrorCallback(int error, sql::Statement* stmt) {
@@ -247,10 +170,6 @@
   }
 }
 
-void ActivityDatabase::SetClockForTesting(base::Clock* clock) {
-  testing_clock_ = clock;
-}
-
 void ActivityDatabase::RecordBatchedActionsWhileTesting() {
   RecordBatchedActions();
   timer_.Stop();
diff --git a/chrome/browser/extensions/activity_log/activity_database.h b/chrome/browser/extensions/activity_log/activity_database.h
index 188c5fb..ea2cff9 100644
--- a/chrome/browser/extensions/activity_log/activity_database.h
+++ b/chrome/browser/extensions/activity_log/activity_database.h
@@ -7,6 +7,7 @@
 
 #include <string>
 #include <vector>
+
 #include "base/basictypes.h"
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
@@ -74,7 +75,16 @@
 
     // Initializes the database schema; this gives a policy a chance to create
     // or update database tables as needed.  Should return true on success.
-    virtual bool OnDatabaseInit(sql::Connection* db) = 0;
+    virtual bool InitDatabase(sql::Connection* db) = 0;
+
+    // Requests that the policy flush any pending actions to the database.
+    // Should return true on success or false on a database error.
+    virtual bool FlushDatabase(sql::Connection* db) = 0;
+
+    // Called if the database encounters a permanent error; the policy should
+    // not expect to make any future writes to the database and may want to
+    // discard any queued data.
+    virtual void OnDatabaseFailure() = 0;
 
     // Called by ActivityDatabase just before the ActivityDatabase object is
     // deleted.  The database will make no further callbacks after invoking
@@ -83,9 +93,6 @@
     virtual void OnDatabaseClose() = 0;
   };
 
-  // Used to simplify the return type of GetActions.
-  typedef std::vector<scoped_refptr<Action> > ActionVector;
-
   // Need to call Init to actually use the ActivityDatabase.  The Delegate
   // provides hooks for an ActivityLogPolicy to control the database schema and
   // reads/writes.
@@ -98,20 +105,18 @@
   // An ActivityLogPolicy should call this to kill the ActivityDatabase.
   void Close();
 
-  // Record an Action in the database.
-  void RecordAction(scoped_refptr<Action> action);
+  // Inform the database that there may be additional data which could be
+  // written out.
+  // TODO(mvrable): Add a method to force a database flush, or perhaps pass
+  // hints to the database about how much data is queued up so the database can
+  // flush before the timeout if there is a large amount of data?
+  void NotifyAction();
 
   // Turns off batch I/O writing mode. This should only be used in unit tests,
   // browser tests, or in our special --enable-extension-activity-log-testing
   // policy state.
   void SetBatchModeForTesting(bool batch_mode);
 
-  // Gets all actions for a given extension for the specified day. 0 = today,
-  // 1 = yesterday, etc. Only returns 1 day at a time. Actions are sorted from
-  // newest to oldest.
-  scoped_ptr<ActionVector> GetActions(const std::string& extension_id,
-                                      const int days_ago);
-
   bool is_db_valid() const { return valid_db_; }
 
   // A helper method for initializing or upgrading a database table.  The
@@ -125,6 +130,11 @@
                               const char* field_types[],
                               const int num_content_fields);
 
+  // Runs the given callback, passing it a handle to the database connection.
+  // If the database is not valid, the callback is run (to allow it to do any
+  // needed cleanup) but passed a NULL value.
+  void RunOnDatabase(const base::Callback<void(sql::Connection*)>& callback);
+
  private:
   // This should never be invoked by another class. Use Close() to order a
   // suicide.
@@ -157,22 +167,25 @@
 
   // For unit testing only.
   void RecordBatchedActionsWhileTesting();
-  void SetClockForTesting(base::Clock* clock);
   void SetTimerForTesting(int milliseconds);
 
+  // Retrieve a handle to the raw SQL database.  This is only intended to be
+  // used by ActivityLogDatabasePolicy::GetDatabaseConnection(), and should
+  // only be called on the database thread.
+  sql::Connection* GetSqlConnection();
+
   // A reference a Delegate for policy-specific database behavior.  See the
   // top-level comment for ActivityDatabase for comments on cleanup.
   Delegate* delegate_;
 
-  base::Clock* testing_clock_;
   sql::Connection db_;
   bool valid_db_;
   bool batch_mode_;
-  std::vector<scoped_refptr<Action> > batched_actions_;
   base::RepeatingTimer<ActivityDatabase> timer_;
   bool already_closed_;
   bool did_init_;
 
+  friend class ActivityLogDatabasePolicy;
   FRIEND_TEST_ALL_PREFIXES(ActivityDatabaseTest, BatchModeOff);
   FRIEND_TEST_ALL_PREFIXES(ActivityDatabaseTest, BatchModeOn);
   DISALLOW_COPY_AND_ASSIGN(ActivityDatabase);
diff --git a/chrome/browser/extensions/activity_log/activity_database_unittest.cc b/chrome/browser/extensions/activity_log/activity_database_unittest.cc
index ab9d869..b8f9d91 100644
--- a/chrome/browser/extensions/activity_log/activity_database_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_database_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <string>
+
 #include "base/command_line.h"
 #include "base/file_util.h"
 #include "base/files/file_path.h"
@@ -46,23 +47,64 @@
  public:
   ActivityDatabaseTestPolicy() {};
 
+  static const char kTableName[];
+  static const char* kTableContentFields[];
+  static const char* kTableFieldTypes[];
+
+  virtual void Record(ActivityDatabase* db, scoped_refptr<Action> action);
+
  protected:
-  virtual bool OnDatabaseInit(sql::Connection* db) OVERRIDE {
-    return ActivityDatabase::InitializeTable(
-        db,
-        FullStreamUIPolicy::kTableName,
-        FullStreamUIPolicy::kTableContentFields,
-        FullStreamUIPolicy::kTableFieldTypes,
-        FullStreamUIPolicy::kTableFieldCount);
+  virtual bool InitDatabase(sql::Connection* db) OVERRIDE;
+  virtual bool FlushDatabase(sql::Connection*) OVERRIDE;
+  virtual void OnDatabaseFailure() OVERRIDE {}
+  virtual void OnDatabaseClose() OVERRIDE { delete this; }
+
+  std::vector<scoped_refptr<Action> > queue_;
+};
+
+const char ActivityDatabaseTestPolicy::kTableName[] = "actions";
+const char* ActivityDatabaseTestPolicy::kTableContentFields[] = {
+    "extension_id", "time", "action_type", "api_name"};
+const char* ActivityDatabaseTestPolicy::kTableFieldTypes[] = {
+    "LONGVARCHAR NOT NULL", "INTEGER", "INTEGER", "LONGVARCHAR"};
+
+bool ActivityDatabaseTestPolicy::InitDatabase(sql::Connection* db) {
+  return ActivityDatabase::InitializeTable(db,
+                                           kTableName,
+                                           kTableContentFields,
+                                           kTableFieldTypes,
+                                           arraysize(kTableContentFields));
+}
+
+bool ActivityDatabaseTestPolicy::FlushDatabase(sql::Connection* db) {
+  std::string sql_str =
+      "INSERT INTO " + std::string(kTableName) +
+      " (extension_id, time, action_type, api_name) VALUES (?,?,?,?)";
+
+  std::vector<scoped_refptr<Action> >::size_type i;
+  for (i = 0; i < queue_.size(); i++) {
+    const Action& action = *queue_[i];
+    sql::Statement statement(db->GetCachedStatement(
+        sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
+    statement.BindString(0, action.extension_id());
+    statement.BindInt64(1, action.time().ToInternalValue());
+    statement.BindInt(2, static_cast<int>(action.action_type()));
+    statement.BindString(3, action.api_name());
+    if (!statement.Run()) {
+      LOG(ERROR) << "Activity log database I/O failed: " << sql_str;
+      return false;
+    }
   }
 
-  // Called by ActivityDatabase just before the ActivityDatabase object is
-  // deleted.  The database will make no further callbacks after invoking this
-  // method, so it is an appropriate time for the policy to delete itself.
-  virtual void OnDatabaseClose() OVERRIDE {
-    delete this;
-  }
-};
+  queue_.clear();
+  return true;
+}
+
+void ActivityDatabaseTestPolicy::Record(ActivityDatabase* db,
+                                        scoped_refptr<Action> action) {
+  queue_.push_back(action);
+  db->NotifyAction();
+}
 
 class ActivityDatabaseTest : public ChromeRenderViewHostTestHarness {
  protected:
@@ -84,14 +126,38 @@
   }
 
   // Creates a test database and initializes the table schema.
-  ActivityDatabase* OpenDatabase(const base::FilePath& db_file) const {
-    ActivityDatabase* activity_db =
-        new ActivityDatabase(new ActivityDatabaseTestPolicy());
+  ActivityDatabase* OpenDatabase(const base::FilePath& db_file) {
+    db_delegate_ = new ActivityDatabaseTestPolicy();
+    ActivityDatabase* activity_db = new ActivityDatabase(db_delegate_);
     activity_db->Init(db_file);
     CHECK(activity_db->is_db_valid());
     return activity_db;
   }
 
+  scoped_refptr<Action> CreateAction(const base::Time& time,
+                                     const std::string& api_name) const {
+    scoped_refptr<Action> action(
+        new Action("punky", time, Action::ACTION_API_CALL, api_name));
+    return action;
+  }
+
+  void Record(ActivityDatabase* db, scoped_refptr<Action> action) {
+    db_delegate_->Record(db, action);
+  }
+
+  int CountActions(sql::Connection* db, const std::string& api_name_pattern) {
+    if (!db->DoesTableExist(ActivityDatabaseTestPolicy::kTableName))
+      return -1;
+    std::string sql_str = "SELECT COUNT(*) FROM " +
+                          std::string(ActivityDatabaseTestPolicy::kTableName) +
+                          " WHERE api_name LIKE ?";
+    sql::Statement statement(db->GetUniqueStatement(sql_str.c_str()));
+    statement.BindString(0, api_name_pattern);
+    if (!statement.Step())
+      return -1;
+    return statement.ColumnInt(0);
+  }
+
  private:
 #if defined OS_CHROMEOS
   chromeos::ScopedStubNetworkLibraryEnabler stub_network_library_enabler_;
@@ -100,6 +166,7 @@
   scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
 #endif
 
+  ActivityDatabaseTestPolicy* db_delegate_;
 };
 
 // Check that the database is initialized properly.
@@ -115,7 +182,7 @@
 
   sql::Connection db;
   ASSERT_TRUE(db.Open(db_file));
-  ASSERT_TRUE(db.DoesTableExist(FullStreamUIPolicy::kTableName));
+  ASSERT_TRUE(db.DoesTableExist(ActivityDatabaseTestPolicy::kTableName));
   db.Close();
 }
 
@@ -129,142 +196,14 @@
 
   ActivityDatabase* activity_db = OpenDatabase(db_file);
   activity_db->SetBatchModeForTesting(false);
-  scoped_refptr<Action> action(new Action(
-      "punky", base::Time::Now(), Action::ACTION_API_CALL, "brewster"));
-  action->mutable_args()->AppendString("woof");
-  action->set_page_url(GURL("http://www.google.com/"));
-  action->mutable_other()->SetString(constants::kActionExtra, "extra");
-  activity_db->RecordAction(action);
+  scoped_refptr<Action> action = CreateAction(base::Time::Now(), "brewster");
+  Record(activity_db, action);
   activity_db->Close();
 
   sql::Connection db;
   ASSERT_TRUE(db.Open(db_file));
 
-  ASSERT_TRUE(db.DoesTableExist(FullStreamUIPolicy::kTableName));
-  std::string sql_str = "SELECT * FROM " +
-      std::string(FullStreamUIPolicy::kTableName);
-  sql::Statement statement(db.GetUniqueStatement(sql_str.c_str()));
-  ASSERT_TRUE(statement.Step());
-  ASSERT_EQ("punky", statement.ColumnString(0));
-  ASSERT_EQ(static_cast<int>(Action::ACTION_API_CALL), statement.ColumnInt(2));
-  ASSERT_EQ("brewster", statement.ColumnString(3));
-  ASSERT_EQ("[\"woof\"]", statement.ColumnString(4));
-  ASSERT_EQ("http://www.google.com/", statement.ColumnString(5));
-  ASSERT_EQ("{\"extra\":\"extra\"}", statement.ColumnString(8));
-}
-
-// Check that we can read back recent actions in the db.
-TEST_F(ActivityDatabaseTest, GetTodaysActions) {
-  base::ScopedTempDir temp_dir;
-  base::FilePath db_file;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  db_file = temp_dir.path().AppendASCII("ActivityRecord.db");
-  base::DeleteFile(db_file, false);
-
-  // Use a mock clock to ensure that events are not recorded on the wrong day
-  // when the test is run close to local midnight.
-  base::SimpleTestClock mock_clock;
-  mock_clock.SetNow(base::Time::Now().LocalMidnight() +
-                    base::TimeDelta::FromHours(12));
-
-  // Record some actions
-  ActivityDatabase* activity_db = OpenDatabase(db_file);
-
-  scoped_refptr<Action> action;
-  action = new Action("punky",
-                      mock_clock.Now() - base::TimeDelta::FromMinutes(40),
-                      Action::ACTION_API_CALL,
-                      "brewster");
-  action->mutable_args()->AppendString("woof");
-  activity_db->RecordAction(action);
-
-  action =
-      new Action("punky", mock_clock.Now(), Action::ACTION_DOM_ACCESS, "lets");
-  action->mutable_args()->AppendString("vamoose");
-  action->set_page_url(GURL("http://www.google.com"));
-  activity_db->RecordAction(action);
-
-  action = new Action(
-      "scoobydoo", mock_clock.Now(), Action::ACTION_DOM_ACCESS, "lets");
-  action->mutable_args()->AppendString("vamoose");
-  action->set_page_url(GURL("http://www.google.com"));
-  activity_db->RecordAction(action);
-
-  // Read them back
-  std::string api_print =
-      "ID=punky CATEGORY=api_call API=brewster ARGS=[\"woof\"]";
-  std::string dom_print =
-      "ID=punky CATEGORY=dom_access API=lets ARGS=[\"vamoose\"] "
-      "PAGE_URL=http://www.google.com/";
-  scoped_ptr<std::vector<scoped_refptr<Action> > > actions =
-      activity_db->GetActions("punky", 0);
-  ASSERT_EQ(2, static_cast<int>(actions->size()));
-  ASSERT_EQ(dom_print, actions->at(0)->PrintForDebug());
-  ASSERT_EQ(api_print, actions->at(1)->PrintForDebug());
-
-  activity_db->Close();
-}
-
-// Check that we can read back less recent actions in the db.
-TEST_F(ActivityDatabaseTest, GetOlderActions) {
-  base::ScopedTempDir temp_dir;
-  base::FilePath db_file;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  db_file = temp_dir.path().AppendASCII("ActivityRecord.db");
-  base::DeleteFile(db_file, false);
-
-  // Use a mock clock to ensure that events are not recorded on the wrong day
-  // when the test is run close to local midnight.
-  base::SimpleTestClock mock_clock;
-  mock_clock.SetNow(base::Time::Now().LocalMidnight() +
-                    base::TimeDelta::FromHours(12));
-
-  // Record some actions
-  ActivityDatabase* activity_db = OpenDatabase(db_file);
-  scoped_refptr<Action> action =
-      new Action("punky",
-                 mock_clock.Now() - base::TimeDelta::FromDays(3) -
-                     base::TimeDelta::FromMinutes(40),
-                 Action::ACTION_API_CALL,
-                 "brewster");
-  action->mutable_args()->AppendString("woof");
-  activity_db->RecordAction(action);
-
-  action = new Action("punky",
-                      mock_clock.Now() - base::TimeDelta::FromDays(3),
-                      Action::ACTION_DOM_ACCESS,
-                      "lets");
-  action->mutable_args()->AppendString("vamoose");
-  action->set_page_url(GURL("http://www.google.com"));
-  activity_db->RecordAction(action);
-
-  action =
-      new Action("punky", mock_clock.Now(), Action::ACTION_DOM_ACCESS, "lets");
-  action->mutable_args()->AppendString("too new");
-  action->set_page_url(GURL("http://www.google.com"));
-  activity_db->RecordAction(action);
-
-  action = new Action("punky",
-                      mock_clock.Now() - base::TimeDelta::FromDays(7),
-                      Action::ACTION_DOM_ACCESS,
-                      "lets");
-  action->mutable_args()->AppendString("too old");
-  action->set_page_url(GURL("http://www.google.com"));
-  activity_db->RecordAction(action);
-
-  // Read them back
-  std::string api_print =
-      "ID=punky CATEGORY=api_call API=brewster ARGS=[\"woof\"]";
-  std::string dom_print =
-      "ID=punky CATEGORY=dom_access API=lets ARGS=[\"vamoose\"] "
-      "PAGE_URL=http://www.google.com/";
-  scoped_ptr<std::vector<scoped_refptr<Action> > > actions =
-      activity_db->GetActions("punky", 3);
-  ASSERT_EQ(2, static_cast<int>(actions->size()));
-  ASSERT_EQ(dom_print, actions->at(0)->PrintForDebug());
-  ASSERT_EQ(api_print, actions->at(1)->PrintForDebug());
-
-  activity_db->Close();
+  ASSERT_EQ(1, CountActions(&db, "brewster"));
 }
 
 TEST_F(ActivityDatabaseTest, BatchModeOff) {
@@ -274,27 +213,14 @@
   db_file = temp_dir.path().AppendASCII("ActivityRecord.db");
   base::DeleteFile(db_file, false);
 
-  // Use a mock clock to ensure that events are not recorded on the wrong day
-  // when the test is run close to local midnight.
-  base::SimpleTestClock mock_clock;
-  mock_clock.SetNow(base::Time::Now().LocalMidnight() +
-                    base::TimeDelta::FromHours(12));
-
   // Record some actions
   ActivityDatabase* activity_db = OpenDatabase(db_file);
   activity_db->SetBatchModeForTesting(false);
-  activity_db->SetClockForTesting(&mock_clock);
-  scoped_refptr<Action> action =
-      new Action("punky",
-                 mock_clock.Now() - base::TimeDelta::FromMinutes(40),
-                 Action::ACTION_API_CALL,
-                 "brewster");
-  action->mutable_args()->AppendString("woof");
-  activity_db->RecordAction(action);
 
-  scoped_ptr<std::vector<scoped_refptr<Action> > > actions =
-      activity_db->GetActions("punky", 0);
-  ASSERT_EQ(1, static_cast<int>(actions->size()));
+  scoped_refptr<Action> action = CreateAction(base::Time::Now(), "brewster");
+  Record(activity_db, action);
+  ASSERT_EQ(1, CountActions(&activity_db->db_, "brewster"));
+
   activity_db->Close();
 }
 
@@ -305,35 +231,17 @@
   db_file = temp_dir.path().AppendASCII("ActivityRecord.db");
   base::DeleteFile(db_file, false);
 
-  // Use a mock clock to set the time, and a special timer to control the
-  // timing and skip ahead in time.
-  base::SimpleTestClock mock_clock;
-  mock_clock.SetNow(base::Time::Now().LocalMidnight() +
-                    base::TimeDelta::FromHours(11));
-
   // Record some actions
   ActivityDatabase* activity_db = OpenDatabase(db_file);
   activity_db->SetBatchModeForTesting(true);
-  activity_db->SetClockForTesting(&mock_clock);
-  scoped_refptr<Action> action =
-      new Action("punky",
-                 mock_clock.Now() - base::TimeDelta::FromMinutes(40),
-                 Action::ACTION_API_CALL,
-                 "brewster");
-  action->mutable_args()->AppendString("woof");
-  activity_db->RecordAction(action);
-
-  scoped_ptr<std::vector<scoped_refptr<Action> > > actions_before =
-      activity_db->GetActions("punky", 0);
-  ASSERT_EQ(0, static_cast<int>(actions_before->size()));
+  scoped_refptr<Action> action = CreateAction(base::Time::Now(), "brewster");
+  Record(activity_db, action);
+  ASSERT_EQ(0, CountActions(&activity_db->db_, "brewster"));
 
   // Artificially trigger and then stop the timer.
   activity_db->SetTimerForTesting(0);
   base::MessageLoop::current()->RunUntilIdle();
-
-  scoped_ptr<std::vector<scoped_refptr<Action> > > actions_after =
-        activity_db->GetActions("punky", 0);
-    ASSERT_EQ(1, static_cast<int>(actions_after->size()));
+  ASSERT_EQ(1, CountActions(&activity_db->db_, "brewster"));
 
   activity_db->Close();
 }
@@ -346,12 +254,12 @@
   db_file = temp_dir.path().AppendASCII("ActivityRecord.db");
   base::DeleteFile(db_file, false);
 
-  ActivityDatabase* activity_db =
-      new ActivityDatabase(new ActivityDatabaseTestPolicy());
+  ActivityDatabaseTestPolicy* delegate = new ActivityDatabaseTestPolicy();
+  ActivityDatabase* activity_db = new ActivityDatabase(delegate);
   scoped_refptr<Action> action = new Action(
       "punky", base::Time::Now(), Action::ACTION_API_CALL, "brewster");
   action->mutable_args()->AppendString("woof");
-  activity_db->RecordAction(action);
+  delegate->Record(activity_db, action);
   activity_db->Close();
 }
 
diff --git a/chrome/browser/extensions/activity_log/activity_log.cc b/chrome/browser/extensions/activity_log/activity_log.cc
index b96886b..2b81ed5 100644
--- a/chrome/browser/extensions/activity_log/activity_log.cc
+++ b/chrome/browser/extensions/activity_log/activity_log.cc
@@ -2,8 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/extensions/activity_log/activity_log.h"
+
 #include <set>
 #include <vector>
+
 #include "base/command_line.h"
 #include "base/json/json_string_value_serializer.h"
 #include "base/logging.h"
@@ -11,16 +14,17 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_checker.h"
 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
-#include "chrome/browser/extensions/activity_log/activity_log.h"
 #include "chrome/browser/extensions/activity_log/stream_noargs_ui_policy.h"
 #include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/extensions/extension_system_factory.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/extensions/install_tracker_factory.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
@@ -87,6 +91,93 @@
   bool cmd_line_enabled_;
 };
 
+// Gets the URL for a given tab ID.  Helper method for LookupTabId.  Returns
+// true if able to perform the lookup.  The URL is stored to *url, and
+// *is_incognito is set to indicate whether the URL is for an incognito tab.
+bool GetUrlForTabId(int tab_id,
+                    Profile* profile,
+                    GURL* url,
+                    bool* is_incognito) {
+  content::WebContents* contents = NULL;
+  Browser* browser = NULL;
+  bool found = ExtensionTabUtil::GetTabById(tab_id,
+                                            profile,
+                                            true,  // search incognito tabs too
+                                            &browser,
+                                            NULL,
+                                            &contents,
+                                            NULL);
+  if (found) {
+    *url = contents->GetURL();
+    *is_incognito = browser->profile()->IsOffTheRecord();
+    return true;
+  } else {
+    return false;
+  }
+}
+
+// Translate tab IDs to URLs in tabs API calls.  Mutates the Action object in
+// place.  There is a small chance that the URL translation could be wrong, if
+// the tab has already been navigated by the time of invocation.
+//
+// If a single tab ID is translated to a URL, the URL is stored into arg_url
+// where it can more easily be searched for in the database.  For APIs that
+// take a list of tab IDs, replace each tab ID with the URL in the argument
+// list; we can only extract a single URL into arg_url so arbitrarily pull out
+// the first one.
+void LookupTabIds(scoped_refptr<extensions::Action> action, Profile* profile) {
+  const std::string& api_call = action->api_name();
+  if (api_call == "tabs.get" ||                 // api calls, ID as int
+      api_call == "tabs.connect" ||
+      api_call == "tabs.sendMessage" ||
+      api_call == "tabs.duplicate" ||
+      api_call == "tabs.update" ||
+      api_call == "tabs.reload" ||
+      api_call == "tabs.detectLanguage" ||
+      api_call == "tabs.executeScript" ||
+      api_call == "tabs.insertCSS" ||
+      api_call == "tabs.move" ||                // api calls, IDs in array
+      api_call == "tabs.remove" ||
+      api_call == "tabs.onUpdated" ||           // events, ID as int
+      api_call == "tabs.onMoved" ||
+      api_call == "tabs.onDetached" ||
+      api_call == "tabs.onAttached" ||
+      api_call == "tabs.onRemoved" ||
+      api_call == "tabs.onReplaced") {
+    int tab_id;
+    base::ListValue* id_list;
+    base::ListValue* args = action->mutable_args();
+    if (args->GetInteger(0, &tab_id)) {
+      // Single tab ID to translate.
+      GURL url;
+      bool is_incognito;
+      if (GetUrlForTabId(tab_id, profile, &url, &is_incognito)) {
+        action->set_arg_url(url);
+        action->set_arg_incognito(is_incognito);
+      }
+    } else if ((api_call == "tabs.move" || api_call == "tabs.remove") &&
+               args->GetList(0, &id_list)) {
+      // Array of tab IDs to translate.
+      for (int i = 0; i < static_cast<int>(id_list->GetSize()); ++i) {
+        if (id_list->GetInteger(i, &tab_id)) {
+          GURL url;
+          bool is_incognito;
+          if (GetUrlForTabId(tab_id, profile, &url, &is_incognito) &&
+              !is_incognito) {
+            id_list->Set(i, new base::StringValue(url.spec()));
+            if (i == 0) {
+              action->set_arg_url(url);
+              action->set_arg_incognito(is_incognito);
+            }
+          }
+        } else {
+          LOG(ERROR) << "The tab ID array is malformed at index " << i;
+        }
+      }
+    }
+  }
+}
+
 }  // namespace
 
 namespace extensions {
@@ -256,150 +347,26 @@
 }
 
 void ActivityLog::LogAction(scoped_refptr<Action> action) {
-  if (IsLogEnabled() &&
-      !ActivityLogAPI::IsExtensionWhitelisted(action->extension_id())) {
-    if (policy_)
-      policy_->ProcessAction(action);
-    observers_->Notify(&Observer::OnExtensionActivity, action);
-    if (testing_mode_)
-      LOG(INFO) << action->PrintForDebug();
-  }
-}
-
-void ActivityLog::LogAPIActionInternal(const std::string& extension_id,
-                                       const std::string& api_call,
-                                       base::ListValue* args,
-                                       const std::string& extra,
-                                       const APIAction::Type type) {
-  std::string verb, manager;
-  bool matches = RE2::FullMatch(api_call, "(.*?)\\.(.*)", &manager, &verb);
-  if (matches) {
-    if (!args->empty() && manager == "tabs") {
-      APIAction::LookupTabId(api_call, args, profile_);
-    }
-
-    DCHECK((type == APIAction::CALL || type == APIAction::EVENT_CALLBACK) &&
-           "Unexpected APIAction call type.");
-
-    scoped_refptr<Action> action;
-    action = new Action(extension_id,
-                        base::Time::Now(),
-                        type == APIAction::CALL ? Action::ACTION_API_CALL
-                                                : Action::ACTION_API_EVENT,
-                        api_call);
-    action->set_args(make_scoped_ptr(args->DeepCopy()));
-    if (!extra.empty())
-      action->mutable_other()->SetString(constants::kActionExtra, extra);
-
-    LogAction(action);
-  } else {
-    LOG(ERROR) << "Unknown API call! " << api_call;
-  }
-}
-
-// A wrapper around LogAPIActionInternal, but we know it's an API call.
-void ActivityLog::LogAPIAction(const std::string& extension_id,
-                               const std::string& api_call,
-                               base::ListValue* args,
-                               const std::string& extra) {
   if (!IsLogEnabled() ||
-      ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return;
-  LogAPIActionInternal(extension_id,
-                       api_call,
-                       args,
-                       extra,
-                       APIAction::CALL);
-}
+      ActivityLogAPI::IsExtensionWhitelisted(action->extension_id()))
+    return;
 
-// A wrapper around LogAPIActionInternal, but we know it's actually an event
-// being fired and triggering extension code. Having the two separate methods
-// (LogAPIAction vs LogEventAction) lets us hide how we actually choose to
-// handle them. Right now they're being handled almost the same.
-void ActivityLog::LogEventAction(const std::string& extension_id,
-                                 const std::string& api_call,
-                                 base::ListValue* args,
-                                 const std::string& extra) {
-  if (!IsLogEnabled() ||
-      ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return;
-  LogAPIActionInternal(extension_id,
-                       api_call,
-                       args,
-                       extra,
-                       APIAction::EVENT_CALLBACK);
-}
-
-void ActivityLog::LogBlockedAction(const std::string& extension_id,
-                                   const std::string& blocked_call,
-                                   base::ListValue* args,
-                                   BlockedAction::Reason reason,
-                                   const std::string& extra) {
-  if (!IsLogEnabled() ||
-      ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return;
-
-  scoped_refptr<Action> action;
-  action = new Action(extension_id,
-                      base::Time::Now(),
-                      Action::ACTION_API_BLOCKED,
-                      blocked_call);
-  action->set_args(make_scoped_ptr(args->DeepCopy()));
-  action->mutable_other()
-      ->SetInteger(constants::kActionBlockedReason, static_cast<int>(reason));
-  if (!extra.empty())
-    action->mutable_other()->SetString(constants::kActionExtra, extra);
-
-  LogAction(action);
-}
-
-void ActivityLog::LogDOMAction(const std::string& extension_id,
-                               const GURL& url,
-                               const string16& url_title,
-                               const std::string& api_call,
-                               const base::ListValue* args,
-                               DomActionType::Type call_type,
-                               const std::string& extra) {
-  if (!IsLogEnabled() ||
-      ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return;
-
-  Action::ActionType action_type = Action::ACTION_DOM_ACCESS;
-  if (call_type == DomActionType::INSERTED) {
-    action_type = Action::ACTION_CONTENT_SCRIPT;
-  } else if (call_type == DomActionType::METHOD &&
-             api_call == "XMLHttpRequest.open") {
-    call_type = DomActionType::XHR;
-    action_type = Action::ACTION_DOM_XHR;
+  // Perform some preprocessing of the Action data: convert tab IDs to URLs and
+  // mask out incognito URLs if appropriate.
+  if ((action->action_type() == Action::ACTION_API_CALL ||
+       action->action_type() == Action::ACTION_API_EVENT) &&
+      StartsWithASCII(action->api_name(), "tabs.", true)) {
+    LookupTabIds(action, profile_);
   }
 
-  scoped_refptr<Action> action;
-  action = new Action(extension_id, base::Time::Now(), action_type, api_call);
-  if (args)
-    action->set_args(make_scoped_ptr(args->DeepCopy()));
-  action->set_page_url(url);
-  action->set_page_title(base::UTF16ToUTF8(url_title));
-  action->mutable_other()
-      ->SetInteger(constants::kActionDomVerb, static_cast<int>(call_type));
-  if (!extra.empty())
-    action->mutable_other()->SetString(constants::kActionExtra, extra);
+  // TODO(mvrable): Add any necessary processing of incognito URLs here, for
+  // crbug.com/253368
 
-  LogAction(action);
-}
-
-void ActivityLog::LogWebRequestAction(const std::string& extension_id,
-                                      const GURL& url,
-                                      const std::string& api_call,
-                                      scoped_ptr<DictionaryValue> details,
-                                      const std::string& extra) {
-  if (!IsLogEnabled() ||
-      ActivityLogAPI::IsExtensionWhitelisted(extension_id)) return;
-
-  scoped_refptr<Action> action;
-  action = new Action(
-      extension_id, base::Time::Now(), Action::ACTION_WEB_REQUEST, api_call);
-  action->set_page_url(url);
-  action->mutable_other()->Set(constants::kActionWebRequest, details.release());
-  if (!extra.empty())
-    action->mutable_other()->SetString(constants::kActionExtra, extra);
-
-  LogAction(action);
+  if (policy_)
+    policy_->ProcessAction(action);
+  observers_->Notify(&Observer::OnExtensionActivity, action);
+  if (testing_mode_)
+    LOG(INFO) << action->PrintForDebug();
 }
 
 void ActivityLog::GetActions(
@@ -426,11 +393,6 @@
   const prerender::PrerenderManager* prerender_manager =
       prerender::PrerenderManagerFactory::GetForProfile(
           Profile::FromBrowserContext(web_contents->GetBrowserContext()));
-  std::string extra;
-
-  if (prerender_manager &&
-      prerender_manager->IsWebContentsPrerendering(web_contents, NULL))
-    extra = "(prerender)";
 
   for (ExecutingScriptsMap::const_iterator it = extension_ids.begin();
        it != extension_ids.end(); ++it) {
@@ -442,22 +404,24 @@
     // of content scripts will be empty.  We don't want to log it because
     // the call to tabs.executeScript will have already been logged anyway.
     if (!it->second.empty()) {
-      std::string ext_scripts_str;
+      scoped_refptr<Action> action;
+      action = new Action(extension->id(),
+                          base::Time::Now(),
+                          Action::ACTION_CONTENT_SCRIPT,
+                          "");  // no API call here
+      action->set_page_url(on_url);
+      action->set_page_title(base::UTF16ToUTF8(web_contents->GetTitle()));
+      action->set_page_incognito(
+          web_contents->GetBrowserContext()->IsOffTheRecord());
+      if (prerender_manager &&
+          prerender_manager->IsWebContentsPrerendering(web_contents, NULL))
+        action->mutable_other()->SetBoolean(constants::kActionPrerender, true);
       for (std::set<std::string>::const_iterator it2 = it->second.begin();
            it2 != it->second.end();
            ++it2) {
-        ext_scripts_str += *it2;
-        ext_scripts_str += " ";
+        action->mutable_args()->AppendString(*it2);
       }
-      scoped_ptr<base::ListValue> script_names(new base::ListValue());
-      script_names->Set(0, new base::StringValue(ext_scripts_str));
-      LogDOMAction(extension->id(),
-                   on_url,
-                   web_contents->GetTitle(),
-                   std::string(),  // no api call here
-                   script_names.get(),
-                   DomActionType::INSERTED,
-                   extra);
+      LogAction(action);
     }
   }
 }
diff --git a/chrome/browser/extensions/activity_log/activity_log.h b/chrome/browser/extensions/activity_log/activity_log.h
index 446d9af..4f75d29 100644
--- a/chrome/browser/extensions/activity_log/activity_log.h
+++ b/chrome/browser/extensions/activity_log/activity_log.h
@@ -68,60 +68,8 @@
   // Logs an extension action: passes it to any installed policy to be logged
   // to the database, to any observers, and logs to the console if in testing
   // mode.
-  //
-  // The convenience Log*Action methods below can be used as well if the caller
-  // does not wish to construct the Action object itself, however those methods
-  // are somewhat deprecated.
   void LogAction(scoped_refptr<Action> action);
 
-  // TODO(mvrable): The calls below take args as a raw pointer, but the callee
-  // does not own the object so these should more properly be a const pointer.
-  // The callee is forced to make a copy of the object which in some cases is
-  // wasteful, and it could be better to take a scoped_ptr as input.  Switching
-  // to using LogAction is another way to clean this up.
-
-  // Log a successful API call made by an extension.
-  // This will create an APIAction for storage in the database.
-  // (Note: implemented as a wrapper for LogAPIActionInternal.)
-  void LogAPIAction(const std::string& extension_id,
-                    const std::string& name,    // e.g., tabs.get
-                    base::ListValue* args,      // the argument values e.g. 46
-                    const std::string& extra);  // any extra logging info
-
-  // Log an event notification delivered to an extension.
-  // This will create an APIAction for storage in the database.
-  // (Note: implemented as a wrapper for LogAPIActionInternal.)
-  void LogEventAction(const std::string& extension_id,
-                      const std::string& name,    // e.g., tabs.onUpdate
-                      base::ListValue* args,      // arguments to the callback
-                      const std::string& extra);  // any extra logging info
-
-  // Log a blocked API call made by an extension.
-  // This will create a BlockedAction for storage in the database.
-  void LogBlockedAction(const std::string& extension_id,
-                        const std::string& blocked_call,  // e.g., tabs.get
-                        base::ListValue* args,            // argument values
-                        BlockedAction::Reason reason,     // why it's blocked
-                        const std::string& extra);        // extra logging info
-
-  // Log an interaction between an extension and a URL.
-  // This will create a DOMAction for storage in the database.
-  void LogDOMAction(const std::string& extension_id,
-                    const GURL& url,                      // target URL
-                    const string16& url_title,            // title of the URL
-                    const std::string& api_call,          // api call
-                    const base::ListValue* args,          // arguments
-                    DomActionType::Type call_type,        // type of the call
-                    const std::string& extra);            // extra logging info
-
-  // Log a use of the WebRequest API to redirect, cancel, or modify page
-  // headers.
-  void LogWebRequestAction(const std::string& extension_id,
-                           const GURL& url,
-                           const std::string& api_call,
-                           scoped_ptr<base::DictionaryValue> details,
-                           const std::string& extra);
-
   // Retrieves the list of actions for a given extension on a specific day.
   // Today is 0, yesterday is 1, etc. Returns one day at a time.
   // Response is sent to the method/function in the callback.
@@ -172,15 +120,6 @@
   // are done with their own setup.
   void Init();
 
-  // We log callbacks and API calls very similarly, so we handle them the same
-  // way internally.
-  void LogAPIActionInternal(
-      const std::string& extension_id,
-      const std::string& api_call,
-      base::ListValue* args,
-      const std::string& extra,
-      const APIAction::Type type);
-
   // TabHelper::ScriptExecutionObserver implementation.
   // Fires when a ContentScript is executed.
   virtual void OnScriptsExecuted(
diff --git a/chrome/browser/extensions/activity_log/activity_log_browsertest.cc b/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
index ef877b3..65e3f51 100644
--- a/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
@@ -46,10 +46,10 @@
     scoped_refptr<Action> last = i->front();
 
     std::string args = base::StringPrintf(
-        "ID=%s CATEGORY=content_script API= ARGS=[\"/google_cs.js \"] "
+        "ID=%s CATEGORY=content_script API= ARGS=[\"/google_cs.js\"] "
         "PAGE_URL=http://www.google.com.bo:%d/test.html "
         "PAGE_TITLE=\"www.google.com.bo:%d/test.html\" "
-        "OTHER={\"dom_verb\":3,\"extra\":\"(prerender)\"}",
+        "OTHER={\"prerender\":true}",
         extension_id.c_str(), port, port);
     // TODO: Replace PrintForDebug with field testing
     // when this feature will be available
diff --git a/chrome/browser/extensions/activity_log/activity_log_policy.cc b/chrome/browser/extensions/activity_log/activity_log_policy.cc
index 157a9ea..3e8b889 100644
--- a/chrome/browser/extensions/activity_log/activity_log_policy.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_policy.cc
@@ -2,27 +2,51 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/extensions/activity_log/activity_log_policy.h"
+
 #include <stdint.h>
 
+#include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/time/clock.h"
 #include "base/time/time.h"
-#include "chrome/browser/extensions/activity_log/activity_log_policy.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/extensions/extension.h"
+#include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 
+using content::BrowserThread;
+
 namespace extensions {
 
-ActivityLogPolicy::ActivityLogPolicy(Profile* profile) {
-  CHECK(profile && "Null ptr dereference");
-  profile_base_path_ = profile->GetPath();
-}
+ActivityLogPolicy::ActivityLogPolicy(Profile* profile) : testing_clock_(NULL) {}
 
-ActivityLogPolicy::~ActivityLogPolicy() {
+ActivityLogPolicy::~ActivityLogPolicy() {}
+
+base::Time ActivityLogPolicy::Now() const {
+  if (testing_clock_)
+    return testing_clock_->Now();
+  else
+    return base::Time::Now();
 }
 
 std::string ActivityLogPolicy::GetKey(KeyType) const {
   return std::string();
 }
 
+ActivityLogDatabasePolicy::ActivityLogDatabasePolicy(
+    Profile* profile,
+    const base::FilePath& database_name)
+    : ActivityLogPolicy(profile) {
+  CHECK(profile);
+  base::FilePath profile_base_path = profile->GetPath();
+  db_ = new ActivityDatabase(this);
+  base::FilePath database_path = profile_base_path.Append(database_name);
+  ScheduleAndForget(db_, &ActivityDatabase::Init, database_path);
+}
+
+sql::Connection* ActivityLogDatabasePolicy::GetDatabaseConnection() const {
+  return db_->GetSqlConnection();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/activity_log_policy.h b/chrome/browser/extensions/activity_log/activity_log_policy.h
index 41dfa0a..e8421dc 100644
--- a/chrome/browser/extensions/activity_log/activity_log_policy.h
+++ b/chrome/browser/extensions/activity_log/activity_log_policy.h
@@ -6,37 +6,47 @@
 #define CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_LOG_POLICY_H_
 
 #include <string>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
-#include "base/files/file_path.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/activity_log/activity_actions.h"
+#include "chrome/browser/extensions/activity_log/activity_database.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 
 class Profile;
 class GURL;
 
+namespace base {
+class FilePath;
+}
+
 namespace extensions {
 
 class Extension;
 
-// Abstract class for summarizing data and storing it into the database.  Any
-// subclass should implement the following functionality:
+// An abstract class for processing and summarizing activity log data.
+// Subclasses will generally store data in an SQLite database (the
+// ActivityLogDatabasePolicy subclass includes some helper methods to assist
+// with this case), but this is not absolutely required.
 //
-// (1) Summarization (and possibly) compression of data
-// (2) Periodical saving of the in-memory state with the database
-// (3) Periodical database cleanup
+// Implementations should support:
+// (1) Receiving Actions to process, and summarizing, compression, and storing
+//     these as appropriate.
+// (2) Reading Actions back from storage.
+//
+// Implementations based on a database should likely implement
+// ActivityDatabase::Delegate, which provides hooks on database events and
+// allows the database to periodically request that actions (which the policy
+// is responsible for queueing) be flushed to storage.
 //
 // Since every policy implementation might summarize data differently, the
 // database implementation is policy-specific and therefore completely
 // encapsulated in the policy class.  All the member functions can be called
-// on the UI thread, because all DB operations are dispatched via the
-// ActivityDatabase.
+// on the UI thread.
 class ActivityLogPolicy {
  public:
   enum PolicyType {
@@ -45,14 +55,6 @@
     POLICY_INVALID,
   };
 
-  enum ActionType {
-    ACTION_API,
-    ACTION_EVENT,
-    ACTION_BLOCKED,
-    ACTION_DOM,
-    ACTION_WEB_REQUEST,
-  };
-
   // For all subclasses, add all the key types they might support here.
   // The actual key is returned by calling GetKey(KeyType).  The subclasses
   // are free to return an empty string for keys they don't support.
@@ -82,34 +84,56 @@
   // state to memory every 5 min.
   virtual void ProcessAction(scoped_refptr<Action> action) = 0;
 
-  // Pass the parameters as a set of key-value pairs and return data back via
-  // a callback passing results as a set of key-value pairs.  The keys are
-  // policy-specific.
-  virtual void ReadData(
-      const base::DictionaryValue& parameters,
-      const base::Callback
-          <void(scoped_ptr<base::DictionaryValue>)>& callback) const {}
-
-  // TODO(felt,dbabic) This is overly specific to the current implementation
-  // of the FullStreamUIPolicy.  We should refactor it to use the above
-  // more general member function.
+  // Gets all actions for a given extension for the specified day. 0 = today,
+  // 1 = yesterday, etc. Only returns 1 day at a time. Actions are sorted from
+  // newest to oldest. Results as passed to the specified callback when
+  // available.
+  //
+  // TODO(felt,dbabic) This is overly specific to the current implementation of
+  // the FullStreamUIPolicy.  We should refactor it to use a more generic read
+  // function, for example one that takes a dictionary of query parameters
+  // (extension_id, time range, etc.).
   virtual void ReadData(
       const std::string& extension_id,
       const int day,
       const base::Callback
-          <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback)
-      const {}
+          <void(scoped_ptr<Action::ActionVector>)>& callback) = 0;
 
   virtual std::string GetKey(KeyType key_id) const;
 
+  // For unit testing only.
+  void SetClockForTesting(base::Clock* clock) { testing_clock_ = clock; }
+
  protected:
   // An ActivityLogPolicy is not directly destroyed.  Instead, call Close()
   // which will cause the object to be deleted when it is safe.
   virtual ~ActivityLogPolicy();
 
+  // Returns Time::Now() unless a mock clock has been installed with
+  // SetClockForTesting, in which case the time according to that clock is used
+  // instead.
+  base::Time Now() const;
+
+ private:
+  // Support for a mock clock for testing purposes.  This is used by ReadData
+  // to determine the date for "today" when when interpreting date ranges to
+  // fetch.  This has no effect on batching of writes to the database.
+  base::Clock* testing_clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(ActivityLogPolicy);
+};
+
+// A subclass of ActivityLogPolicy which is designed for policies that use
+// database storage; it contains several useful helper methods.
+class ActivityLogDatabasePolicy : public ActivityLogPolicy,
+                                  public ActivityDatabase::Delegate {
+ public:
+  ActivityLogDatabasePolicy(Profile* profile,
+                            const base::FilePath& database_name);
+
+ protected:
   // The Schedule methods dispatch the calls to the database on a
-  // separate thread. We dispatch to the UI thread if the DB thread doesn't
-  // exist, which should only happen in tests where there is no DB thread.
+  // separate thread.
   template<typename DatabaseType, typename DatabaseFunc>
   void ScheduleAndForget(DatabaseType db, DatabaseFunc func) {
     content::BrowserThread::PostTask(
@@ -135,10 +159,18 @@
         base::Bind(func, base::Unretained(db), a, b));
   }
 
-  base::FilePath profile_base_path_;
+  // Access to the underlying ActivityDatabase.
+  ActivityDatabase* activity_database() const { return db_; }
+
+  // Access to the SQL connection in the ActivityDatabase.  This should only be
+  // called from the database thread.  May return NULL if the database is not
+  // valid.
+  sql::Connection* GetDatabaseConnection() const;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(ActivityLogPolicy);
+  // See the comments for the ActivityDatabase class for a discussion of how
+  // database cleanup runs.
+  ActivityDatabase* db_;
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index 10dbd19..118cb4d 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -76,44 +76,6 @@
     ASSERT_EQ(2, static_cast<int>(i->size()));
   }
 
-  static void RetrieveActions_LogAndFetchPathActions(
-      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
-    std::string args;
-    ASSERT_EQ(1U, i->size());
-    scoped_refptr<Action> last = i->front();
-    if (CommandLine::ForCurrentProcess()->HasSwitch(
-        switches::kEnableExtensionActivityLogTesting))
-      args =
-          "ID=abc CATEGORY=content_script API=document.write ARGS=[] "
-          "PAGE_URL=http://www.google.com/foo?bar "
-          "OTHER={\"dom_verb\":3,\"extra\":\"extra\"}";
-    else
-      args =
-          "ID=abc CATEGORY=content_script API=document.write ARGS=[] "
-          "PAGE_URL=http://www.google.com/foo "
-          "OTHER={\"dom_verb\":3,\"extra\":\"extra\"}";
-    ASSERT_EQ(args, last->PrintForDebug());
-  }
-
-  static void Arguments_Missing(
-      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
-    scoped_refptr<Action> last = i->front();
-    std::string id(kExtensionId);
-    std::string noargs =
-        "ID=" + id + " CATEGORY=api_call API=tabs.testMethod";
-    ASSERT_EQ(noargs, last->PrintForDebug());
-  }
-
-  static void Arguments_Present(
-      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
-    scoped_refptr<Action> last = i->front();
-    std::string id(kExtensionId);
-    std::string args = "ID=" + id +
-                       " CATEGORY=api_call API=extension.connect "
-                       "ARGS=[\"hello\",\"world\"]";
-    ASSERT_EQ(args, last->PrintForDebug());
-  }
-
   void SetPolicy(bool log_arguments) {
     ActivityLog* activity_log = ActivityLog::GetInstance(profile());
     if (log_arguments)
@@ -128,8 +90,8 @@
     scoped_refptr<Action> last = i->front();
     std::string args =
         "ID=odlameecjipmbmbejkplpemijjgpljce CATEGORY=content_script API= "
-        "ARGS=[\"script \"] PAGE_URL=http://www.google.com/ "
-        "OTHER={\"dom_verb\":3,\"extra\":\"(prerender)\"}";
+        "ARGS=[\"script\"] PAGE_URL=http://www.google.com/ "
+        "OTHER={\"prerender\":true}";
     ASSERT_EQ(args, last->PrintForDebug());
   }
 
@@ -153,10 +115,13 @@
 
 TEST_F(ActivityLogTest, Construct) {
   ActivityLog* activity_log = ActivityLog::GetInstance(profile());
-  scoped_ptr<base::ListValue> args(new base::ListValue());
   ASSERT_TRUE(activity_log->IsLogEnabled());
-  activity_log->LogAPIAction(
-      kExtensionId, std::string("tabs.testMethod"), args.get(), std::string());
+
+  scoped_refptr<Action> action = new Action(kExtensionId,
+                                            base::Time::Now(),
+                                            Action::ACTION_API_CALL,
+                                            "tabs.testMethod");
+  activity_log->LogAction(action);
 }
 
 TEST_F(ActivityLogTest, LogAndFetchActions) {
@@ -165,68 +130,24 @@
   ASSERT_TRUE(activity_log->IsLogEnabled());
 
   // Write some API calls
-  activity_log->LogAPIAction(
-      kExtensionId, std::string("tabs.testMethod"), args.get(), std::string());
-  activity_log->LogDOMAction(kExtensionId,
-                             GURL("http://www.google.com"),
-                             string16(),
-                             std::string("document.write"),
-                             args.get(),
-                             DomActionType::METHOD,
-                             std::string("extra"));
+  scoped_refptr<Action> action = new Action(kExtensionId,
+                                            base::Time::Now(),
+                                            Action::ACTION_API_CALL,
+                                            "tabs.testMethod");
+  activity_log->LogAction(action);
+  action = new Action(kExtensionId,
+                      base::Time::Now(),
+                      Action::ACTION_DOM_ACCESS,
+                      "document.write");
+  action->set_page_url(GURL("http://www.google.com"));
+  activity_log->LogAction(action);
+
   activity_log->GetActions(
       kExtensionId,
       0,
       base::Bind(ActivityLogTest::RetrieveActions_LogAndFetchActions));
 }
 
-TEST_F(ActivityLogTest, LogAndFetchPathActions) {
-  ActivityLog* activity_log = ActivityLog::GetInstance(profile());
-  scoped_ptr<base::ListValue> args(new base::ListValue());
-  ASSERT_TRUE(activity_log->IsLogEnabled());
-
-  activity_log->LogDOMAction(kExtensionId,
-                             GURL("http://www.google.com/foo?bar"),
-                             string16(),
-                             std::string("document.write"),
-                             args.get(),
-                             DomActionType::INSERTED,
-                             std::string("extra"));
-  activity_log->GetActions(
-      kExtensionId,
-      0,
-      base::Bind(ActivityLogTest::RetrieveActions_LogAndFetchPathActions));
-}
-
-TEST_F(ActivityLogTest, LogWithoutArguments) {
-  ActivityLog* activity_log = ActivityLog::GetInstance(profile());
-  ASSERT_TRUE(activity_log->IsLogEnabled());
-  SetPolicy(false);
-  scoped_ptr<base::ListValue> args(new base::ListValue());
-  args->Set(0, new base::StringValue("hello"));
-  args->Set(1, new base::StringValue("world"));
-  activity_log->LogAPIAction(
-      kExtensionId, std::string("tabs.testMethod"), args.get(), std::string());
-  activity_log->GetActions(
-      kExtensionId, 0, base::Bind(ActivityLogTest::Arguments_Missing));
-  SetPolicy(true);
-}
-
-TEST_F(ActivityLogTest, LogWithArguments) {
-  ActivityLog* activity_log = ActivityLog::GetInstance(profile());
-  ASSERT_TRUE(activity_log->IsLogEnabled());
-
-  scoped_ptr<base::ListValue> args(new base::ListValue());
-  args->Set(0, new base::StringValue("hello"));
-  args->Set(1, new base::StringValue("world"));
-  activity_log->LogAPIAction(kExtensionId,
-                             std::string("extension.connect"),
-                             args.get(),
-                             std::string());
-  activity_log->GetActions(
-      kExtensionId, 0, base::Bind(ActivityLogTest::Arguments_Present));
-}
-
 TEST_F(ActivityLogTest, LogPrerender) {
   scoped_refptr<const Extension> extension =
       ExtensionBuilder()
@@ -270,4 +191,3 @@
 }
 
 }  // namespace extensions
-
diff --git a/chrome/browser/extensions/activity_log/api_name_constants.h b/chrome/browser/extensions/activity_log/api_name_constants.h
index d50f72e..9cc4984 100644
--- a/chrome/browser/extensions/activity_log/api_name_constants.h
+++ b/chrome/browser/extensions/activity_log/api_name_constants.h
@@ -67,7 +67,7 @@
     "experimental.discovery.removeSuggestion",
     "experimental.discovery.suggest", "experimental.history.getMostVisited",
     "experimental.identity.getAuthToken",
-    "experimental.identity.launchWebAuthFlow", "experimental.infobars.show",
+    "experimental.identity.launchWebAuthFlow", "infobars.show",
     "experimental.mediaGalleries.assembleMediaFile",
     "experimental.mediaGalleries.extractEmbeddedThumbnails",
     "experimental.processes.getProcessIdForTab",
diff --git a/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc b/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc
index a8cc82d..24f5494 100644
--- a/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc
+++ b/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc
@@ -2,18 +2,25 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
+
+#include "base/callback.h"
+#include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/json/json_reader.h"
 #include "base/json/json_string_value_serializer.h"
 #include "base/logging.h"
 #include "base/strings/string16.h"
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/extensions/activity_log/activity_action_constants.h"
 #include "chrome/browser/extensions/activity_log/activity_database.h"
-#include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/dom_action_types.h"
 #include "chrome/common/extensions/extension.h"
 #include "sql/error_delegate_util.h"
+#include "sql/transaction.h"
 #include "url/gurl.h"
 
 using base::Callback;
@@ -22,6 +29,8 @@
 using base::Unretained;
 using content::BrowserThread;
 
+namespace constants = activity_log_constants;
+
 namespace {
 
 // Key strings for passing parameters to the ProcessAction member function.
@@ -35,6 +44,17 @@
 const char* kObsoleteTables[] = {"activitylog_apis", "activitylog_blocked",
                                  "activitylog_urls"};
 
+std::string Serialize(const base::Value* value) {
+  std::string value_as_text;
+  if (!value) {
+    value_as_text = "null";
+  } else {
+    JSONStringValueSerializer serializer(&value_as_text);
+    serializer.SerializeAndOmitBinaryValues(*value);
+  }
+  return value_as_text;
+}
+
 }  // namespace
 
 namespace extensions {
@@ -51,14 +71,13 @@
 const int FullStreamUIPolicy::kTableFieldCount = arraysize(kTableContentFields);
 
 FullStreamUIPolicy::FullStreamUIPolicy(Profile* profile)
-    : ActivityLogPolicy(profile) {
-  db_ = new ActivityDatabase(this);
-  FilePath database_name = profile_base_path_.Append(
-      chrome::kExtensionActivityLogFilename);
-  ScheduleAndForget(db_, &ActivityDatabase::Init, database_name);
-}
+    : ActivityLogDatabasePolicy(
+          profile,
+          FilePath(chrome::kExtensionActivityLogFilename)) {}
 
-bool FullStreamUIPolicy::OnDatabaseInit(sql::Connection* db) {
+FullStreamUIPolicy::~FullStreamUIPolicy() {}
+
+bool FullStreamUIPolicy::InitDatabase(sql::Connection* db) {
   // Drop old database tables.
   for (size_t i = 0; i < arraysize(kObsoleteTables); i++) {
     const char* table_name = kObsoleteTables[i];
@@ -79,6 +98,160 @@
                                            arraysize(kTableContentFields));
 }
 
+bool FullStreamUIPolicy::FlushDatabase(sql::Connection* db) {
+  if (queued_actions_.empty())
+    return true;
+
+  sql::Transaction transaction(db);
+  if (!transaction.Begin())
+    return false;
+
+  std::string sql_str =
+      "INSERT INTO " + std::string(FullStreamUIPolicy::kTableName) +
+      " (extension_id, time, action_type, api_name, args, "
+      "page_url, page_title, arg_url, other) VALUES (?,?,?,?,?,?,?,?,?)";
+  sql::Statement statement(db->GetCachedStatement(
+      sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
+
+  url_canon::Replacements<char> url_sanitizer;
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableExtensionActivityLogTesting)) {
+    url_sanitizer.ClearQuery();
+    url_sanitizer.ClearRef();
+  }
+
+  Action::ActionVector::size_type i;
+  for (i = 0; i != queued_actions_.size(); ++i) {
+    const Action& action = *queued_actions_[i];
+    statement.Reset(true);
+    statement.BindString(0, action.extension_id());
+    statement.BindInt64(1, action.time().ToInternalValue());
+    statement.BindInt(2, static_cast<int>(action.action_type()));
+    statement.BindString(3, action.api_name());
+    if (action.args()) {
+      statement.BindString(4, Serialize(action.args()));
+    }
+    if (action.page_url().is_valid()) {
+      if (action.page_incognito()) {
+        statement.BindString(5, constants::kIncognitoUrl);
+      } else {
+        statement.BindString(
+            5, action.page_url().ReplaceComponents(url_sanitizer).spec());
+      }
+    }
+    if (!action.page_title().empty() && !action.page_incognito()) {
+      statement.BindString(6, action.page_title());
+    }
+    if (action.arg_url().is_valid()) {
+      if (action.arg_incognito()) {
+        statement.BindString(7, constants::kIncognitoUrl);
+      } else {
+        statement.BindString(
+            7, action.arg_url().ReplaceComponents(url_sanitizer).spec());
+      }
+    }
+    if (action.other()) {
+      statement.BindString(8, Serialize(action.other()));
+    }
+
+    if (!statement.Run()) {
+      LOG(ERROR) << "Activity log database I/O failed: " << sql_str;
+      return false;
+    }
+  }
+
+  if (!transaction.Commit())
+    return false;
+
+  queued_actions_.clear();
+  return true;
+}
+
+scoped_ptr<Action::ActionVector> FullStreamUIPolicy::DoReadData(
+    const std::string& extension_id,
+    const int days_ago) {
+  DCHECK_GE(days_ago, 0);
+  scoped_ptr<Action::ActionVector> actions(new Action::ActionVector());
+
+  sql::Connection* db = GetDatabaseConnection();
+  if (!db) {
+    return actions.Pass();
+  }
+
+  // Compute the time bounds for that day.
+  base::Time morning_midnight = Now().LocalMidnight();
+  int64 early_bound = 0;
+  int64 late_bound = 0;
+  if (days_ago == 0) {
+      early_bound = morning_midnight.ToInternalValue();
+      late_bound = base::Time::Max().ToInternalValue();
+  } else {
+      base::Time early_time = morning_midnight -
+          base::TimeDelta::FromDays(days_ago);
+      base::Time late_time = morning_midnight -
+          base::TimeDelta::FromDays(days_ago-1);
+      early_bound = early_time.ToInternalValue();
+      late_bound = late_time.ToInternalValue();
+  }
+  std::string query_str = base::StringPrintf(
+      "SELECT time, action_type, api_name, args, page_url, page_title, "
+      "arg_url, other "
+      "FROM %s WHERE extension_id=? AND time>? AND time<=? "
+      "ORDER BY time DESC",
+      kTableName);
+  sql::Statement query(db->GetCachedStatement(SQL_FROM_HERE,
+                                              query_str.c_str()));
+  query.BindString(0, extension_id);
+  query.BindInt64(1, early_bound);
+  query.BindInt64(2, late_bound);
+  while (query.is_valid() && query.Step()) {
+    scoped_refptr<Action> action =
+        new Action(extension_id,
+                   base::Time::FromInternalValue(query.ColumnInt64(0)),
+                   static_cast<Action::ActionType>(query.ColumnInt(1)),
+                   query.ColumnString(2));
+
+    if (query.ColumnType(3) != sql::COLUMN_TYPE_NULL) {
+      scoped_ptr<Value> parsed_value(
+          base::JSONReader::Read(query.ColumnString(3)));
+      if (parsed_value && parsed_value->IsType(Value::TYPE_LIST)) {
+        action->set_args(
+            make_scoped_ptr(static_cast<ListValue*>(parsed_value.release())));
+      } else {
+        LOG(WARNING) << "Unable to parse args: '" << query.ColumnString(3)
+                     << "'";
+      }
+    }
+
+    GURL page_url(query.ColumnString(4));
+    action->set_page_url(page_url);
+
+    action->set_page_title(query.ColumnString(5));
+
+    GURL arg_url(query.ColumnString(6));
+    action->set_arg_url(arg_url);
+
+    if (query.ColumnType(7) != sql::COLUMN_TYPE_NULL) {
+      scoped_ptr<Value> parsed_value(
+          base::JSONReader::Read(query.ColumnString(7)));
+      if (parsed_value && parsed_value->IsType(Value::TYPE_DICTIONARY)) {
+        action->set_other(make_scoped_ptr(
+            static_cast<DictionaryValue*>(parsed_value.release())));
+      } else {
+        LOG(WARNING) << "Unable to parse other: '" << query.ColumnString(7)
+                     << "'";
+      }
+    }
+
+    actions->push_back(action);
+  }
+  return actions.Pass();
+}
+
+void FullStreamUIPolicy::OnDatabaseFailure() {
+  queued_actions_.clear();
+}
+
 void FullStreamUIPolicy::OnDatabaseClose() {
   delete this;
 }
@@ -86,21 +259,21 @@
 void FullStreamUIPolicy::Close() {
   // The policy object should have never been created if there's no DB thread.
   DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::DB));
-  ScheduleAndForget(db_, &ActivityDatabase::Close);
+  ScheduleAndForget(activity_database(), &ActivityDatabase::Close);
 }
 
 // Get data as a set of key-value pairs.  The keys are policy-specific.
 void FullStreamUIPolicy::ReadData(
     const std::string& extension_id,
     const int day,
-    const Callback
-        <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback)
-    const {
+    const Callback<void(scoped_ptr<Action::ActionVector>)>& callback) {
   BrowserThread::PostTaskAndReplyWithResult(
       BrowserThread::DB,
       FROM_HERE,
-      base::Bind(&ActivityDatabase::GetActions, Unretained(db_),
-                 extension_id, day),
+      base::Bind(&FullStreamUIPolicy::DoReadData,
+                 base::Unretained(this),
+                 extension_id,
+                 day),
       callback);
 }
 
@@ -120,38 +293,9 @@
   }
 }
 
-void FullStreamUIPolicy::ProcessArguments(scoped_refptr<Action> action) const {
-  return;
-}
-
-std::string FullStreamUIPolicy::JoinArguments(
-    ActionType action_type,
-    const std::string& name,
-    const base::ListValue* args) const {
-  std::string processed_args;
-  if (args) {
-    base::ListValue::const_iterator it = args->begin();
-    // TODO(felt,dbabic) Think about replacing the loop with a single
-    // call to SerializeAndOmitBinaryValues.
-    for (; it != args->end(); ++it) {
-      std::string arg;
-      JSONStringValueSerializer serializer(&arg);
-      if (serializer.SerializeAndOmitBinaryValues(**it)) {
-        if (it != args->begin()) {
-          processed_args.append(", ");
-        }
-        processed_args.append(arg);
-      }
-    }
-  }
-  return processed_args;
-}
-
-void FullStreamUIPolicy::ProcessWebRequestModifications(
-    DictionaryValue& details,
-    std::string& details_string) const {
-  JSONStringValueSerializer serializer(&details_string);
-  serializer.Serialize(details);
+scoped_refptr<Action> FullStreamUIPolicy::ProcessArguments(
+    scoped_refptr<Action> action) const {
+  return action;
 }
 
 void FullStreamUIPolicy::ProcessAction(scoped_refptr<Action> action) {
@@ -159,8 +303,15 @@
   // in place, which isn't good if there are other users of the object.  When
   // database writing is moved to policy class, the modifications should be
   // made locally.
-  ProcessArguments(action);
-  ScheduleAndForget(db_, &ActivityDatabase::RecordAction, action);
+  action = ProcessArguments(action);
+  ScheduleAndForget(this, &FullStreamUIPolicy::QueueAction, action);
+}
+
+void FullStreamUIPolicy::QueueAction(scoped_refptr<Action> action) {
+  if (activity_database()->is_db_valid()) {
+    queued_actions_.push_back(action);
+    activity_database()->NotifyAction();
+  }
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/fullstream_ui_policy.h b/chrome/browser/extensions/activity_log/fullstream_ui_policy.h
index 8ff9c61..49a8b70 100644
--- a/chrome/browser/extensions/activity_log/fullstream_ui_policy.h
+++ b/chrome/browser/extensions/activity_log/fullstream_ui_policy.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_FULLSTREAM_UI_POLICY_H_
 
 #include <string>
-#include <vector>
+
 #include "chrome/browser/extensions/activity_log/activity_database.h"
 #include "chrome/browser/extensions/activity_log/activity_log_policy.h"
 
@@ -16,8 +16,7 @@
 
 // A policy for logging the full stream of actions, including all arguments.
 // It's mostly intended to be used in testing and analysis.
-class FullStreamUIPolicy : public ActivityLogPolicy,
-                           public ActivityDatabase::Delegate {
+class FullStreamUIPolicy : public ActivityLogDatabasePolicy {
  public:
   // For more info about these member functions, see the super class.
   explicit FullStreamUIPolicy(Profile* profile);
@@ -33,8 +32,7 @@
       const std::string& extension_id,
       const int day,
       const base::Callback
-          <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback)
-      const OVERRIDE;
+          <void(scoped_ptr<Action::ActionVector>)>& callback) OVERRIDE;
 
   // Returns the actual key for a given key type
   virtual std::string GetKey(ActivityLogPolicy::KeyType key_id) const OVERRIDE;
@@ -50,28 +48,33 @@
  protected:
   // Only ever run by OnDatabaseClose() below; see the comments on the
   // ActivityDatabase class for an overall discussion of how cleanup works.
-  virtual ~FullStreamUIPolicy() {}
+  virtual ~FullStreamUIPolicy();
 
   // The ActivityDatabase::PolicyDelegate interface.  These are always called
   // from the database thread.
-  virtual bool OnDatabaseInit(sql::Connection* db) OVERRIDE;
+  virtual bool InitDatabase(sql::Connection* db) OVERRIDE;
+  virtual bool FlushDatabase(sql::Connection* db) OVERRIDE;
+  virtual void OnDatabaseFailure() OVERRIDE;
   virtual void OnDatabaseClose() OVERRIDE;
 
-  // Strips arguments if needed by policy.
-  virtual void ProcessArguments(scoped_refptr<Action> action) const;
+  // Strips arguments if needed by policy.  May return the original object (if
+  // unmodified), or a copy (if modifications were made).  The implementation
+  // in FullStreamUIPolicy returns the action unmodified.
+  virtual scoped_refptr<Action> ProcessArguments(
+      scoped_refptr<Action> action) const;
 
-  // Concatenates arguments.
-  virtual std::string JoinArguments(ActionType action_type,
-                                    const std::string& name,
-                                    const base::ListValue* args) const;
+  // Tracks any pending updates to be written to the database, if write
+  // batching is turned on.  Should only be accessed from the database thread.
+  Action::ActionVector queued_actions_;
 
-  virtual void ProcessWebRequestModifications(
-      base::DictionaryValue& details,
-      std::string& details_string) const;
+ private:
+  // Adds an Action to queued_actions_; this should be invoked only on the
+  // database thread.
+  void QueueAction(scoped_refptr<Action> action);
 
-  // See the comments for the ActivityDatabase class for a discussion of how
-  // cleanup runs.
-  ActivityDatabase* db_;
+  // The implementation of ReadData; this must only run on the database thread.
+  scoped_ptr<Action::ActionVector> DoReadData(const std::string& extension_id,
+                                              const int days_ago);
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc b/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
index 860a6b9..0421357 100644
--- a/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
+++ b/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/cancelable_callback.h"
 #include "base/command_line.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/test/simple_test_clock.h"
 #include "chrome/browser/extensions/activity_log/activity_log.h"
 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -58,13 +60,56 @@
     *CommandLine::ForCurrentProcess() = saved_cmdline_;
   }
 
+  // A helper function to call ReadData on a policy object and wait for the
+  // results to be processed.
+  void CheckReadData(
+      ActivityLogPolicy* policy,
+      const std::string& extension_id,
+      const int day,
+      const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
+    // Submit a request to the policy to read back some data, and call the
+    // checker function when results are available.  This will happen on the
+    // database thread.
+    policy->ReadData(
+        extension_id,
+        day,
+        base::Bind(&FullStreamUIPolicyTest::CheckWrapper,
+                   checker,
+                   base::MessageLoop::current()->QuitClosure()));
+
+    // Set up a timeout that will trigger after 5 seconds; if we haven't
+    // received any results by then assume that the test is broken.
+    base::CancelableClosure timeout(
+        base::Bind(&FullStreamUIPolicyTest::TimeoutCallback));
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE, timeout.callback(), base::TimeDelta::FromSeconds(5));
+
+    // Wait for results; either the checker or the timeout callbacks should
+    // cause the main loop to exit.
+    base::MessageLoop::current()->Run();
+
+    timeout.Cancel();
+  }
+
+  static void CheckWrapper(
+      const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker,
+      const base::Closure& done,
+      scoped_ptr<Action::ActionVector> results) {
+    checker.Run(results.Pass());
+    done.Run();
+  }
+
+  static void TimeoutCallback() {
+    base::MessageLoop::current()->QuitWhenIdle();
+    FAIL() << "Policy test timed out waiting for results";
+  }
+
   static void RetrieveActions_LogAndFetchActions(
       scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
     ASSERT_EQ(2, static_cast<int>(i->size()));
   }
 
-  static void Arguments_Present(
-      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
+  static void Arguments_Present(scoped_ptr<Action::ActionVector> i) {
     scoped_refptr<Action> last = i->front();
     std::string args =
         "ID=odlameecjipmbmbejkplpemijjgpljce CATEGORY=api_call "
@@ -72,6 +117,30 @@
     ASSERT_EQ(args, last->PrintForDebug());
   }
 
+  static void Arguments_GetTodaysActions(
+      scoped_ptr<Action::ActionVector> actions) {
+    std::string api_print =
+        "ID=punky CATEGORY=api_call API=brewster ARGS=[\"woof\"]";
+    std::string dom_print =
+        "ID=punky CATEGORY=dom_access API=lets ARGS=[\"vamoose\"] "
+        "PAGE_URL=http://www.google.com/";
+    ASSERT_EQ(2, static_cast<int>(actions->size()));
+    ASSERT_EQ(dom_print, actions->at(0)->PrintForDebug());
+    ASSERT_EQ(api_print, actions->at(1)->PrintForDebug());
+  }
+
+  static void Arguments_GetOlderActions(
+      scoped_ptr<Action::ActionVector> actions) {
+    std::string api_print =
+        "ID=punky CATEGORY=api_call API=brewster ARGS=[\"woof\"]";
+    std::string dom_print =
+        "ID=punky CATEGORY=dom_access API=lets ARGS=[\"vamoose\"] "
+        "PAGE_URL=http://www.google.com/";
+    ASSERT_EQ(2, static_cast<int>(actions->size()));
+    ASSERT_EQ(dom_print, actions->at(0)->PrintForDebug());
+    ASSERT_EQ(api_print, actions->at(1)->PrintForDebug());
+  }
+
  protected:
   ExtensionService* extension_service_;
   scoped_ptr<TestingProfile> profile_;
@@ -137,8 +206,11 @@
   action_dom->set_page_url(gurl);
   policy->ProcessAction(action_dom);
 
-  policy->ReadData(extension->id(), 0,
-      base::Bind(FullStreamUIPolicyTest::RetrieveActions_LogAndFetchActions));
+  CheckReadData(
+      policy,
+      extension->id(),
+      0,
+      base::Bind(&FullStreamUIPolicyTest::RetrieveActions_LogAndFetchActions));
 
   policy->Close();
 }
@@ -164,8 +236,102 @@
   action->set_args(args.Pass());
 
   policy->ProcessAction(action);
-  policy->ReadData(extension->id(), 0,
-      base::Bind(FullStreamUIPolicyTest::Arguments_Present));
+  CheckReadData(policy,
+                extension->id(),
+                0,
+                base::Bind(&FullStreamUIPolicyTest::Arguments_Present));
+  policy->Close();
+}
+
+TEST_F(FullStreamUIPolicyTest, GetTodaysActions) {
+  ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+
+  // Use a mock clock to ensure that events are not recorded on the wrong day
+  // when the test is run close to local midnight.
+  base::SimpleTestClock mock_clock;
+  mock_clock.SetNow(base::Time::Now().LocalMidnight() +
+                    base::TimeDelta::FromHours(12));
+  policy->SetClockForTesting(&mock_clock);
+
+  // Record some actions
+  scoped_refptr<Action> action =
+      new Action("punky",
+                 mock_clock.Now() - base::TimeDelta::FromMinutes(40),
+                 Action::ACTION_API_CALL,
+                 "brewster");
+  action->mutable_args()->AppendString("woof");
+  policy->ProcessAction(action);
+
+  action =
+      new Action("punky", mock_clock.Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google.com"));
+  policy->ProcessAction(action);
+
+  action = new Action(
+      "scoobydoo", mock_clock.Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google.com"));
+  policy->ProcessAction(action);
+
+  CheckReadData(
+      policy,
+      "punky",
+      0,
+      base::Bind(&FullStreamUIPolicyTest::Arguments_GetTodaysActions));
+  policy->Close();
+}
+
+// Check that we can read back less recent actions in the db.
+TEST_F(FullStreamUIPolicyTest, GetOlderActions) {
+  ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+
+  // Use a mock clock to ensure that events are not recorded on the wrong day
+  // when the test is run close to local midnight.
+  base::SimpleTestClock mock_clock;
+  mock_clock.SetNow(base::Time::Now().LocalMidnight() +
+                    base::TimeDelta::FromHours(12));
+  policy->SetClockForTesting(&mock_clock);
+
+  // Record some actions
+  scoped_refptr<Action> action =
+      new Action("punky",
+                 mock_clock.Now() - base::TimeDelta::FromDays(3) -
+                     base::TimeDelta::FromMinutes(40),
+                 Action::ACTION_API_CALL,
+                 "brewster");
+  action->mutable_args()->AppendString("woof");
+  policy->ProcessAction(action);
+
+  action = new Action("punky",
+                      mock_clock.Now() - base::TimeDelta::FromDays(3),
+                      Action::ACTION_DOM_ACCESS,
+                      "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google.com"));
+  policy->ProcessAction(action);
+
+  action = new Action("punky",
+                      mock_clock.Now(),
+                      Action::ACTION_DOM_ACCESS,
+                      "lets");
+  action->mutable_args()->AppendString("too new");
+  action->set_page_url(GURL("http://www.google.com"));
+  policy->ProcessAction(action);
+
+  action = new Action("punky",
+                      mock_clock.Now() - base::TimeDelta::FromDays(7),
+                      Action::ACTION_DOM_ACCESS,
+                      "lets");
+  action->mutable_args()->AppendString("too old");
+  action->set_page_url(GURL("http://www.google.com"));
+  policy->ProcessAction(action);
+
+  CheckReadData(
+      policy,
+      "punky",
+      3,
+      base::Bind(&FullStreamUIPolicyTest::Arguments_GetOlderActions));
   policy->Close();
 }
 
diff --git a/chrome/browser/extensions/activity_log/stream_noargs_ui_policy.cc b/chrome/browser/extensions/activity_log/stream_noargs_ui_policy.cc
index f977cc6..6ec8916 100644
--- a/chrome/browser/extensions/activity_log/stream_noargs_ui_policy.cc
+++ b/chrome/browser/extensions/activity_log/stream_noargs_ui_policy.cc
@@ -2,44 +2,62 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/json/json_string_value_serializer.h"
 #include "chrome/browser/extensions/activity_log/stream_noargs_ui_policy.h"
 
+#include "base/json/json_string_value_serializer.h"
+#include "chrome/browser/extensions/activity_log/activity_action_constants.h"
+
+namespace constants = activity_log_constants;
+
+namespace {
+
+// We should log the arguments to these API calls.  Be careful when
+// constructing this whitelist to not keep arguments that might compromise
+// privacy by logging too much data to the activity log.
+//
+// TODO(mvrable): The contents of this whitelist should be reviewed and
+// expanded as needed.
+const char* kAlwaysLog[] = {"extension.connect", "extension.sendMessage",
+                            "tabs.executeScript", "tabs.insertCSS"};
+
+}  // namespace
+
 namespace extensions {
 
 StreamWithoutArgsUIPolicy::StreamWithoutArgsUIPolicy(Profile* profile)
     : FullStreamUIPolicy(profile) {
-  for (int i = 0; i < APIAction::kSizeAlwaysLog; i++) {
-    arg_whitelist_api_.insert(APIAction::kAlwaysLog[i]);
+  for (size_t i = 0; i < arraysize(kAlwaysLog); i++) {
+    arg_whitelist_api_.insert(kAlwaysLog[i]);
   }
 }
 
 StreamWithoutArgsUIPolicy::~StreamWithoutArgsUIPolicy() {}
 
-void StreamWithoutArgsUIPolicy::ProcessArguments(
+scoped_refptr<Action> StreamWithoutArgsUIPolicy::ProcessArguments(
     scoped_refptr<Action> action) const {
   if (action->action_type() == Action::ACTION_DOM_ACCESS ||
       action->action_type() == Action::ACTION_DOM_EVENT ||
-      action->action_type() == Action::ACTION_DOM_XHR ||
-      action->action_type() == Action::ACTION_WEB_REQUEST ||
       arg_whitelist_api_.find(action->api_name()) != arg_whitelist_api_.end()) {
     // No stripping of arguments
   } else {
+    // Do not modify the Action in-place, as there might be other users.
+    action = action->Clone();
     action->set_args(scoped_ptr<ListValue>());
-  }
-}
 
-void StreamWithoutArgsUIPolicy::ProcessWebRequestModifications(
-    base::DictionaryValue& details,
-    std::string& details_string) const {
-  // Strip details of the web request modifications (for privacy reasons).
-  DictionaryValue::Iterator details_iterator(details);
-  while (!details_iterator.IsAtEnd()) {
-    details.SetBoolean(details_iterator.key(), true);
-    details_iterator.Advance();
+    // Strip details of the web request modifications (for privacy reasons).
+    if (action->action_type() == Action::ACTION_WEB_REQUEST) {
+      DictionaryValue* details = NULL;
+      if (action->mutable_other()->GetDictionary(constants::kActionWebRequest,
+                                                 &details)) {
+        DictionaryValue::Iterator details_iterator(*details);
+        while (!details_iterator.IsAtEnd()) {
+          details->SetBoolean(details_iterator.key(), true);
+          details_iterator.Advance();
+        }
+      }
+    }
   }
-  JSONStringValueSerializer serializer(&details_string);
-  serializer.SerializeAndOmitBinaryValues(details);
+  return action;
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/stream_noargs_ui_policy.h b/chrome/browser/extensions/activity_log/stream_noargs_ui_policy.h
index b87c5c7..addbacd 100644
--- a/chrome/browser/extensions/activity_log/stream_noargs_ui_policy.h
+++ b/chrome/browser/extensions/activity_log/stream_noargs_ui_policy.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_STREAM_NOARGS_UI_POLICY_H_
 
 #include <string>
+
 #include "base/containers/hash_tables.h"
 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
 
@@ -18,12 +19,12 @@
   virtual ~StreamWithoutArgsUIPolicy();
 
  protected:
-  virtual void ProcessArguments(scoped_refptr<Action> action) const
-      OVERRIDE;
+  // Clears the args field of the action (except for some DOM events and a
+  // small whitelisted set of api_names).  For WEB_REQUEST actions, keeps keys
+  // but removes values from the "web_request" dictionary in other.
+  virtual scoped_refptr<Action> ProcessArguments(
+      scoped_refptr<Action> action) const OVERRIDE;
 
-  virtual void ProcessWebRequestModifications(
-      base::DictionaryValue& details,
-      std::string& details_string) const OVERRIDE;
  private:
   base::hash_set<std::string> arg_whitelist_api_;
 };
diff --git a/chrome/browser/extensions/all_urls_apitest.cc b/chrome/browser/extensions/all_urls_apitest.cc
index c06bed6..24a1886 100644
--- a/chrome/browser/extensions/all_urls_apitest.cc
+++ b/chrome/browser/extensions/all_urls_apitest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
@@ -9,13 +10,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/extensions/extension.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "extensions/common/id_util.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 const std::string kAllUrlsTarget =
     "files/extensions/api_test/all_urls/index.html";
 
@@ -30,7 +28,7 @@
 IN_PROC_BROWSER_TEST_F(AllUrlsApiTest, MAYBE_WhitelistedExtension) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc b/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc
index 2f1b92b..0bf4d8a 100644
--- a/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc
+++ b/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc
@@ -8,9 +8,10 @@
 #include "base/command_line.h"
 #include "chrome/browser/extensions/shell_window_registry.h"
 #include "chrome/browser/ui/extensions/native_app_window.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/api/app_current_window_internal.h"
 #include "chrome/common/extensions/api/app_window.h"
+#include "chrome/common/extensions/features/feature.h"
+#include "extensions/common/switches.h"
 
 using apps::ShellWindow;
 namespace SetBounds = extensions::api::app_current_window_internal::SetBounds;
@@ -25,9 +26,8 @@
     "The context from which the function was called did not have an "
     "associated shell window.";
 
-const char kNoExperimental[] =
-    "This function is experimental. Use --enable-experimental-extension-apis "
-    "to enable.";
+const char kDevChannelOnly[] =
+    "This function is currently only available in the Dev channel.";
 
 }  // namespace
 
@@ -123,9 +123,9 @@
 
 bool AppCurrentWindowInternalSetIconFunction::RunWithWindow(
     ShellWindow* window) {
-  if (!CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableExperimentalExtensionApis)) {
-    error_ = kNoExperimental;
+  if (Feature::GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV &&
+      GetExtension()->location() != extensions::Manifest::COMPONENT) {
+    error_ = kDevChannelOnly;
     return false;
   }
 
diff --git a/chrome/browser/extensions/api/app_window/app_window_api.cc b/chrome/browser/extensions/api/app_window/app_window_api.cc
index 96306e3..7d7e900 100644
--- a/chrome/browser/extensions/api/app_window/app_window_api.cc
+++ b/chrome/browser/extensions/api/app_window/app_window_api.cc
@@ -16,14 +16,15 @@
 #include "chrome/browser/extensions/window_controller.h"
 #include "chrome/browser/ui/apps/chrome_shell_window_delegate.h"
 #include "chrome/browser/ui/extensions/native_app_window.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/api/app_window.h"
+#include "chrome/common/extensions/features/feature.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
+#include "extensions/common/switches.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/rect.h"
 #include "url/gurl.h"
@@ -206,8 +207,8 @@
         create_params.bounds.set_y(*bounds->top.get());
     }
 
-    if (CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kEnableExperimentalExtensionApis)) {
+    if (Feature::GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV ||
+        GetExtension()->location() == extensions::Manifest::COMPONENT) {
       if (options->type == extensions::api::app_window::WINDOW_TYPE_PANEL) {
           create_params.window_type = ShellWindow::WINDOW_TYPE_PANEL;
       }
diff --git a/chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc b/chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc
index 92ad350..0c50191 100644
--- a/chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc
+++ b/chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_test_message_listener.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_out_of_band_pairing_data.h"
@@ -43,11 +42,6 @@
  public:
   BluetoothApiTest() : empty_extension_(utils::CreateEmptyExtension()) {}
 
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
-  }
-
   virtual void SetUpOnMainThread() OVERRIDE {
     SetUpMockAdapter();
     profile1_.reset(new testing::NiceMock<MockBluetoothProfile>());
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_apitest.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_apitest.cc
index 102ced9..4e0f528 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_apitest.cc
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_apitest.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/user_prefs/user_prefs.h"
@@ -23,17 +22,11 @@
 #define MAYBE_BookmarkManager BookmarkManager
 #endif
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_BookmarkManager) {
-  CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
-
   ASSERT_TRUE(RunComponentExtensionTest("bookmark_manager/standard"))
       << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, BookmarkManagerEditDisabled) {
-  CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
-
   Profile* profile = browser()->profile();
 
   // Provide some testing data here, since bookmark editing will be disabled
diff --git a/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc b/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
index d8a5378..b9a32c4 100644
--- a/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
+++ b/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc
@@ -217,14 +217,14 @@
   permitted_dict->SetBoolean(data_type, is_permitted);
 }
 
-void BrowsingDataRemoveFunction::OnBrowsingDataRemoverDone() {
+void BrowsingDataRemoverFunction::OnBrowsingDataRemoverDone() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   this->SendResponse(true);
 
   Release();  // Balanced in RunImpl.
 }
 
-bool BrowsingDataRemoveFunction::RunImpl() {
+bool BrowsingDataRemoverFunction::RunImpl() {
   // If we don't have a profile, something's pretty wrong.
   DCHECK(profile());
 
@@ -265,7 +265,7 @@
     BrowserThread::PostTask(
         BrowserThread::FILE, FROM_HERE,
         base::Bind(
-            &BrowsingDataRemoveFunction::CheckRemovingPluginDataSupported,
+            &BrowsingDataRemoverFunction::CheckRemovingPluginDataSupported,
             this,
             PluginPrefs::GetForProfile(profile())));
   } else {
@@ -276,17 +276,17 @@
   return true;
 }
 
-void BrowsingDataRemoveFunction::CheckRemovingPluginDataSupported(
+void BrowsingDataRemoverFunction::CheckRemovingPluginDataSupported(
     scoped_refptr<PluginPrefs> plugin_prefs) {
   if (!PluginDataRemoverHelper::IsSupported(plugin_prefs.get()))
     removal_mask_ &= ~BrowsingDataRemover::REMOVE_PLUGIN_DATA;
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::Bind(&BrowsingDataRemoveFunction::StartRemoving, this));
+      base::Bind(&BrowsingDataRemoverFunction::StartRemoving, this));
 }
 
-void BrowsingDataRemoveFunction::StartRemoving() {
+void BrowsingDataRemoverFunction::StartRemoving() {
   if (BrowsingDataRemover::is_removing()) {
     error_ = extension_browsing_data_api_constants::kOneAtATimeError;
     SendResponse(false);
@@ -306,7 +306,7 @@
   remover->Remove(removal_mask_, origin_set_mask_);
 }
 
-int BrowsingDataRemoveFunction::ParseOriginSetMask(
+int BrowsingDataRemoverFunction::ParseOriginSetMask(
     const base::DictionaryValue& options) {
   // Parse the |options| dictionary to generate the origin set mask. Default to
   // UNPROTECTED_WEB if the developer doesn't specify anything.
@@ -350,7 +350,7 @@
 // |bad_message_| (like EXTENSION_FUNCTION_VALIDATE would if this were a bool
 // method) if 'dataToRemove' is not present or any data-type keys don't have
 // supported (boolean) values.
-int RemoveBrowsingDataFunction::GetRemovalMask() {
+int BrowsingDataRemoveFunction::GetRemovalMask() {
   base::DictionaryValue* data_to_remove;
   if (!args_->GetDictionary(1, &data_to_remove)) {
     bad_message_ = true;
@@ -374,51 +374,51 @@
   return removal_mask;
 }
 
-int RemoveAppCacheFunction::GetRemovalMask() {
+int BrowsingDataRemoveAppcacheFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_APPCACHE;
 }
 
-int RemoveCacheFunction::GetRemovalMask() {
+int BrowsingDataRemoveCacheFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_CACHE;
 }
 
-int RemoveCookiesFunction::GetRemovalMask() {
+int BrowsingDataRemoveCookiesFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_COOKIES |
          BrowsingDataRemover::REMOVE_SERVER_BOUND_CERTS;
 }
 
-int RemoveDownloadsFunction::GetRemovalMask() {
+int BrowsingDataRemoveDownloadsFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_DOWNLOADS;
 }
 
-int RemoveFileSystemsFunction::GetRemovalMask() {
+int BrowsingDataRemoveFileSystemsFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_FILE_SYSTEMS;
 }
 
-int RemoveFormDataFunction::GetRemovalMask() {
+int BrowsingDataRemoveFormDataFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_FORM_DATA;
 }
 
-int RemoveHistoryFunction::GetRemovalMask() {
+int BrowsingDataRemoveHistoryFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_HISTORY;
 }
 
-int RemoveIndexedDBFunction::GetRemovalMask() {
+int BrowsingDataRemoveIndexedDBFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_INDEXEDDB;
 }
 
-int RemoveLocalStorageFunction::GetRemovalMask() {
+int BrowsingDataRemoveLocalStorageFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_LOCAL_STORAGE;
 }
 
-int RemovePluginDataFunction::GetRemovalMask() {
+int BrowsingDataRemovePluginDataFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_PLUGIN_DATA;
 }
 
-int RemovePasswordsFunction::GetRemovalMask() {
+int BrowsingDataRemovePasswordsFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_PASSWORDS;
 }
 
-int RemoveWebSQLFunction::GetRemovalMask() {
+int BrowsingDataRemoveWebSQLFunction::GetRemovalMask() {
   return BrowsingDataRemover::REMOVE_WEBSQL;
 }
diff --git a/chrome/browser/extensions/api/browsing_data/browsing_data_api.h b/chrome/browser/extensions/api/browsing_data/browsing_data_api.h
index f0a4f79..1078ec2 100644
--- a/chrome/browser/extensions/api/browsing_data/browsing_data_api.h
+++ b/chrome/browser/extensions/api/browsing_data/browsing_data_api.h
@@ -80,7 +80,7 @@
 //
 // Each child class must implement GetRemovalMask(), which returns the bitmask
 // of data types to remove.
-class BrowsingDataRemoveFunction : public AsyncExtensionFunction,
+class BrowsingDataRemoverFunction : public AsyncExtensionFunction,
                                    public BrowsingDataRemover::Observer {
  public:
   // BrowsingDataRemover::Observer interface method.
@@ -90,7 +90,7 @@
   virtual bool RunImpl() OVERRIDE;
 
  protected:
-  virtual ~BrowsingDataRemoveFunction() {}
+  virtual ~BrowsingDataRemoverFunction() {}
 
   // Children should override this method to provide the proper removal mask
   // based on the API call they represent.
@@ -114,158 +114,161 @@
   int origin_set_mask_;
 };
 
-class RemoveAppCacheFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveAppcacheFunction : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removeAppcache",
                              BROWSINGDATA_REMOVEAPPCACHE)
 
  protected:
-  virtual ~RemoveAppCacheFunction() {}
+  virtual ~BrowsingDataRemoveAppcacheFunction() {}
 
-  // BrowsingDataTypeExtensionFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemoveBrowsingDataFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveFunction : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.remove", BROWSINGDATA_REMOVE)
 
  protected:
-  virtual ~RemoveBrowsingDataFunction() {}
+  virtual ~BrowsingDataRemoveFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemoveCacheFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveCacheFunction : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removeCache",
                              BROWSINGDATA_REMOVECACHE)
 
  protected:
-  virtual ~RemoveCacheFunction() {}
+  virtual ~BrowsingDataRemoveCacheFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemoveCookiesFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveCookiesFunction : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removeCookies",
                              BROWSINGDATA_REMOVECOOKIES)
 
  protected:
-  virtual ~RemoveCookiesFunction() {}
+  virtual ~BrowsingDataRemoveCookiesFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemoveDownloadsFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveDownloadsFunction : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removeDownloads",
                              BROWSINGDATA_REMOVEDOWNLOADS)
 
  protected:
-  virtual ~RemoveDownloadsFunction() {}
+  virtual ~BrowsingDataRemoveDownloadsFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemoveFileSystemsFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveFileSystemsFunction
+    : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removeFileSystems",
                              BROWSINGDATA_REMOVEFILESYSTEMS)
 
  protected:
-  virtual ~RemoveFileSystemsFunction() {}
+  virtual ~BrowsingDataRemoveFileSystemsFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemoveFormDataFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveFormDataFunction : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removeFormData",
                              BROWSINGDATA_REMOVEFORMDATA)
 
  protected:
-  virtual ~RemoveFormDataFunction() {}
+  virtual ~BrowsingDataRemoveFormDataFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemoveHistoryFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveHistoryFunction : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removeHistory",
                              BROWSINGDATA_REMOVEHISTORY)
 
  protected:
-  virtual ~RemoveHistoryFunction() {}
+  virtual ~BrowsingDataRemoveHistoryFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemoveIndexedDBFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveIndexedDBFunction : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removeIndexedDB",
                              BROWSINGDATA_REMOVEINDEXEDDB)
 
  protected:
-  virtual ~RemoveIndexedDBFunction() {}
+  virtual ~BrowsingDataRemoveIndexedDBFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemoveLocalStorageFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveLocalStorageFunction
+    : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removeLocalStorage",
                              BROWSINGDATA_REMOVELOCALSTORAGE)
 
  protected:
-  virtual ~RemoveLocalStorageFunction() {}
+  virtual ~BrowsingDataRemoveLocalStorageFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemovePluginDataFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemovePluginDataFunction
+    : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removePluginData",
                              BROWSINGDATA_REMOVEPLUGINDATA)
 
  protected:
-  virtual ~RemovePluginDataFunction() {}
+  virtual ~BrowsingDataRemovePluginDataFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemovePasswordsFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemovePasswordsFunction : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removePasswords",
                              BROWSINGDATA_REMOVEPASSWORDS)
 
  protected:
-  virtual ~RemovePasswordsFunction() {}
+  virtual ~BrowsingDataRemovePasswordsFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
-class RemoveWebSQLFunction : public BrowsingDataRemoveFunction {
+class BrowsingDataRemoveWebSQLFunction : public BrowsingDataRemoverFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("browsingData.removeWebSQL",
                              BROWSINGDATA_REMOVEWEBSQL)
 
  protected:
-  virtual ~RemoveWebSQLFunction() {}
+  virtual ~BrowsingDataRemoveWebSQLFunction() {}
 
-  // BrowsingDataRemoveFunction:
+  // BrowsingDataRemoverFunction:
   virtual int GetRemovalMask() OVERRIDE;
 };
 
diff --git a/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc b/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc
index 4bd65cf..2be0ea3 100644
--- a/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc
+++ b/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc
@@ -84,11 +84,11 @@
     return result ? mask_value : 0;
   }
 
-  void RunRemoveBrowsingDataFunctionAndCompareRemovalMask(
+  void RunBrowsingDataRemoveFunctionAndCompareRemovalMask(
       const std::string& data_types,
       int expected_mask) {
-    scoped_refptr<RemoveBrowsingDataFunction> function =
-        new RemoveBrowsingDataFunction();
+    scoped_refptr<BrowsingDataRemoveFunction> function =
+        new BrowsingDataRemoveFunction();
     SCOPED_TRACE(data_types);
     EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
         function.get(),
@@ -98,18 +98,18 @@
     EXPECT_EQ(UNPROTECTED_WEB, GetOriginSetMask());
   }
 
-  void RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  void RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       const std::string& key,
       int expected_mask) {
-    RunRemoveBrowsingDataFunctionAndCompareRemovalMask(
+    RunBrowsingDataRemoveFunctionAndCompareRemovalMask(
         std::string("{\"") + key + "\": true}", expected_mask);
   }
 
-  void RunRemoveBrowsingDataFunctionAndCompareOriginSetMask(
+  void RunBrowsingDataRemoveFunctionAndCompareOriginSetMask(
       const std::string& protectedStr,
       int expected_mask) {
-    scoped_refptr<RemoveBrowsingDataFunction> function =
-        new RemoveBrowsingDataFunction();
+    scoped_refptr<BrowsingDataRemoveFunction> function =
+        new BrowsingDataRemoveFunction();
     SCOPED_TRACE(protectedStr);
     EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
         function.get(),
@@ -238,8 +238,8 @@
   // The kAllowDeletingBrowserHistory pref must be set to false before this
   // is called.
   void CheckRemovalPermitted(const std::string& data_types, bool permitted) {
-    scoped_refptr<RemoveBrowsingDataFunction> function =
-        new RemoveBrowsingDataFunction();
+    scoped_refptr<BrowsingDataRemoveFunction> function =
+        new BrowsingDataRemoveFunction();
     std::string args = "[{\"since\": 1}," + data_types + "]";
 
     if (permitted) {
@@ -262,8 +262,8 @@
 
 IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, OneAtATime) {
   BrowsingDataRemover::set_removing(true);
-  scoped_refptr<RemoveBrowsingDataFunction> function =
-      new RemoveBrowsingDataFunction();
+  scoped_refptr<BrowsingDataRemoveFunction> function =
+      new BrowsingDataRemoveFunction();
   EXPECT_TRUE(
       MatchPattern(RunFunctionAndReturnError(
                        function.get(), kRemoveEverythingArguments, browser()),
@@ -305,8 +305,8 @@
 // Use-after-free, see http://crbug.com/116522
 IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest,
                        DISABLED_RemoveBrowsingDataAll) {
-  scoped_refptr<RemoveBrowsingDataFunction> function =
-      new RemoveBrowsingDataFunction();
+  scoped_refptr<BrowsingDataRemoveFunction> function =
+      new BrowsingDataRemoveFunction();
   EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(function.get(),
                                                    kRemoveEverythingArguments,
                                                    browser()));
@@ -323,26 +323,26 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, BrowsingDataOriginSetMask) {
-  RunRemoveBrowsingDataFunctionAndCompareOriginSetMask("{}", 0);
+  RunBrowsingDataRemoveFunctionAndCompareOriginSetMask("{}", 0);
 
-  RunRemoveBrowsingDataFunctionAndCompareOriginSetMask(
+  RunBrowsingDataRemoveFunctionAndCompareOriginSetMask(
       "{\"unprotectedWeb\": true}", UNPROTECTED_WEB);
-  RunRemoveBrowsingDataFunctionAndCompareOriginSetMask(
+  RunBrowsingDataRemoveFunctionAndCompareOriginSetMask(
       "{\"protectedWeb\": true}", PROTECTED_WEB);
-  RunRemoveBrowsingDataFunctionAndCompareOriginSetMask(
+  RunBrowsingDataRemoveFunctionAndCompareOriginSetMask(
       "{\"extension\": true}", EXTENSION);
 
-  RunRemoveBrowsingDataFunctionAndCompareOriginSetMask(
+  RunBrowsingDataRemoveFunctionAndCompareOriginSetMask(
       "{\"unprotectedWeb\": true, \"protectedWeb\": true}",
       UNPROTECTED_WEB | PROTECTED_WEB);
-  RunRemoveBrowsingDataFunctionAndCompareOriginSetMask(
+  RunBrowsingDataRemoveFunctionAndCompareOriginSetMask(
       "{\"unprotectedWeb\": true, \"extension\": true}",
       UNPROTECTED_WEB | EXTENSION);
-  RunRemoveBrowsingDataFunctionAndCompareOriginSetMask(
+  RunBrowsingDataRemoveFunctionAndCompareOriginSetMask(
       "{\"protectedWeb\": true, \"extension\": true}",
       PROTECTED_WEB | EXTENSION);
 
-  RunRemoveBrowsingDataFunctionAndCompareOriginSetMask(
+  RunBrowsingDataRemoveFunctionAndCompareOriginSetMask(
       ("{\"unprotectedWeb\": true, \"protectedWeb\": true, "
        "\"extension\": true}"),
       UNPROTECTED_WEB | PROTECTED_WEB | EXTENSION);
@@ -350,38 +350,38 @@
 
 IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest,
                        FLAKY_BrowsingDataRemovalMask) {
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "appcache", BrowsingDataRemover::REMOVE_APPCACHE);
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "cache", BrowsingDataRemover::REMOVE_CACHE);
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "cookies", BrowsingDataRemover::REMOVE_COOKIES);
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "downloads", BrowsingDataRemover::REMOVE_DOWNLOADS);
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "fileSystems", BrowsingDataRemover::REMOVE_FILE_SYSTEMS);
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "formData", BrowsingDataRemover::REMOVE_FORM_DATA);
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "history", BrowsingDataRemover::REMOVE_HISTORY);
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "indexedDB", BrowsingDataRemover::REMOVE_INDEXEDDB);
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "localStorage", BrowsingDataRemover::REMOVE_LOCAL_STORAGE);
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "serverBoundCertificates",
       BrowsingDataRemover::REMOVE_SERVER_BOUND_CERTS);
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "passwords", BrowsingDataRemover::REMOVE_PASSWORDS);
   // We can't remove plugin data inside a test profile.
-  RunRemoveBrowsingDataWithKeyAndCompareRemovalMask(
+  RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(
       "webSQL", BrowsingDataRemover::REMOVE_WEBSQL);
 }
 
 // Test an arbitrary combination of data types.
 IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest,
                        BrowsingDataRemovalMaskCombination) {
-  RunRemoveBrowsingDataFunctionAndCompareRemovalMask(
+  RunBrowsingDataRemoveFunctionAndCompareRemovalMask(
        "{\"appcache\": true, \"cookies\": true, \"history\": true}",
        BrowsingDataRemover::REMOVE_APPCACHE |
            BrowsingDataRemover::REMOVE_COOKIES |
@@ -421,8 +421,8 @@
     EXPECT_TRUE(serializer.Serialize(*data_to_remove));
   }
   {
-    scoped_refptr<RemoveBrowsingDataFunction> remove_function =
-        new RemoveBrowsingDataFunction();
+    scoped_refptr<BrowsingDataRemoveFunction> remove_function =
+        new BrowsingDataRemoveFunction();
     SCOPED_TRACE("remove_json");
     EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult(
         remove_function.get(),
@@ -434,29 +434,29 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, ShortcutFunctionRemovalMask) {
-  RunAndCompareRemovalMask<RemoveAppCacheFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemoveAppcacheFunction>(
       BrowsingDataRemover::REMOVE_APPCACHE);
-  RunAndCompareRemovalMask<RemoveCacheFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemoveCacheFunction>(
       BrowsingDataRemover::REMOVE_CACHE);
-  RunAndCompareRemovalMask<RemoveCookiesFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemoveCookiesFunction>(
       BrowsingDataRemover::REMOVE_COOKIES |
       BrowsingDataRemover::REMOVE_SERVER_BOUND_CERTS);
-  RunAndCompareRemovalMask<RemoveDownloadsFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemoveDownloadsFunction>(
       BrowsingDataRemover::REMOVE_DOWNLOADS);
-  RunAndCompareRemovalMask<RemoveFileSystemsFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemoveFileSystemsFunction>(
       BrowsingDataRemover::REMOVE_FILE_SYSTEMS);
-  RunAndCompareRemovalMask<RemoveFormDataFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemoveFormDataFunction>(
       BrowsingDataRemover::REMOVE_FORM_DATA);
-  RunAndCompareRemovalMask<RemoveHistoryFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemoveHistoryFunction>(
       BrowsingDataRemover::REMOVE_HISTORY);
-  RunAndCompareRemovalMask<RemoveIndexedDBFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemoveIndexedDBFunction>(
       BrowsingDataRemover::REMOVE_INDEXEDDB);
-  RunAndCompareRemovalMask<RemoveLocalStorageFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemoveLocalStorageFunction>(
       BrowsingDataRemover::REMOVE_LOCAL_STORAGE);
   // We can't remove plugin data inside a test profile.
-  RunAndCompareRemovalMask<RemovePasswordsFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemovePasswordsFunction>(
       BrowsingDataRemover::REMOVE_PASSWORDS);
-  RunAndCompareRemovalMask<RemoveWebSQLFunction>(
+  RunAndCompareRemovalMask<BrowsingDataRemoveWebSQLFunction>(
       BrowsingDataRemover::REMOVE_WEBSQL);
 }
 
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
index 8c3bbed..10fb412 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
@@ -18,9 +18,6 @@
 namespace extensions {
 
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentSettings) {
-  CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
-
   EXPECT_TRUE(RunExtensionTest("content_settings/standard")) << message_;
 
   HostContentSettingsMap* map =
@@ -100,7 +97,6 @@
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     ExtensionApiTest::SetUpCommandLine(command_line);
     command_line->AppendSwitch(switches::kDisablePluginsDiscovery);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
   }
 };
 
diff --git a/chrome/browser/extensions/api/declarative/declarative_api.cc b/chrome/browser/extensions/api/declarative/declarative_api.cc
index 4274b20..9f2f5cc 100644
--- a/chrome/browser/extensions/api/declarative/declarative_api.cc
+++ b/chrome/browser/extensions/api/declarative/declarative_api.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/extensions/extension_system_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/extensions/api/events.h"
+#include "chrome/common/extensions/api/extension_api.h"
 #include "content/public/browser/browser_thread.h"
 
 using extensions::api::events::Rule;
@@ -29,7 +30,11 @@
 bool RulesFunction::HasPermission() {
   std::string event_name;
   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &event_name));
-  return extension_->HasAPIPermission(event_name);
+  Feature::Availability availability =
+      ExtensionAPI::GetSharedInstance()->IsAvailable(
+          event_name, extension_, Feature::BLESSED_EXTENSION_CONTEXT,
+          source_url());
+  return availability.is_available();
 }
 
 bool RulesFunction::RunImpl() {
diff --git a/chrome/browser/extensions/api/declarative/declarative_apitest.cc b/chrome/browser/extensions/api/declarative/declarative_apitest.cc
index e9ba29b..1ee36ce 100644
--- a/chrome/browser/extensions/api/declarative/declarative_apitest.cc
+++ b/chrome/browser/extensions/api/declarative/declarative_apitest.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/extensions/extension_system_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/browser_thread.h"
@@ -23,14 +22,6 @@
 using extensions::WebRequestRulesRegistry;
 
 class DeclarativeApiTest : public ExtensionApiTest {
- public:
-  DeclarativeApiTest() {}
-  virtual ~DeclarativeApiTest() {}
-
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
-  }
 };
 
 IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, DeclarativeApi) {
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
index 5684f63..aa9bf22 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
@@ -33,11 +33,12 @@
 const char kRuleId4[] = "rule4";
 }  // namespace
 
+using extension_test_util::LoadManifest;
+using extension_test_util::LoadManifestUnchecked;
+
 namespace extensions {
 
 using base::Value;
-using extension_test_util::LoadManifest;
-using extension_test_util::LoadManifestUnchecked;
 using testing::HasSubstr;
 
 namespace helpers = extension_web_request_api_helpers;
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index 1db4081..b6c766a 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -60,8 +60,8 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/webui/web_ui_util.h"
 #include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_operation.h"
 #include "webkit/browser/fileapi/file_system_operation_runner.h"
-#include "webkit/browser/fileapi/local_file_system_operation.h"
 #include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
 #include "webkit/common/blob/shareable_file_reference.h"
 
@@ -1169,14 +1169,18 @@
   SET_STRING("extensionSettings", IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE);
 
   SET_STRING("appsDevtoolSearch", IDS_APPS_DEVTOOL_SEARCH);
-  SET_STRING("appsDevtoolNoApps", IDS_APPS_DEVTOOL_NO_APPS_INSTALLED);
   SET_STRING("appsDevtoolApps", IDS_APPS_DEVTOOL_APPS_INSTALLED);
   SET_STRING("appsDevtoolExtensions", IDS_APPS_DEVTOOL_EXTENSIONS_INSTALLED);
   SET_STRING("appsDevtoolNoExtensions", IDS_EXTENSIONS_NONE_INSTALLED);
   SET_STRING("appsDevtoolUnpacked", IDS_APPS_DEVTOOL_UNPACKED_INSTALLED);
   SET_STRING("appsDevtoolInstalled", IDS_APPS_DEVTOOL_INSTALLED);
-  SET_STRING("appsDevtoolNoUnpacked", IDS_APPS_DEVTOOL_NO_UNPACKED_INSTALLED);
-  SET_STRING("appsDevtoolTitle", IDS_APPS_DEVTOOL_TITLE);
+  SET_STRING("appsDevtoolNoPackedApps", IDS_APPS_DEVTOOL_NO_PACKED_APPS);
+  SET_STRING("appsDevtoolNoUnpackedApps", IDS_APPS_DEVTOOL_NO_UNPACKED_APPS);
+  SET_STRING("appsDevtoolNoPackedExtensions",
+      IDS_APPS_DEVTOOL_NO_PACKED_EXTENSIONS);
+  SET_STRING("appsDevtoolNoUnpackedExtensions",
+      IDS_APPS_DEVTOOL_NO_UNPACKED_EXTENSIONS);
+  SET_STRING("appsDevtoolUpdating", IDS_APPS_DEVTOOL_UPDATING);
   SET_STRING("extensionSettingsGetMoreExtensions", IDS_GET_MORE_EXTENSIONS);
   SET_STRING("extensionSettingsExtensionId", IDS_EXTENSIONS_ID);
   SET_STRING("extensionSettingsExtensionPath", IDS_EXTENSIONS_PATH);
@@ -1230,8 +1234,8 @@
   SET_STRING("packExtensionOverlay", IDS_EXTENSION_PACK_DIALOG_TITLE);
   SET_STRING("packExtensionHeading", IDS_EXTENSION_ADT_PACK_DIALOG_HEADING);
   SET_STRING("packExtensionCommit", IDS_EXTENSION_PACK_BUTTON);
-  SET_STRING("ok",IDS_OK);
-  SET_STRING("cancel",IDS_CANCEL);
+  SET_STRING("ok", IDS_OK);
+  SET_STRING("cancel", IDS_CANCEL);
   SET_STRING("packExtensionRootDir",
      IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL);
   SET_STRING("packExtensionPrivateKey",
@@ -1241,6 +1245,16 @@
   SET_STRING("packExtensionWarningTitle", IDS_EXTENSION_PACK_WARNING_TITLE);
   SET_STRING("packExtensionErrorTitle", IDS_EXTENSION_PACK_ERROR_TITLE);
 
+// Delete confirmation dialog.
+  SET_STRING("deleteConfirmationDeleteButton",
+      IDS_APPS_DEVTOOL_DELETE_CONFIRMATION_BUTTON);
+  SET_STRING("deleteConfirmationTitle",
+      IDS_APPS_DEVTOOL_DELETE_CONFIRMATION_TITLE);
+  SET_STRING("deleteConfirmationMessageApp",
+      IDS_APPS_DEVTOOL_DELETE_CONFIRMATION_MESSAGE_APP);
+  SET_STRING("deleteConfirmationMessageExtension",
+      IDS_APPS_DEVTOOL_DELETE_CONFIRMATION_MESSAGE_EXTENSION);
+
   #undef   SET_STRING
   return true;
 }
diff --git a/chrome/browser/extensions/api/dns/dns_apitest.cc b/chrome/browser/extensions/api/dns/dns_apitest.cc
index ccd9602..3e12984 100644
--- a/chrome/browser/extensions/api/dns/dns_apitest.cc
+++ b/chrome/browser/extensions/api/dns/dns_apitest.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/extensions/api/dns/mock_host_resolver_creator.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
-#include "chrome/common/chrome_switches.h"
+#include "extensions/common/switches.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
 #include "net/dns/mock_host_resolver.h"
@@ -27,7 +27,8 @@
 
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
+    command_line->AppendSwitch(
+        extensions::switches::kEnableExperimentalExtensionApis);
   }
 
   virtual void SetUpOnMainThread() OVERRIDE {
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.cc b/chrome/browser/extensions/api/downloads/downloads_api.cc
index 0bfa228..2b6c64a 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api.cc
@@ -47,17 +47,22 @@
 #include "chrome/browser/icon_loader.h"
 #include "chrome/browser/icon_manager.h"
 #include "chrome/browser/platform_util.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/common/cancelable_task_tracker.h"
 #include "chrome/common/extensions/api/downloads.h"
+#include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/permissions/permissions_data.h"
 #include "content/public/browser/download_interrupt_reasons.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_save_info.h"
 #include "content/public/browser/download_url_parameters.h"
+#include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/resource_context.h"
@@ -82,6 +87,7 @@
 
 const char kEmptyFile[] = "Filename not yet determined";
 const char kFileAlreadyDeleted[] = "Download file already deleted";
+const char kHostPermission[] = "Access to that hostname must be requested";
 const char kIconNotFound[] = "Icon not found";
 const char kInvalidDangerType[] = "Invalid danger type";
 const char kInvalidFilename[] = "Invalid filename";
@@ -99,6 +105,9 @@
 const char kNotInProgress[] = "Download must be in progress";
 const char kNotResumable[] = "DownloadItem.canResume must be true";
 const char kOpenPermission[] = "The \"downloads.open\" permission is required";
+const char kShelfDisabled[] = "Another extension has disabled the shelf";
+const char kShelfPermission[] = "downloads.setShelfEnabled requires the "
+  "\"downloads.shelf\" permission";
 const char kTooManyListeners[] = "Each extension may have at most one "
   "onDeterminingFilename listener between all of its renderer execution "
   "contexts.";
@@ -114,6 +123,8 @@
 const int  kDefaultIconSize = 32;
 
 // Parameter keys
+const char kByExtensionIdKey[] = "byExtensionId";
+const char kByExtensionNameKey[] = "byExtensionName";
 const char kBytesReceivedKey[] = "bytesReceived";
 const char kCanResumeKey[] = "canResume";
 const char kDangerAccepted[] = "accepted";
@@ -227,7 +238,7 @@
 
 scoped_ptr<base::DictionaryValue> DownloadItemToJSON(
     DownloadItem* download_item,
-    bool incognito) {
+    Profile* profile) {
   base::DictionaryValue* json = new base::DictionaryValue();
   json->SetBoolean(kExistsKey, !download_item->GetFileExternallyRemoved());
   json->SetInteger(kIdKey, download_item->GetId());
@@ -246,7 +257,7 @@
   json->SetString(kStartTimeKey, TimeToISO8601(download_item->GetStartTime()));
   json->SetInteger(kBytesReceivedKey, download_item->GetReceivedBytes());
   json->SetInteger(kTotalBytesKey, download_item->GetTotalBytes());
-  json->SetBoolean(kIncognitoKey, incognito);
+  json->SetBoolean(kIncognitoKey, profile->IsOffTheRecord());
   if (download_item->GetState() == DownloadItem::INTERRUPTED) {
     json->SetString(kErrorKey, content::InterruptReasonDebugString(
         download_item->GetLastReason()));
@@ -261,6 +272,20 @@
     base::Time now = base::Time::Now();
     json->SetString(kEstimatedEndTimeKey, TimeToISO8601(now + time_remaining));
   }
+  DownloadedByExtension* by_ext = DownloadedByExtension::Get(download_item);
+  if (by_ext) {
+    json->SetString(kByExtensionIdKey, by_ext->id());
+    json->SetString(kByExtensionNameKey, by_ext->name());
+    // Lookup the extension's current name() in case the user changed their
+    // language. This won't work if the extension was uninstalled, so the name
+    // might be the wrong language.
+    bool include_disabled = true;
+    const extensions::Extension* extension = extensions::ExtensionSystem::Get(
+        profile)->extension_service()->GetExtensionById(
+            by_ext->id(), include_disabled);
+    if (extension)
+      json->SetString(kByExtensionNameKey, extension->name());
+  }
   // TODO(benjhayden): Implement fileSize.
   json->SetInteger(kFileSizeKey, download_item->GetTotalBytes());
   return scoped_ptr<base::DictionaryValue>(json);
@@ -398,7 +423,7 @@
   DOWNLOADS_FUNCTION_RESUME = 3,
   DOWNLOADS_FUNCTION_CANCEL = 4,
   DOWNLOADS_FUNCTION_ERASE = 5,
-  DOWNLOADS_FUNCTION_SET_DESTINATION = 6,
+  // 6 unused
   DOWNLOADS_FUNCTION_ACCEPT_DANGER = 7,
   DOWNLOADS_FUNCTION_SHOW = 8,
   DOWNLOADS_FUNCTION_DRAG = 9,
@@ -406,6 +431,7 @@
   DOWNLOADS_FUNCTION_OPEN = 11,
   DOWNLOADS_FUNCTION_REMOVE_FILE = 12,
   DOWNLOADS_FUNCTION_SHOW_DEFAULT_FOLDER = 13,
+  DOWNLOADS_FUNCTION_SET_SHELF_ENABLED = 14,
   // Insert new values here, not at the beginning.
   DOWNLOADS_FUNCTION_LAST
 };
@@ -882,6 +908,25 @@
 
 }  // namespace
 
+const char DownloadedByExtension::kKey[] =
+  "DownloadItem DownloadedByExtension";
+
+DownloadedByExtension* DownloadedByExtension::Get(
+    content::DownloadItem* item) {
+  base::SupportsUserData::Data* data = item->GetUserData(kKey);
+  return (data == NULL) ? NULL :
+      static_cast<DownloadedByExtension*>(data);
+}
+
+DownloadedByExtension::DownloadedByExtension(
+    content::DownloadItem* item,
+    const std::string& id,
+    const std::string& name)
+  : id_(id),
+    name_(name) {
+  item->SetUserData(kKey, this);
+}
+
 DownloadsDownloadFunction::DownloadsDownloadFunction() {}
 
 DownloadsDownloadFunction::~DownloadsDownloadFunction() {}
@@ -892,7 +937,12 @@
   EXTENSION_FUNCTION_VALIDATE(params.get());
   const extensions::api::downloads::DownloadOptions& options = params->options;
   GURL download_url(options.url);
-  if (Fault(!download_url.is_valid(), errors::kInvalidURL, &error_))
+  if (Fault(!download_url.is_valid(), errors::kInvalidURL, &error_) ||
+      Fault((!download_url.SchemeIs("data") &&
+             (download_url.GetOrigin() != GetExtension()->url().GetOrigin()) &&
+             !extensions::PermissionsData::HasHostPermission(
+                 GetExtension(), download_url)),
+            errors::kHostPermission, &error_))
     return false;
 
   Profile* current_profile = profile();
@@ -985,6 +1035,9 @@
       data->CreatorSuggestedFilename(
           creator_suggested_filename, creator_conflict_action);
     }
+    new DownloadedByExtension(
+        item, GetExtension()->id(), GetExtension()->name());
+    item->UpdateObservers();
   } else {
     DCHECK_NE(net::OK, error);
     error_ = net::ErrorToString(error);
@@ -1022,7 +1075,8 @@
     bool off_record = ((incognito_manager != NULL) &&
                        (incognito_manager->GetDownload(download_id) != NULL));
     scoped_ptr<base::DictionaryValue> json_item(DownloadItemToJSON(
-        *it, off_record));
+        *it, off_record ? profile()->GetOffTheRecordProfile()
+                        : profile()->GetOriginalProfile()));
     json_results->Append(json_item.release());
   }
   SetResult(json_results);
@@ -1301,6 +1355,64 @@
   return true;
 }
 
+DownloadsSetShelfEnabledFunction::DownloadsSetShelfEnabledFunction() {}
+
+DownloadsSetShelfEnabledFunction::~DownloadsSetShelfEnabledFunction() {}
+
+bool DownloadsSetShelfEnabledFunction::RunImpl() {
+  scoped_ptr<extensions::api::downloads::SetShelfEnabled::Params> params(
+      extensions::api::downloads::SetShelfEnabled::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+  if (!GetExtension()->HasAPIPermission(
+        extensions::APIPermission::kDownloadsShelf)) {
+    error_ = download_extension_errors::kShelfPermission;
+    return false;
+  }
+
+  RecordApiFunctions(DOWNLOADS_FUNCTION_SET_SHELF_ENABLED);
+  DownloadManager* manager = NULL;
+  DownloadManager* incognito_manager = NULL;
+  GetManagers(profile(), include_incognito(), &manager, &incognito_manager);
+  DownloadService* service = NULL;
+  DownloadService* incognito_service = NULL;
+  if (manager) {
+    service = DownloadServiceFactory::GetForBrowserContext(
+        manager->GetBrowserContext());
+    service->GetExtensionEventRouter()->SetShelfEnabled(
+        GetExtension(), params->enabled);
+  }
+  if (incognito_manager) {
+    incognito_service = DownloadServiceFactory::GetForBrowserContext(
+        incognito_manager->GetBrowserContext());
+    incognito_service->GetExtensionEventRouter()->SetShelfEnabled(
+        GetExtension(), params->enabled);
+  }
+
+  BrowserList* browsers = BrowserList::GetInstance(chrome::GetActiveDesktop());
+  if (browsers) {
+    for (BrowserList::const_iterator iter = browsers->begin();
+        iter != browsers->end(); ++iter) {
+      const Browser* browser = *iter;
+      DownloadService* current_service =
+        DownloadServiceFactory::GetForBrowserContext(browser->profile());
+      if (((current_service == service) ||
+           (current_service == incognito_service)) &&
+          browser->window()->IsDownloadShelfVisible() &&
+          !current_service->IsShelfEnabled())
+        browser->window()->GetDownloadShelf()->Close(DownloadShelf::AUTOMATIC);
+    }
+  }
+
+  if (params->enabled &&
+      ((manager && !service->IsShelfEnabled()) ||
+       (incognito_manager && !incognito_service->IsShelfEnabled()))) {
+    error_ = download_extension_errors::kShelfDisabled;
+    return false;
+  }
+
+  return true;
+}
+
 DownloadsGetFileIconFunction::DownloadsGetFileIconFunction()
     : icon_extractor_(new DownloadFileIconExtractorImpl()) {
 }
@@ -1358,6 +1470,8 @@
       notifier_(manager, this) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(profile_);
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
+                 content::Source<Profile>(profile_));
   extensions::EventRouter* router = extensions::ExtensionSystem::Get(profile_)->
       event_router();
   if (router)
@@ -1372,6 +1486,22 @@
     router->UnregisterObserver(this);
 }
 
+void ExtensionDownloadsEventRouter::SetShelfEnabled(
+    const extensions::Extension* extension, bool enabled) {
+  std::set<const extensions::Extension*>::iterator iter =
+    shelf_disabling_extensions_.find(extension);
+  if (iter == shelf_disabling_extensions_.end()) {
+    if (!enabled)
+      shelf_disabling_extensions_.insert(extension);
+  } else if (enabled) {
+    shelf_disabling_extensions_.erase(extension);
+  }
+}
+
+bool ExtensionDownloadsEventRouter::IsShelfEnabled() const {
+  return shelf_disabling_extensions_.empty();
+}
+
 // The method by which extensions hook into the filename determination process
 // is based on the method by which the omnibox API allows extensions to hook
 // into the omnibox autocompletion process. Extensions that wish to play a part
@@ -1413,7 +1543,7 @@
   data->set_filename_change_callbacks(no_change, change);
   bool any_determiners = false;
   base::DictionaryValue* json = DownloadItemToJSON(
-      item, profile_->IsOffTheRecord()).release();
+      item, profile_).release();
   json->SetString(kFilenameKey, suggested_path.LossyDisplayName());
   DispatchEvent(events::kOnDownloadDeterminingFilename,
                 false,
@@ -1534,7 +1664,7 @@
     return;
   }
   scoped_ptr<base::DictionaryValue> json_item(
-      DownloadItemToJSON(download_item, profile_->IsOffTheRecord()));
+      DownloadItemToJSON(download_item, profile_));
   DispatchEvent(events::kOnDownloadCreated,
                 true,
                 extensions::Event::WillDispatchCallback(),
@@ -1565,7 +1695,7 @@
         scoped_ptr<base::DictionaryValue>(new base::DictionaryValue()));
   }
   scoped_ptr<base::DictionaryValue> new_json(DownloadItemToJSON(
-      download_item, profile_->IsOffTheRecord()));
+      download_item, profile_));
   scoped_ptr<base::DictionaryValue> delta(new base::DictionaryValue());
   delta->SetInteger(kIdKey, download_item->GetId());
   std::set<std::string> new_fields;
@@ -1661,3 +1791,20 @@
       content_source,
       content::Details<std::string>(&json_args));
 }
+
+void ExtensionDownloadsEventRouter::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  switch (type) {
+    case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
+      extensions::UnloadedExtensionInfo* unloaded =
+          content::Details<extensions::UnloadedExtensionInfo>(details).ptr();
+      std::set<const extensions::Extension*>::iterator iter =
+        shelf_disabling_extensions_.find(unloaded->extension);
+      if (iter != shelf_disabling_extensions_.end())
+        shelf_disabling_extensions_.erase(iter);
+      break;
+    }
+  }
+}
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.h b/chrome/browser/extensions/api/downloads/downloads_api.h
index 465148c..16575f8 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api.h
+++ b/chrome/browser/extensions/api/downloads/downloads_api.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_API_DOWNLOADS_DOWNLOADS_API_H_
 #define CHROME_BROWSER_EXTENSIONS_API_DOWNLOADS_DOWNLOADS_API_H_
 
+#include <set>
 #include <string>
 
 #include "base/files/file_path.h"
@@ -19,6 +20,8 @@
 #include "chrome/common/extensions/api/downloads.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_manager.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
 
 class DownloadFileIconExtractor;
 class DownloadQuery;
@@ -37,6 +40,7 @@
 // Errors that can be returned through chrome.runtime.lastError.message.
 extern const char kEmptyFile[];
 extern const char kFileAlreadyDeleted[];
+extern const char kHostPermission[];
 extern const char kIconNotFound[];
 extern const char kInvalidDangerType[];
 extern const char kInvalidFilename[];
@@ -53,12 +57,34 @@
 extern const char kNotInProgress[];
 extern const char kNotResumable[];
 extern const char kOpenPermission[];
+extern const char kShelfDisabled[];
+extern const char kShelfPermission[];
 extern const char kTooManyListeners[];
 extern const char kUnexpectedDeterminer[];
 
 }  // namespace download_extension_errors
 
 
+class DownloadedByExtension : public base::SupportsUserData::Data {
+ public:
+  static DownloadedByExtension* Get(content::DownloadItem* item);
+
+  DownloadedByExtension(content::DownloadItem* item,
+                        const std::string& id,
+                        const std::string& name);
+
+  const std::string& id() const { return id_; }
+  const std::string& name() const { return name_; }
+
+ private:
+  static const char kKey[];
+
+  std::string id_;
+  std::string name_;
+
+  DISALLOW_COPY_AND_ASSIGN(DownloadedByExtension);
+};
+
 class DownloadsDownloadFunction : public AsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("downloads.download", DOWNLOADS_DOWNLOAD)
@@ -218,6 +244,20 @@
   DISALLOW_COPY_AND_ASSIGN(DownloadsOpenFunction);
 };
 
+class DownloadsSetShelfEnabledFunction : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("downloads.setShelfEnabled",
+                             DOWNLOADS_SETSHELFENABLED)
+  DownloadsSetShelfEnabledFunction();
+  virtual bool RunImpl() OVERRIDE;
+
+ protected:
+  virtual ~DownloadsSetShelfEnabledFunction();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DownloadsSetShelfEnabledFunction);
+};
+
 class DownloadsDragFunction : public AsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("downloads.drag", DOWNLOADS_DRAG)
@@ -251,6 +291,7 @@
 // Observes a single DownloadManager and many DownloadItems and dispatches
 // onCreated and onErased events.
 class ExtensionDownloadsEventRouter : public extensions::EventRouter::Observer,
+                                      public content::NotificationObserver,
                                       public AllDownloadItemNotifier::Observer {
  public:
   typedef base::Callback<void(
@@ -277,6 +318,9 @@
       Profile* profile, content::DownloadManager* manager);
   virtual ~ExtensionDownloadsEventRouter();
 
+  void SetShelfEnabled(const extensions::Extension* extension, bool enabled);
+  bool IsShelfEnabled() const;
+
   // Called by ChromeDownloadManagerDelegate during the filename determination
   // process, allows extensions to change the item's target filename. If no
   // extension wants to change the target filename, then |no_change| will be
@@ -318,8 +362,15 @@
       const extensions::Event::WillDispatchCallback& will_dispatch_callback,
       base::Value* json_arg);
 
+  // content::NotificationObserver
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
   Profile* profile_;
   AllDownloadItemNotifier notifier_;
+  std::set<const extensions::Extension*> shelf_disabling_extensions_;
+  content::NotificationRegistrar registrar_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionDownloadsEventRouter);
 };
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index 316498b..c398541 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/prefs/pref_service.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/download_file_icon_extractor.h"
 #include "chrome/browser/download/download_service.h"
@@ -47,12 +48,9 @@
 #include "net/url_request/url_request_job.h"
 #include "net/url_request/url_request_job_factory.h"
 #include "net/url_request/url_request_job_factory_impl.h"
-#include "webkit/browser/blob/blob_storage_controller.h"
-#include "webkit/browser/blob/blob_url_request_job.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_operation_runner.h"
 #include "webkit/browser/fileapi/file_system_url.h"
-#include "webkit/common/blob/blob_data.h"
 
 using content::BrowserContext;
 using content::BrowserThread;
@@ -527,8 +525,12 @@
                    const std::string& args) {
     scoped_refptr<UIThreadExtensionFunction> delete_function(function);
     SetUpExtensionFunction(function);
-    return extension_function_test_utils::RunFunction(
+    bool result = extension_function_test_utils::RunFunction(
         function, args, browser(), GetFlags());
+    if (!result) {
+      LOG(ERROR) << function->GetError();
+    }
+    return result;
   }
 
   extension_function_test_utils::RunFunctionFlags GetFlags() {
@@ -691,203 +693,59 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedItemVectorCanceller);
 };
 
-class TestProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
- public:
-  explicit TestProtocolHandler(
-      webkit_blob::BlobStorageController* blob_storage_controller,
-      fileapi::FileSystemContext* file_system_context)
-      : blob_storage_controller_(blob_storage_controller),
-        file_system_context_(file_system_context) {}
-
-  virtual ~TestProtocolHandler() {}
-
-  virtual net::URLRequestJob* MaybeCreateJob(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const OVERRIDE {
-    return new webkit_blob::BlobURLRequestJob(
-        request,
-        network_delegate,
-        blob_storage_controller_->GetBlobDataFromUrl(request->url()),
-        file_system_context_,
-        base::MessageLoopProxy::current().get());
-  }
-
- private:
-  webkit_blob::BlobStorageController* const blob_storage_controller_;
-  fileapi::FileSystemContext* const file_system_context_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestProtocolHandler);
-};
-
-class TestURLRequestContext : public net::URLRequestContext {
- public:
-  explicit TestURLRequestContext(
-      fileapi::FileSystemContext* file_system_context)
-      : blob_storage_controller_(new webkit_blob::BlobStorageController) {
-    // Job factory owns the protocol handler.
-    job_factory_.SetProtocolHandler(
-        "blob", new TestProtocolHandler(blob_storage_controller_.get(),
-                                        file_system_context));
-    set_job_factory(&job_factory_);
-  }
-
-  virtual ~TestURLRequestContext() {}
-
-  webkit_blob::BlobStorageController* blob_storage_controller() const {
-    return blob_storage_controller_.get();
-  }
-
- private:
-  net::URLRequestJobFactoryImpl job_factory_;
-  scoped_ptr<webkit_blob::BlobStorageController> blob_storage_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestURLRequestContext);
-};
-
 // Writes an HTML5 file so that it can be downloaded.
 class HTML5FileWriter {
  public:
-  HTML5FileWriter(
-      Profile* profile,
-      const std::string& filename,
-      const std::string& origin,
-      DownloadsEventsListener* events_listener,
-      const std::string& payload)
-    : profile_(profile),
-      filename_(filename),
-      origin_(origin),
-      events_listener_(events_listener),
-      blob_data_(new webkit_blob::BlobData()),
-      payload_(payload),
-      fs_(BrowserContext::GetDefaultStoragePartition(profile_)->
-          GetFileSystemContext()) {
-    CHECK(profile_);
-    CHECK(events_listener_);
-    CHECK(fs_);
-  }
-
-  ~HTML5FileWriter() {
-    CHECK(BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
-        &HTML5FileWriter::TearDownURLRequestContext, base::Unretained(this))));
-    events_listener_->WaitFor(
-        profile_, kURLRequestContextToreDown, std::string());
-  }
-
-  bool WriteFile() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-    fs_->OpenFileSystem(
-        GURL(origin_),
-        fileapi::kFileSystemTypeTemporary,
-        fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
-        base::Bind(&HTML5FileWriter::OpenFileSystemCallback,
-                   base::Unretained(this)));
-    return events_listener_->WaitFor(profile_, kHTML5FileWritten, filename_);
+  static bool CreateFileForTesting(fileapi::FileSystemContext* context,
+                                   const fileapi::FileSystemURL& path,
+                                   const char*data,
+                                   int length) {
+    // Create a temp file.
+    base::FilePath temp_file;
+    if (!file_util::CreateTemporaryFile(&temp_file) ||
+        file_util::WriteFile(temp_file, data, length) != length) {
+      return false;
+    }
+    // Invoke the fileapi to copy it into the sandboxed filesystem.
+    bool result = false;
+    base::WaitableEvent done_event(true, false);
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::Bind(&CreateFileForTestingOnIOThread,
+                   base::Unretained(context),
+                   path, temp_file,
+                   base::Unretained(&result),
+                   base::Unretained(&done_event)));
+    // Wait for that to finish.
+    done_event.Wait();
+    base::DeleteFile(temp_file, false);
+    return result;
   }
 
  private:
-  static const char kHTML5FileWritten[];
-  static const char kURLRequestContextToreDown[];
-  static const bool kExclusive = true;
-
-  GURL blob_url() const { return GURL("blob:" + filename_); }
-
-  void OpenFileSystemCallback(
-      base::PlatformFileError result,
-      const std::string& fs_name,
-      const GURL& root) {
-    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-    root_ = root.spec();
-    CHECK(BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
-        &HTML5FileWriter::CreateFile, base::Unretained(this))));
+  static void CopyInCompletion(bool* result,
+                               base::WaitableEvent* done_event,
+                               base::PlatformFileError error) {
+    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+    *result = error == base::PLATFORM_FILE_OK;
+    done_event->Signal();
   }
 
-  fileapi::FileSystemOperationRunner* operation_runner() {
-    return fs_->operation_runner();
+  static void CreateFileForTestingOnIOThread(
+      fileapi::FileSystemContext* context,
+      const fileapi::FileSystemURL& path,
+      const base::FilePath& temp_file,
+      bool* result,
+      base::WaitableEvent* done_event) {
+    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+    context->operation_runner()->CopyInForeignFile(
+        temp_file, path,
+        base::Bind(&CopyInCompletion,
+                   base::Unretained(result),
+                   base::Unretained(done_event)));
   }
-
-  void CreateFile() {
-    CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    operation_runner()->CreateFile(fs_->CrackURL(GURL(root_ + filename_)),
-        kExclusive, base::Bind(
-            &HTML5FileWriter::CreateFileCallback, base::Unretained(this)));
-  }
-
-  void CreateFileCallback(base::PlatformFileError result) {
-    CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    CHECK_EQ(base::PLATFORM_FILE_OK, result);
-    blob_data_->AppendData(payload_);
-    url_request_context_.reset(new TestURLRequestContext(fs_));
-    url_request_context_->blob_storage_controller()
-        ->AddFinishedBlob(blob_url(), blob_data_.get());
-    operation_runner()->Write(
-        url_request_context_.get(),
-        fs_->CrackURL(GURL(root_ + filename_)),
-        blob_url(),
-        0,  // offset
-        base::Bind(&HTML5FileWriter::WriteCallback, base::Unretained(this)));
-  }
-
-  void WriteCallback(
-      base::PlatformFileError result,
-      int64 bytes,
-      bool complete) {
-    CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    CHECK_EQ(base::PLATFORM_FILE_OK, result);
-    CHECK(BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
-        &HTML5FileWriter::NotifyWritten, base::Unretained(this))));
-  }
-
-  void NotifyWritten() {
-    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-    DownloadsEventsListener::DownloadsNotificationSource notification_source;
-    notification_source.event_name = kHTML5FileWritten;
-    notification_source.profile = profile_;
-    content::NotificationService::current()->Notify(
-        chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT,
-        content::Source<DownloadsEventsListener::DownloadsNotificationSource>(
-          &notification_source),
-        content::Details<std::string>(&filename_));
-  }
-
-  void TearDownURLRequestContext() {
-    CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    url_request_context_->blob_storage_controller()->RemoveBlob(blob_url());
-    url_request_context_.reset();
-    CHECK(BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
-        &HTML5FileWriter::NotifyURLRequestContextToreDown,
-        base::Unretained(this))));
-  }
-
-  void NotifyURLRequestContextToreDown() {
-    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-    DownloadsEventsListener::DownloadsNotificationSource notification_source;
-    notification_source.event_name = kURLRequestContextToreDown;
-    notification_source.profile = profile_;
-    std::string empty_args;
-    content::NotificationService::current()->Notify(
-        chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT,
-        content::Source<DownloadsEventsListener::DownloadsNotificationSource>(
-          &notification_source),
-        content::Details<std::string>(&empty_args));
-  }
-
-  Profile* profile_;
-  std::string filename_;
-  std::string origin_;
-  std::string root_;
-  DownloadsEventsListener* events_listener_;
-  scoped_refptr<webkit_blob::BlobData> blob_data_;
-  std::string payload_;
-  scoped_ptr<TestURLRequestContext> url_request_context_;
-  fileapi::FileSystemContext* fs_;
-
-  DISALLOW_COPY_AND_ASSIGN(HTML5FileWriter);
 };
 
-const char HTML5FileWriter::kHTML5FileWritten[] = "html5_file_written";
-const char HTML5FileWriter::kURLRequestContextToreDown[] =
-  "url_request_context_tore_down";
-
 // TODO(benjhayden) Merge this with the other TestObservers.
 class JustInProgressDownloadObserver
     : public content::DownloadTestObserverInProgress {
@@ -1804,15 +1662,19 @@
       << kInvalidURLs[index];
   }
 
-  EXPECT_STREQ("net::ERR_ACCESS_DENIED", RunFunctionAndReturnError(
-      new DownloadsDownloadFunction(),
-      "[{\"url\": \"javascript:document.write(\\\"hello\\\");\"}]").c_str());
-  EXPECT_STREQ("net::ERR_ACCESS_DENIED", RunFunctionAndReturnError(
-      new DownloadsDownloadFunction(),
-      "[{\"url\": \"javascript:return false;\"}]").c_str());
-  EXPECT_STREQ("net::ERR_NOT_IMPLEMENTED", RunFunctionAndReturnError(
-      new DownloadsDownloadFunction(),
-      "[{\"url\": \"ftp://example.com/example.txt\"}]").c_str());
+  static const char* kDisallowedHost[] = {
+    "javascript:document.write(\\\"hello\\\");",
+    "javascript:return false;",
+    "ftp://example.com/example.txt",
+  };
+
+  for (size_t index = 0; index < arraysize(kDisallowedHost); ++index) {
+    EXPECT_STREQ(errors::kHostPermission,
+                  RunFunctionAndReturnError(new DownloadsDownloadFunction(),
+                                            base::StringPrintf(
+        "[{\"url\": \"%s\"}]", kDisallowedHost[index])).c_str())
+      << kDisallowedHost[index];
+  }
 }
 
 // TODO(benjhayden): Set up a test ftp server, add ftp://localhost* to
@@ -2295,16 +2157,18 @@
   static const char* kPayloadData = "on the record\ndata";
   GoOnTheRecord();
   LoadExtension("downloads_split");
-  HTML5FileWriter html5_file_writer(
-      browser()->profile(),
-      "on_record.txt",
-      GetExtensionURL(),
-      events_listener(),
-      kPayloadData);
-  ASSERT_TRUE(html5_file_writer.WriteFile());
 
-  std::string download_url = "filesystem:" + GetExtensionURL() +
+  const std::string download_url = "filesystem:" + GetExtensionURL() +
     "temporary/on_record.txt";
+
+  // Setup a file in the filesystem which we can download.
+  ASSERT_TRUE(HTML5FileWriter::CreateFileForTesting(
+      BrowserContext::GetDefaultStoragePartition(browser()->profile())->
+          GetFileSystemContext(),
+      fileapi::FileSystemURL::CreateForTest(GURL(download_url)),
+      kPayloadData, strlen(kPayloadData)));
+
+  // Now download it.
   scoped_ptr<base::Value> result(RunFunctionAndReturnResult(
       new DownloadsDownloadFunction(), base::StringPrintf(
           "[{\"url\": \"%s\"}]", download_url.c_str())));
@@ -3588,6 +3452,23 @@
                          item->GetId())));
 }
 
+IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
+                       DownloadExtensionTest_SetShelfEnabled) {
+  LoadExtension("downloads_split");
+  EXPECT_TRUE(RunFunction(new DownloadsSetShelfEnabledFunction(), "[false]"));
+  EXPECT_FALSE(DownloadServiceFactory::GetForBrowserContext(
+      browser()->profile())->IsShelfEnabled());
+  EXPECT_TRUE(RunFunction(new DownloadsSetShelfEnabledFunction(), "[true]"));
+  EXPECT_TRUE(DownloadServiceFactory::GetForBrowserContext(
+      browser()->profile())->IsShelfEnabled());
+  // TODO(benjhayden) Test that existing shelves are hidden.
+  // TODO(benjhayden) Test multiple extensions.
+  // TODO(benjhayden) Test disabling extensions.
+  // TODO(benjhayden) Test that browsers associated with other profiles are not
+  // affected.
+  // TODO(benjhayden) Test incognito.
+}
+
 // TODO(benjhayden) Figure out why DisableExtension() does not fire
 // OnListenerRemoved.
 
@@ -3607,7 +3488,6 @@
   ASSERT_TRUE(RunExtensionTest("downloads")) << message_;
 }
 
-
 TEST(DownloadInterruptReasonEnumsSynced,
      DownloadInterruptReasonEnumsSynced) {
 #define INTERRUPT_REASON(name, value) \
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc b/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc
index b24a308..4286c1e 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc
+++ b/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc
@@ -6,6 +6,7 @@
 
 #include "base/lazy_instance.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/api/feedback_private/feedback_service.h"
@@ -17,7 +18,6 @@
 #include "ui/webui/web_ui_util.h"
 #include "url/url_util.h"
 
-
 namespace extensions {
 
 using api::feedback_private::SystemInformation;
@@ -49,12 +49,21 @@
 void FeedbackPrivateAPI::RequestFeedback(
     const std::string& description_template,
     const std::string& category_tag,
-    const GURL& page_url) {
+    const GURL& page_url,
+    const gfx::Rect& screen_size) {
   if (profile_ && ExtensionSystem::Get(profile_)->event_router()) {
     FeedbackInfo info;
     info.description = description_template;
     info.category_tag = make_scoped_ptr(new std::string(category_tag));
     info.page_url = make_scoped_ptr(new std::string(page_url.spec()));
+    info.system_information.reset(new SystemInformationList);
+
+    FeedbackService::PopulateSystemInfo(
+        info.system_information.get(), feedback_util::kScreensizeHeightKey,
+        base::IntToString(screen_size.height()));
+    FeedbackService::PopulateSystemInfo(
+        info.system_information.get(), feedback_util::kScreensizeWidthKey,
+        base::IntToString(screen_size.width()));
 
     scoped_ptr<base::ListValue> args(new base::ListValue());
     args->Append(info.ToValue().release());
@@ -66,6 +75,31 @@
   }
 }
 
+bool FeedbackPrivateGetStringsFunction::RunImpl() {
+  DictionaryValue* dict = new DictionaryValue();
+  SetResult(dict);
+
+#define SET_STRING(id, idr) \
+  dict->SetString(id, l10n_util::GetStringUTF16(idr))
+  SET_STRING("page-title", IDS_FEEDBACK_REPORT_PAGE_TITLE);
+  SET_STRING("page-url", IDS_FEEDBACK_REPORT_URL_LABEL);
+  SET_STRING("screenshot", IDS_FEEDBACK_SCREENSHOT_LABEL);
+  SET_STRING("user-email", IDS_FEEDBACK_USER_EMAIL_LABEL);
+  SET_STRING("sysinfo", IDS_FEEDBACK_INCLUDE_SYSTEM_INFORMATION_CHKBOX);
+  SET_STRING("attach-file-label", IDS_FEEDBACK_ATTACH_FILE_LABEL);
+  SET_STRING("attach-file-note", IDS_FEEDBACK_ATTACH_FILE_NOTE);
+  SET_STRING("attach-file-to-big", IDS_FEEDBACK_ATTACH_FILE_TO_BIG);
+  SET_STRING("reading-file", IDS_FEEDBACK_READING_FILE);
+  SET_STRING("send-report", IDS_FEEDBACK_SEND_REPORT);
+  SET_STRING("cancel", IDS_CANCEL);
+  SET_STRING("no-description", IDS_FEEDBACK_NO_DESCRIPTION);
+  SET_STRING("privacy-note", IDS_FEEDBACK_PRIVACY_NOTE);
+#undef SET_STRING
+
+  webui::SetFontAndTextDirection(dict);
+  return true;
+}
+
 bool FeedbackPrivateGetUserEmailFunction::RunImpl() {
   FeedbackService* service =
       FeedbackPrivateAPI::GetFactoryInstance()->GetForProfile(
@@ -100,19 +134,14 @@
 
   const FeedbackInfo &feedback_info = params->feedback;
 
-  std::string description = feedback_info.description;
   std::string attached_file_url, screenshot_url;
-  if (feedback_info.attached_file.get() &&
-      feedback_info.attached_file_blob_url.get() &&
-      !feedback_info.attached_file_blob_url->empty()) {
+  if (feedback_info.attached_file_blob_url.get() &&
+      !feedback_info.attached_file_blob_url->empty())
     attached_file_url = *feedback_info.attached_file_blob_url;
-  }
 
-  if (feedback_info.screenshot.get() &&
-      feedback_info.screenshot_blob_url.get() &&
-      !feedback_info.screenshot_blob_url->empty()) {
+  if (feedback_info.screenshot_blob_url.get() &&
+      !feedback_info.screenshot_blob_url->empty())
     screenshot_url = *feedback_info.screenshot_blob_url;
-  }
 
   // Populate feedback data.
   scoped_refptr<FeedbackData> feedback_data(new FeedbackData());
@@ -135,11 +164,8 @@
   if (!screenshot_url.empty())
     feedback_data->set_screenshot_url(GURL(screenshot_url));
 
-  // TODO(rkc): Take this out of OS_CHROMEOS once we have FeedbackData and
-  // FeedbackUtil migrated to handle system logs for both Chrome and ChromeOS.
-#if defined(OS_CHROMEOS)
-  scoped_ptr<chromeos::SystemLogsResponse> sys_logs(
-      new chromeos::SystemLogsResponse);
+  scoped_ptr<feedback_util::SystemLogsMap> sys_logs(
+      new feedback_util::SystemLogsMap);
   SystemInformationList* sys_info = feedback_info.system_information.get();
   if (sys_info) {
     for (SystemInformationList::iterator it = sys_info->begin();
@@ -147,7 +173,6 @@
       (*sys_logs.get())[it->get()->key] = it->get()->value;
   }
   feedback_data->set_sys_info(sys_logs.Pass());
-#endif
 
   FeedbackService* service = FeedbackPrivateAPI::GetFactoryInstance()->
       GetForProfile(profile())->GetService();
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_private_api.h b/chrome/browser/extensions/api/feedback_private/feedback_private_api.h
index 3bed626..fdc880c 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_private_api.h
+++ b/chrome/browser/extensions/api/feedback_private/feedback_private_api.h
@@ -22,7 +22,8 @@
   FeedbackService* GetService() const;
   void RequestFeedback(const std::string& description_template,
                        const std::string& category_tag,
-                       const GURL& page_url);
+                       const GURL& page_url,
+                       const gfx::Rect& screen_size);
 
   // ProfileKeyedAPI implementation.
   static ProfileKeyedAPIFactory<FeedbackPrivateAPI>* GetFactoryInstance();
@@ -39,6 +40,19 @@
   FeedbackService* service_;
 };
 
+// Feedback strings.
+class FeedbackPrivateGetStringsFunction : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("feedbackPrivate.getStrings",
+                             FEEDBACKPRIVATE_GETSTRINGS)
+
+ protected:
+  virtual ~FeedbackPrivateGetStringsFunction() {}
+
+  // SyncExtensionFunction overrides.
+  virtual bool RunImpl() OVERRIDE;
+};
+
 class FeedbackPrivateGetUserEmailFunction : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("feedbackPrivate.getUserEmail",
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_private_apitest.cc b/chrome/browser/extensions/api/feedback_private/feedback_private_apitest.cc
new file mode 100644
index 0000000..322601d
--- /dev/null
+++ b/chrome/browser/extensions/api/feedback_private/feedback_private_apitest.cc
@@ -0,0 +1,26 @@
+// Copyright 2013 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.
+
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "chrome/browser/extensions/api/audio/audio_api.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_test_message_listener.h"
+#include "chrome/common/chrome_switches.h"
+
+namespace extensions {
+
+class AudioApiTest: public ExtensionApiTest {
+ public:
+  AudioApiTest() {}
+  virtual ~AudioApiTest() {}
+};
+
+#if defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(AudioApiTest, Audio) {
+  EXPECT_TRUE(RunExtensionTest("audio")) << message_;
+}
+#endif
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_service.cc b/chrome/browser/extensions/api/feedback_private/feedback_service.cc
index ea207a0..c3a2278 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_service.cc
+++ b/chrome/browser/extensions/api/feedback_private/feedback_service.cc
@@ -8,23 +8,27 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/screenshot_source.h"
 #include "content/public/browser/browser_thread.h"
 
 using content::BrowserThread;
 
-namespace {
-
-ScreenshotDataPtr ConvertStringToScreenshotPtr(scoped_ptr<std::string> image) {
-  ScreenshotDataPtr screenshot(new ScreenshotData);
-  std::copy(image->begin(), image->end(), screenshot->begin());
-  return screenshot;
-}
-
-}
-
 namespace extensions {
 
+// static
+void FeedbackService::PopulateSystemInfo(
+    SystemInformationList* sys_info_list,
+    const std::string& key,
+    const std::string& value) {
+  base::DictionaryValue sys_info_value;
+  sys_info_value.Set("key", new base::StringValue(key));
+  sys_info_value.Set("value", new base::StringValue(value));
+
+  linked_ptr<SystemInformation> sys_info(new SystemInformation());
+  SystemInformation::Populate(sys_info_value, sys_info.get());
+
+  sys_info_list->push_back(sys_info);
+}
+
 FeedbackService::FeedbackService() {
 }
 
@@ -47,7 +51,7 @@
   }
 
   if (feedback_data_->screenshot_url().is_valid()) {
-    attached_file_reader_ = new BlobReader(
+    screenshot_reader_ = new BlobReader(
         profile, feedback_data_->screenshot_url(),
         base::Bind(&FeedbackService::ScreenshotCallback,
                    GetWeakPtr()));
@@ -70,25 +74,30 @@
   if (!data.get())
     feedback_data_->set_screenshot_url(GURL());
   else
-    feedback_data_->set_image(ConvertStringToScreenshotPtr(data.Pass()));
+    feedback_data_->set_image(data.Pass());
 
   CompleteSendFeedback();
 }
 
 void FeedbackService::CompleteSendFeedback() {
-  // If either the blob URL is invalid (we never needed to read it), or if the
-  // data exists in the feedback object (the read is completed).
+  // A particular data collection is considered completed if,
+  // a.) The blob URL is invalid - this will either happen because we never had
+  //     a URL and never needed to read this data, or that the data read failed
+  //     and we set it to invalid in the data read callback.
+  // b.) The associated data object exists, meaning that the data has been read
+  //     and the read callback has updated the associated data on the feedback
+  //     object.
   bool attached_file_completed =
       !feedback_data_->attached_file_url().is_valid() ||
-      feedback_data_->attached_filedata();
+      !feedback_data_->attached_filedata();
   bool screenshot_completed =
       !feedback_data_->screenshot_url().is_valid() ||
-      !feedback_data_->image().get();
+      !feedback_data_->image();
 
   if (screenshot_completed && attached_file_completed) {
     // Signal the feedback object that the data from the feedback page has been
     // filled - the object will manage sending of the actual report.
-    feedback_data_->FeedbackPageDataComplete();
+    feedback_data_->OnFeedbackPageDataComplete();
     // TODO(rkc): Change this once we have FeedbackData/Util refactored to
     // report the status of the report being sent.
     send_feedback_callback_.Run(true);
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_service.h b/chrome/browser/extensions/api/feedback_private/feedback_service.h
index ba64353..db202f6 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_service.h
+++ b/chrome/browser/extensions/api/feedback_private/feedback_service.h
@@ -16,10 +16,11 @@
 
 class Profile;
 
+using extensions::api::feedback_private::SystemInformation;
+
 namespace extensions {
 
-typedef std::vector<linked_ptr<api::feedback_private::SystemInformation> >
-    SystemInformationList;
+typedef std::vector<linked_ptr<SystemInformation> > SystemInformationList;
 
 class FeedbackService {
  public:
@@ -29,6 +30,11 @@
 
   // Creates a platform-specific FeedbackService instance.
   static FeedbackService* CreateInstance();
+  // Convenience method for populating a SystemInformationList structure
+  // with a key/value pair.
+  static void PopulateSystemInfo(SystemInformationList* sys_info_list,
+                                 const std::string& key,
+                                 const std::string& value);
 
   virtual ~FeedbackService();
 
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_service_chromeos.cc b/chrome/browser/extensions/api/feedback_private/feedback_service_chromeos.cc
index ba3e1f2..9397fa7 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_service_chromeos.cc
+++ b/chrome/browser/extensions/api/feedback_private/feedback_service_chromeos.cc
@@ -69,16 +69,8 @@
   }
 
   for (chromeos::SystemLogsResponse::iterator it = sys_info_map->begin();
-       it != sys_info_map->end(); ++it) {
-    base::DictionaryValue sys_info_value;
-    sys_info_value.Set("key", new base::StringValue(it->first));
-    sys_info_value.Set("value", new base::StringValue(it->second));
-
-    linked_ptr<SystemInformation> sys_info(new SystemInformation());
-    SystemInformation::Populate(sys_info_value, sys_info.get());
-
-    sys_info_list.push_back(sys_info);
-  }
+       it != sys_info_map->end(); ++it)
+    FeedbackService::PopulateSystemInfo(&sys_info_list, it->first, it->second);
 
   system_information_callback_.Run(sys_info_list);
 }
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_service_nonchromeos.cc b/chrome/browser/extensions/api/feedback_private/feedback_service_nonchromeos.cc
index 9c05c30..da6cce3 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_service_nonchromeos.cc
+++ b/chrome/browser/extensions/api/feedback_private/feedback_service_nonchromeos.cc
@@ -60,6 +60,8 @@
   system_information_callback_ = callback;
 
   SystemInformationList sys_info_list;
+  // TODO(rkc): Figure out what other Chrome system information we can add and
+  // add it here.
   system_information_callback_.Run(sys_info_list);
 }
 
diff --git a/chrome/browser/extensions/api/file_system/file_system_api.cc b/chrome/browser/extensions/api/file_system/file_system_api.cc
index c02d5e01..e347fcf 100644
--- a/chrome/browser/extensions/api/file_system/file_system_api.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_api.cc
@@ -12,6 +12,7 @@
 #include "base/logging.h"
 #include "base/path_service.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/value_conversions.h"
@@ -60,10 +61,13 @@
 const char kInvalidCallingPage[] = "Invalid calling page. This function can't "
     "be called from a background page.";
 const char kUserCancelled[] = "User cancelled";
-const char kWritableFileError[] =
+const char kWritableFileRestrictedLocationError[] =
     "Cannot write to file in a restricted location";
+const char kWritableFileErrorFormat[] = "Error opening %s";
 const char kRequiresFileSystemWriteError[] =
     "Operation requires fileSystem.write permission";
+const char kMultipleUnsupportedError[] =
+    "acceptsMultiple: true is not supported for 'saveFile'";
 const char kUnknownIdError[] = "Unknown id";
 
 namespace file_system = extensions::api::file_system;
@@ -156,6 +160,7 @@
 bool g_skip_picker_for_test = false;
 bool g_use_suggested_path_for_test = false;
 base::FilePath* g_path_to_be_picked_for_test;
+std::vector<base::FilePath>* g_paths_to_be_picked_for_test;
 
 bool GetFileSystemAndPathOfFileEntry(
     const std::string& filesystem_name,
@@ -210,13 +215,19 @@
 }
 
 bool DoCheckWritableFile(const base::FilePath& path,
-                         const base::FilePath& extension_directory) {
+                         const base::FilePath& extension_directory,
+                         std::string* error_message) {
   // Don't allow links.
-  if (base::PathExists(path) && file_util::IsLink(path))
+  if (base::PathExists(path) && file_util::IsLink(path)) {
+    *error_message = base::StringPrintf(kWritableFileErrorFormat,
+                                        path.BaseName().AsUTF8Unsafe().c_str());
     return false;
+  }
 
-  if (extension_directory == path || extension_directory.IsParent(path))
+  if (extension_directory == path || extension_directory.IsParent(path)) {
+    *error_message = kWritableFileRestrictedLocationError;
     return false;
+  }
 
   bool is_whitelisted_path = false;
 
@@ -236,6 +247,7 @@
       base::FilePath blacklisted_path;
       if (PathService::Get(kBlacklistedPaths[i], &blacklisted_path) &&
           (blacklisted_path == path || blacklisted_path.IsParent(path))) {
+        *error_message = kWritableFileRestrictedLocationError;
         return false;
       }
     }
@@ -251,29 +263,120 @@
   // Close the file so we don't keep a lock open.
   if (file != base::kInvalidPlatformFileValue)
     base::ClosePlatformFile(file);
-  return error == base::PLATFORM_FILE_OK ||
-         error == base::PLATFORM_FILE_ERROR_EXISTS;
+  if (error != base::PLATFORM_FILE_OK &&
+      error != base::PLATFORM_FILE_ERROR_EXISTS) {
+    *error_message = base::StringPrintf(kWritableFileErrorFormat,
+                                        path.BaseName().AsUTF8Unsafe().c_str());
+    return false;
+  }
+
+  return true;
 }
 
-void CheckLocalWritableFile(const base::FilePath& path,
-                            const base::FilePath& extension_directory,
-                            const base::Closure& on_success,
-                            const base::Closure& on_failure) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
-  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
-      DoCheckWritableFile(path, extension_directory) ? on_success : on_failure);
-}
+// Checks whether a list of paths are all OK for writing and calls a provided
+// on_success or on_failure callback when done. A file is OK for writing if it
+// is not a symlink, is not in a blacklisted path and can be opened for writing;
+// files are created if they do not exist.
+class WritableFileChecker
+    : public base::RefCountedThreadSafe<WritableFileChecker> {
+ public:
+  WritableFileChecker(
+      const std::vector<base::FilePath>& paths,
+      Profile* profile,
+      const base::FilePath& extension_path,
+      const base::Closure& on_success,
+      const base::Callback<void(const std::string&)>& on_failure)
+      : outstanding_tasks_(1),
+        extension_path_(extension_path),
+        on_success_(on_success),
+        on_failure_(on_failure) {
+#if defined(OS_CHROMEOS)
+    if (drive::util::IsUnderDriveMountPoint(paths[0])) {
+      outstanding_tasks_ = paths.size();
+      for (std::vector<base::FilePath>::const_iterator it = paths.begin();
+           it != paths.end(); ++it) {
+        DCHECK(drive::util::IsUnderDriveMountPoint(*it));
+        drive::util::PrepareWritableFileAndRun(
+            profile,
+            *it,
+            base::Bind(&WritableFileChecker::CheckRemoteWritableFile, this));
+      }
+      return;
+    }
+#endif
+    content::BrowserThread::PostTask(
+        content::BrowserThread::FILE,
+        FROM_HERE,
+        base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this, paths));
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<WritableFileChecker>;
+  virtual ~WritableFileChecker() {}
+
+  // Called when a work item is completed. If all work items are done, this
+  // posts a task to run AllTasksDone on the UI thread.
+  void TaskDone() {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+    if (--outstanding_tasks_ == 0) {
+      content::BrowserThread::PostTask(
+          content::BrowserThread::UI,
+          FROM_HERE,
+          base::Bind(&WritableFileChecker::AllTasksDone, this));
+    }
+  }
+
+  // Called on the UI thread when all tasks are done.
+  void AllTasksDone() {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    if (error_.empty())
+      on_success_.Run();
+    else
+      on_failure_.Run(error_);
+  }
+
+  // Reports an error in completing a work item. This may be called more than
+  // once, but only the last message will be retained.
+  void Error(const std::string& message) {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+    DCHECK(!message.empty());
+    error_ = message;
+    TaskDone();
+  }
+
+  void CheckLocalWritableFiles(const std::vector<base::FilePath>& paths) {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+    std::string error;
+    for (std::vector<base::FilePath>::const_iterator it = paths.begin();
+         it != paths.end(); ++it) {
+      if (!DoCheckWritableFile(*it, extension_path_, &error)) {
+        Error(error);
+        return;
+      }
+    }
+    TaskDone();
+  }
 
 #if defined(OS_CHROMEOS)
-void CheckRemoteWritableFile(const base::Closure& on_success,
-                             const base::Closure& on_failure,
-                             drive::FileError error,
-                             const base::FilePath& path) {
-  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
-      error == drive::FILE_ERROR_OK ? on_success : on_failure);
-}
+  void CheckRemoteWritableFile(drive::FileError error,
+                               const base::FilePath& path) {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+    if (error == drive::FILE_ERROR_OK) {
+      TaskDone();
+    } else {
+      Error(base::StringPrintf(kWritableFileErrorFormat,
+                               path.BaseName().AsUTF8Unsafe().c_str()));
+    }
+  }
 #endif
 
+  int outstanding_tasks_;
+  const base::FilePath extension_path_;
+  std::string error_;
+  base::Closure on_success_;
+  base::Callback<void(const std::string&)> on_failure_;
+};
+
 // Expand the mime-types and extensions provided in an AcceptOption, returning
 // them within the passed extension vector. Returns false if no valid types
 // were found.
@@ -387,6 +490,11 @@
   return true;
 }
 
+FileSystemEntryFunction::FileSystemEntryFunction()
+    : multiple_(false),
+      entry_type_(READ_ONLY),
+      response_(NULL) {}
+
 bool FileSystemEntryFunction::HasFileSystemWritePermission() {
   const extensions::Extension* extension = GetExtension();
   if (!extension)
@@ -395,60 +503,68 @@
   return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
 }
 
-void FileSystemEntryFunction::CheckWritableFile(const base::FilePath& path) {
+void FileSystemEntryFunction::CheckWritableFiles(
+    const std::vector<base::FilePath>& paths) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  base::Closure on_success =
-      base::Bind(&FileSystemEntryFunction::RegisterFileSystemAndSendResponse,
-                 this, path, WRITABLE);
-  base::Closure on_failure =
-      base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this);
+  scoped_refptr<WritableFileChecker> helper = new WritableFileChecker(
+      paths, profile_, extension_->path(),
+      base::Bind(
+          &FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
+          this, paths),
+      base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this));
+}
 
-#if defined(OS_CHROMEOS)
-  if (drive::util::IsUnderDriveMountPoint(path)) {
-    drive::util::PrepareWritableFileAndRun(profile_, path,
-        base::Bind(&CheckRemoteWritableFile, on_success, on_failure));
-    return;
+void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse(
+    const std::vector<base::FilePath>& paths) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  CreateResponse();
+  for (std::vector<base::FilePath>::const_iterator it = paths.begin();
+       it != paths.end(); ++it) {
+    AddEntryToResponse(*it, "");
   }
-#endif
-  content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
-      base::Bind(&CheckLocalWritableFile, path, extension_->path(), on_success,
-                 on_failure));
-}
-
-void FileSystemEntryFunction::RegisterFileSystemAndSendResponse(
-    const base::FilePath& path, EntryType entry_type) {
-  RegisterFileSystemAndSendResponseWithIdOverride(path, entry_type, "");
-}
-
-void FileSystemEntryFunction::RegisterFileSystemAndSendResponseWithIdOverride(
-    const base::FilePath& path, EntryType entry_type, const std::string& id) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
-  fileapi::IsolatedContext* isolated_context =
-      fileapi::IsolatedContext::GetInstance();
-  DCHECK(isolated_context);
-
-  bool writable = entry_type == WRITABLE;
-  extensions::app_file_handler_util::GrantedFileEntry file_entry =
-      extensions::app_file_handler_util::CreateFileEntry(profile(),
-          GetExtension()->id(), render_view_host_->GetProcess()->GetID(), path,
-          writable);
-
-  base::DictionaryValue* dict = new base::DictionaryValue();
-  SetResult(dict);
-  dict->SetString("fileSystemId", file_entry.filesystem_id);
-  dict->SetString("baseName", file_entry.registered_name);
-  if (id.empty())
-    dict->SetString("id", file_entry.id);
-  else
-    dict->SetString("id", id);
-
   SendResponse(true);
 }
 
-void FileSystemEntryFunction::HandleWritableFileError() {
+void FileSystemEntryFunction::CreateResponse() {
+  DCHECK(!response_);
+  response_ = new base::DictionaryValue();
+  base::ListValue* list = new base::ListValue();
+  response_->Set("entries", list);
+  response_->SetBoolean("multiple", multiple_);
+  SetResult(response_);
+}
+
+void FileSystemEntryFunction::AddEntryToResponse(
+    const base::FilePath& path,
+    const std::string& id_override) {
+  DCHECK(response_);
+  bool writable = entry_type_ == WRITABLE;
+  extensions::app_file_handler_util::GrantedFileEntry file_entry =
+      extensions::app_file_handler_util::CreateFileEntry(
+          profile(),
+          GetExtension()->id(),
+          render_view_host_->GetProcess()->GetID(),
+          path,
+          writable);
+  base::ListValue* entries;
+  bool success = response_->GetList("entries", &entries);
+  DCHECK(success);
+
+  base::DictionaryValue* entry = new base::DictionaryValue();
+  entry->SetString("fileSystemId", file_entry.filesystem_id);
+  entry->SetString("baseName", file_entry.registered_name);
+  if (id_override.empty())
+    entry->SetString("id", file_entry.id);
+  else
+    entry->SetString("id", id_override);
+  entries->Append(entry);
+}
+
+void FileSystemEntryFunction::HandleWritableFileError(
+    const std::string& error) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  error_ = kWritableFileError;
+  error_ = error;
   SendResponse(false);
 }
 
@@ -462,13 +578,16 @@
     error_ = kRequiresFileSystemWriteError;
     return false;
   }
+  entry_type_ = WRITABLE;
 
   base::FilePath path;
   if (!GetFilePathOfFileEntry(filesystem_name, filesystem_path,
                               render_view_host_, &path, &error_))
     return false;
 
-  CheckWritableFile(path);
+  std::vector<base::FilePath> paths;
+  paths.push_back(path);
+  CheckWritableFiles(paths);
   return true;
 }
 
@@ -503,10 +622,8 @@
              content::WebContents* web_contents,
              const base::FilePath& suggested_name,
              const ui::SelectFileDialog::FileTypeInfo& file_type_info,
-             ui::SelectFileDialog::Type picker_type,
-             EntryType entry_type)
-      : entry_type_(entry_type),
-        function_(function) {
+             ui::SelectFileDialog::Type picker_type)
+      : function_(function) {
     select_file_dialog_ = ui::SelectFileDialog::Create(
         this, new ChromeSelectFilePolicy(web_contents));
     gfx::NativeWindow owning_window = web_contents ?
@@ -521,11 +638,21 @@
                 base::Unretained(this), suggested_name, 1,
                 static_cast<void*>(NULL)));
       } else if (g_path_to_be_picked_for_test) {
-        content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+        content::BrowserThread::PostTask(
+            content::BrowserThread::UI, FROM_HERE,
             base::Bind(
                 &FileSystemChooseEntryFunction::FilePicker::FileSelected,
                 base::Unretained(this), *g_path_to_be_picked_for_test, 1,
                 static_cast<void*>(NULL)));
+      } else if (g_paths_to_be_picked_for_test) {
+        content::BrowserThread::PostTask(
+            content::BrowserThread::UI,
+            FROM_HERE,
+            base::Bind(
+                &FileSystemChooseEntryFunction::FilePicker::MultiFilesSelected,
+                base::Unretained(this),
+                *g_paths_to_be_picked_for_test,
+                static_cast<void*>(NULL)));
       } else {
         content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
             base::Bind(
@@ -553,8 +680,9 @@
   virtual void FileSelected(const base::FilePath& path,
                             int index,
                             void* params) OVERRIDE {
-    function_->FileSelected(path, entry_type_);
-    delete this;
+    std::vector<base::FilePath> paths;
+    paths.push_back(path);
+    MultiFilesSelected(paths, params);
   }
 
   virtual void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file,
@@ -568,17 +696,31 @@
     //
     // TODO(kinaba): remove this, once after the file picker implements proper
     // switch of the path treatment depending on the |support_drive| flag.
-    function_->FileSelected(file.file_path, entry_type_);
+    FileSelected(file.file_path, index, params);
+  }
+
+  virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
+                                  void* params) OVERRIDE {
+    function_->FilesSelected(files);
     delete this;
   }
 
+  virtual void MultiFilesSelectedWithExtraInfo(
+      const std::vector<ui::SelectedFileInfo>& files,
+      void* params) OVERRIDE {
+    std::vector<base::FilePath> paths;
+    for (std::vector<ui::SelectedFileInfo>::const_iterator it = files.begin();
+         it != files.end(); ++it) {
+      paths.push_back(it->file_path);
+    }
+    MultiFilesSelected(paths, params);
+  }
+
   virtual void FileSelectionCanceled(void* params) OVERRIDE {
     function_->FileSelectionCanceled();
     delete this;
   }
 
-  EntryType entry_type_;
-
   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
   scoped_refptr<FileSystemChooseEntryFunction> function_;
 
@@ -587,8 +729,7 @@
 
 void FileSystemChooseEntryFunction::ShowPicker(
     const ui::SelectFileDialog::FileTypeInfo& file_type_info,
-    ui::SelectFileDialog::Type picker_type,
-    EntryType entry_type) {
+    ui::SelectFileDialog::Type picker_type) {
   // TODO(asargent/benwells) - As a short term remediation for crbug.com/179010
   // we're adding the ability for a whitelisted extension to use this API since
   // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd
@@ -613,8 +754,8 @@
   // its destruction (and subsequent sending of the function response) until the
   // user has selected a file or cancelled the picker. At that point, the picker
   // will delete itself, which will also free the function instance.
-  new FilePicker(this, web_contents, initial_path_, file_type_info,
-                 picker_type, entry_type);
+  new FilePicker(
+      this, web_contents, initial_path_, file_type_info, picker_type);
 }
 
 // static
@@ -623,6 +764,14 @@
   g_skip_picker_for_test = true;
   g_use_suggested_path_for_test = false;
   g_path_to_be_picked_for_test = path;
+  g_paths_to_be_picked_for_test = NULL;
+}
+
+void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
+    std::vector<base::FilePath>* paths) {
+  g_skip_picker_for_test = true;
+  g_use_suggested_path_for_test = false;
+  g_paths_to_be_picked_for_test = paths;
 }
 
 // static
@@ -630,6 +779,7 @@
   g_skip_picker_for_test = true;
   g_use_suggested_path_for_test = true;
   g_path_to_be_picked_for_test = NULL;
+  g_paths_to_be_picked_for_test = NULL;
 }
 
 // static
@@ -637,6 +787,7 @@
   g_skip_picker_for_test = true;
   g_use_suggested_path_for_test = false;
   g_path_to_be_picked_for_test = NULL;
+  g_paths_to_be_picked_for_test = NULL;
 }
 
 // static
@@ -670,19 +821,18 @@
   }
 }
 
-void FileSystemChooseEntryFunction::FileSelected(const base::FilePath& path,
-                                                 EntryType entry_type) {
+void FileSystemChooseEntryFunction::FilesSelected(
+    const std::vector<base::FilePath>& paths) {
+  DCHECK(!paths.empty());
   file_system_api::SetLastChooseEntryDirectory(
-      ExtensionPrefs::Get(profile()),
-      GetExtension()->id(),
-      path.DirName());
-  if (entry_type == WRITABLE) {
-    CheckWritableFile(path);
+      ExtensionPrefs::Get(profile()), GetExtension()->id(), paths[0].DirName());
+  if (entry_type_ == WRITABLE) {
+    CheckWritableFiles(paths);
     return;
   }
 
   // Don't need to check the file, it's for reading.
-  RegisterFileSystemAndSendResponse(path, READ_ONLY);
+  RegisterFileSystemsAndSendResponse(paths);
 }
 
 void FileSystemChooseEntryFunction::FileSelectionCanceled() {
@@ -756,16 +906,22 @@
 
   base::FilePath suggested_name;
   ui::SelectFileDialog::FileTypeInfo file_type_info;
-  EntryType entry_type = READ_ONLY;
   ui::SelectFileDialog::Type picker_type =
       ui::SelectFileDialog::SELECT_OPEN_FILE;
 
   file_system::ChooseEntryOptions* options = params->options.get();
   if (options) {
+    multiple_ = options->accepts_multiple;
+    if (multiple_)
+      picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
     if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE) {
-      entry_type = WRITABLE;
+      entry_type_ = WRITABLE;
     } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) {
-      entry_type = WRITABLE;
+      if (multiple_) {
+        error_ = kMultipleUnsupportedError;
+        return false;
+      }
+      entry_type_ = WRITABLE;
       picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
     }
 
@@ -777,7 +933,7 @@
         options->accepts.get(), options->accepts_all_types.get());
   }
 
-  if (entry_type == WRITABLE && !HasFileSystemWritePermission()) {
+  if (entry_type_ == WRITABLE && !HasFileSystemWritePermission()) {
     error_ = kRequiresFileSystemWriteError;
     return false;
   }
@@ -798,7 +954,7 @@
           suggested_name, previous_path),
       base::Bind(
           &FileSystemChooseEntryFunction::ShowPicker, this, file_type_info,
-          picker_type, entry_type));
+          picker_type));
   return true;
 }
 
@@ -868,13 +1024,11 @@
   // |needs_new_entry| will be false if the renderer already has an Entry for
   // |entry_id|.
   if (needs_new_entry) {
-    // Reuse the ID of the retained file entry so retainEntry returns the same
-    // ID that was passed to restoreEntry.
-    RegisterFileSystemAndSendResponseWithIdOverride(
-        file_entry->path,
-        file_entry->writable ? WRITABLE : READ_ONLY,
-        file_entry->id);
+    entry_type_ = file_entry->writable ? WRITABLE : READ_ONLY;
+    CreateResponse();
+    AddEntryToResponse(file_entry->path, file_entry->id);
   }
+  SendResponse(true);
   return true;
 }
 
diff --git a/chrome/browser/extensions/api/file_system/file_system_api.h b/chrome/browser/extensions/api/file_system/file_system_api.h
index 97b8dd1..cf46329 100644
--- a/chrome/browser/extensions/api/file_system/file_system_api.h
+++ b/chrome/browser/extensions/api/file_system/file_system_api.h
@@ -50,34 +50,42 @@
     WRITABLE
   };
 
+  FileSystemEntryFunction();
+
   virtual ~FileSystemEntryFunction() {}
 
   bool HasFileSystemWritePermission();
 
-  // This is called when a writable file entry is being returned. The function
-  // will ensure the file exists, creating it if necessary, and also check that
-  // the file is not a link. If it succeeds it proceeds to
-  // RegisterFileSystemAndSendResponse, otherwise to HandleWritableFileError.
-  void CheckWritableFile(const base::FilePath& path);
+  // This is called when writable file entries are being returned. The function
+  // will ensure the files exist, creating them if necessary, and also check
+  // that none of the files are links. If it succeeds it proceeds to
+  // RegisterFileSystemsAndSendResponse, otherwise to HandleWritableFileError.
+  void CheckWritableFiles(const std::vector<base::FilePath>& path);
 
   // This will finish the choose file process. This is either called directly
-  // from FileSelected, or from CreateFileIfNecessary. It is called on the UI
+  // from FilesSelected, or from WritableFileChecker. It is called on the UI
   // thread.
-  void RegisterFileSystemAndSendResponse(const base::FilePath& path,
-                                         EntryType entry_type);
+  void RegisterFileSystemsAndSendResponse(
+      const std::vector<base::FilePath>& path);
 
-  // This will finish the choose file process. This is either called directly
-  // from FileSelected, or from CreateFileIfNecessary. It is called on the UI
-  // thread. |id_override| specifies the id to send in the response instead of
-  // the generated id. This can be useful for creating a file entry with an id
-  // matching another file entry, e.g. for restoreEntry.
-  void RegisterFileSystemAndSendResponseWithIdOverride(
-      const base::FilePath& path,
-      EntryType entry_type,
-      const std::string& id_override);
+  // Creates a response dictionary and sets it as the response to be sent.
+  void CreateResponse();
+
+  // Adds an entry to the response dictionary.
+  void AddEntryToResponse(const base::FilePath& path,
+                          const std::string& id_override);
 
   // called on the UI thread if there is a problem checking a writable file.
-  void HandleWritableFileError();
+  void HandleWritableFileError(const std::string& error);
+
+  // Whether multiple entries have been requested.
+  bool multiple_;
+
+  // The type of the entry or entries to return.
+  EntryType entry_type_;
+
+  // The dictionary to send as the response.
+  base::DictionaryValue* response_;
 };
 
 class FileSystemGetWritableEntryFunction : public FileSystemEntryFunction {
@@ -104,6 +112,8 @@
  public:
   // Allow picker UI to be skipped in testing.
   static void SkipPickerAndAlwaysSelectPathForTest(base::FilePath* path);
+  static void SkipPickerAndAlwaysSelectPathsForTest(
+      std::vector<base::FilePath>* paths);
   static void SkipPickerAndSelectSuggestedPathForTest();
   static void SkipPickerAndAlwaysCancelForTest();
   static void StopSkippingPickerForTest();
@@ -133,15 +143,14 @@
   virtual ~FileSystemChooseEntryFunction() {}
   virtual bool RunImpl() OVERRIDE;
   void ShowPicker(const ui::SelectFileDialog::FileTypeInfo& file_type_info,
-                  ui::SelectFileDialog::Type picker_type,
-                  EntryType entry_type);
+                  ui::SelectFileDialog::Type picker_type);
 
  private:
   void SetInitialPathOnFileThread(const base::FilePath& suggested_name,
                                   const base::FilePath& previous_path);
 
-  // FileSelected and FileSelectionCanceled are called by the file picker.
-  void FileSelected(const base::FilePath& path, EntryType entry_type);
+  // FilesSelected and FileSelectionCanceled are called by the file picker.
+  void FilesSelected(const std::vector<base::FilePath>& path);
   void FileSelectionCanceled();
 
   base::FilePath initial_path_;
diff --git a/chrome/browser/extensions/api/file_system/file_system_apitest.cc b/chrome/browser/extensions/api/file_system/file_system_apitest.cc
index a08faad..9f1c709 100644
--- a/chrome/browser/extensions/api/file_system/file_system_apitest.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_apitest.cc
@@ -97,6 +97,30 @@
     return destination;
   }
 
+  std::vector<base::FilePath> TempFilePaths(
+      const std::vector<std::string>& destination_names,
+      bool copy_gold) {
+    if (!temp_dir_.CreateUniqueTempDir()) {
+      ADD_FAILURE() << "CreateUniqueTempDir failed";
+      return std::vector<base::FilePath>();
+    }
+    FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
+        "test_temp", temp_dir_.path());
+
+    std::vector<base::FilePath> result;
+    for (std::vector<std::string>::const_iterator it =
+             destination_names.begin();
+         it != destination_names.end(); ++it) {
+      base::FilePath destination = temp_dir_.path().AppendASCII(*it);
+      if (copy_gold) {
+        base::FilePath source = test_root_folder_.AppendASCII("gold.txt");
+        EXPECT_TRUE(base::CopyFile(source, destination));
+      }
+      result.push_back(destination);
+    }
+    return result;
+  }
+
   void CheckStoredDirectoryMatches(const base::FilePath& filename) {
     const Extension* extension = GetSingleLoadedExtension();
     ASSERT_TRUE(extension);
@@ -222,6 +246,31 @@
   CheckStoredDirectoryMatches(test_file);
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiOpenMultipleSuggested) {
+  base::FilePath test_file = TempFilePath("open_existing.txt", true);
+  ASSERT_FALSE(test_file.empty());
+  ASSERT_TRUE(PathService::OverrideAndCreateIfNeeded(
+      chrome::DIR_USER_DOCUMENTS, test_file.DirName(), false));
+  FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest();
+  ASSERT_TRUE(RunPlatformAppTest(
+      "api_test/file_system/open_multiple_with_suggested_name"))
+      << message_;
+  CheckStoredDirectoryMatches(test_file);
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
+                       FileSystemApiOpenMultipleExistingFilesTest) {
+  std::vector<std::string> names;
+  names.push_back("open_existing1.txt");
+  names.push_back("open_existing2.txt");
+  std::vector<base::FilePath> test_files = TempFilePaths(names, true);
+  ASSERT_EQ(2u, test_files.size());
+  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
+      &test_files);
+  ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/open_multiple_existing"))
+      << message_;
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
     FileSystemApiInvalidChooseEntryTypeTest) {
   base::FilePath test_file = TempFilePath("open_existing.txt", true);
@@ -272,6 +321,20 @@
   CheckStoredDirectoryMatches(test_file);
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
+                       FileSystemApiOpenMultipleWritableExistingFilesTest) {
+  std::vector<std::string> names;
+  names.push_back("open_existing1.txt");
+  names.push_back("open_existing2.txt");
+  std::vector<base::FilePath> test_files = TempFilePaths(names, true);
+  ASSERT_EQ(2u, test_files.size());
+  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
+      &test_files);
+  ASSERT_TRUE(RunPlatformAppTest(
+      "api_test/file_system/open_multiple_writable_existing_with_write"))
+      << message_;
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiOpenCancelTest) {
   FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest();
   ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/open_cancel"))
@@ -326,6 +389,18 @@
   CheckStoredDirectoryMatches(test_file);
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiSaveMultipleFilesTest) {
+  std::vector<std::string> names;
+  names.push_back("save1.txt");
+  names.push_back("save2.txt");
+  std::vector<base::FilePath> test_files = TempFilePaths(names, false);
+  ASSERT_EQ(2u, test_files.size());
+  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
+      &test_files);
+  ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/save_multiple"))
+      << message_;
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiSaveCancelTest) {
   FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest();
   ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/save_cancel"))
diff --git a/chrome/browser/extensions/api/history/history_apitest.cc b/chrome/browser/extensions/api/history/history_apitest.cc
index 2d8e5f0..3a4b886 100644
--- a/chrome/browser/extensions/api/history/history_apitest.cc
+++ b/chrome/browser/extensions/api/history/history_apitest.cc
@@ -12,8 +12,8 @@
 #include "chrome/browser/history/history_types.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "extensions/common/switches.h"
 #include "net/dns/mock_host_resolver.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/extensions/api/identity/experimental_identity_api.cc b/chrome/browser/extensions/api/identity/experimental_identity_api.cc
index 27c5051..2e01c4e 100644
--- a/chrome/browser/extensions/api/identity/experimental_identity_api.cc
+++ b/chrome/browser/extensions/api/identity/experimental_identity_api.cc
@@ -227,9 +227,25 @@
 }
 
 void ExperimentalIdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() {
-  login_token_request_ =
-      ProfileOAuth2TokenServiceFactory::GetForProfile(profile())->
-          StartRequest(OAuth2TokenService::ScopeSet(), this);
+  ProfileOAuth2TokenService* service =
+      ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
+#if defined(OS_CHROMEOS)
+  if (chrome::IsRunningInForcedAppMode()) {
+    std::string app_client_id;
+    std::string app_client_secret;
+    if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo(
+           &app_client_id, &app_client_secret)) {
+      login_token_request_ =
+          service->StartRequestForClient(app_client_id,
+                                         app_client_secret,
+                                         OAuth2TokenService::ScopeSet(),
+                                         this);
+      return;
+    }
+  }
+#endif
+  login_token_request_ = service->StartRequest(OAuth2TokenService::ScopeSet(),
+                                               this);
 }
 
 void ExperimentalIdentityGetAuthTokenFunction::StartGaiaRequest(
@@ -269,17 +285,6 @@
               oauth2_info.client_id,
               oauth2_info.scopes,
               gaia_mint_token_mode_));
-#if defined(OS_CHROMEOS)
-  if (chrome::IsRunningInForcedAppMode()) {
-    std::string chrome_client_id;
-    std::string chrome_client_secret;
-    if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo(
-           &chrome_client_id, &chrome_client_secret)) {
-      mint_token_flow->SetChromeOAuthClientInfo(chrome_client_id,
-                                                chrome_client_secret);
-    }
-  }
-#endif
   return mint_token_flow;
 }
 
diff --git a/chrome/browser/extensions/api/identity/identity_api.cc b/chrome/browser/extensions/api/identity/identity_api.cc
index 75dcaba..6a8beb7 100644
--- a/chrome/browser/extensions/api/identity/identity_api.cc
+++ b/chrome/browser/extensions/api/identity/identity_api.cc
@@ -416,9 +416,25 @@
 }
 
 void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() {
-  login_token_request_ =
-      ProfileOAuth2TokenServiceFactory::GetForProfile(profile())->
-          StartRequest(OAuth2TokenService::ScopeSet(), this);
+  ProfileOAuth2TokenService* service =
+      ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
+#if defined(OS_CHROMEOS)
+  if (chrome::IsRunningInForcedAppMode()) {
+    std::string app_client_id;
+    std::string app_client_secret;
+    if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo(
+           &app_client_id, &app_client_secret)) {
+      login_token_request_ =
+          service->StartRequestForClient(app_client_id,
+                                         app_client_secret,
+                                         OAuth2TokenService::ScopeSet(),
+                                         this);
+      return;
+    }
+  }
+#endif
+  login_token_request_ = service->StartRequest(OAuth2TokenService::ScopeSet(),
+                                               this);
 }
 
 void IdentityGetAuthTokenFunction::StartGaiaRequest(
@@ -458,17 +474,6 @@
               oauth2_client_id_,
               oauth2_info.scopes,
               gaia_mint_token_mode_));
-#if defined(OS_CHROMEOS)
-  if (chrome::IsRunningInForcedAppMode()) {
-    std::string chrome_client_id;
-    std::string chrome_client_secret;
-    if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo(
-           &chrome_client_id, &chrome_client_secret)) {
-      mint_token_flow->SetChromeOAuthClientInfo(chrome_client_id,
-                                                chrome_client_secret);
-    }
-  }
-#endif
   return mint_token_flow;
 }
 
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index 5bedcc4..a0708c6 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
@@ -17,6 +18,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/test/test_utils.h"
@@ -29,10 +31,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using testing::_;
 using testing::Return;
 using testing::ReturnRef;
@@ -474,7 +472,7 @@
                        NonInteractiveSuccess) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -1162,7 +1160,7 @@
 IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -1189,7 +1187,7 @@
     LaunchWebAuthFlowFunctionTest, InteractiveFirstNavigationSuccess) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/api/idltest/idltest_apitest.cc b/chrome/browser/extensions/api/idltest/idltest_apitest.cc
index d950038..da3d621 100644
--- a/chrome/browser/extensions/api/idltest/idltest_apitest.cc
+++ b/chrome/browser/extensions/api/idltest/idltest_apitest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/common/chrome_switches.h"
+#include "extensions/common/switches.h"
 
 class ExtensionIdltestApiTest : public ExtensionApiTest {
  public:
@@ -12,7 +12,8 @@
 
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
+    command_line->AppendSwitch(
+        extensions::switches::kEnableExperimentalExtensionApis);
   }
 };
 
diff --git a/chrome/browser/extensions/api/management/management_api_browsertest.cc b/chrome/browser/extensions/api/management/management_api_browsertest.cc
index 9f73188..51663c9 100644
--- a/chrome/browser/extensions/api/management/management_api_browsertest.cc
+++ b/chrome/browser/extensions/api/management/management_api_browsertest.cc
@@ -95,6 +95,16 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest,
+                       SelfUninstallNoPermissions) {
+  ExtensionTestMessageListener listener1("success", false);
+  ASSERT_TRUE(LoadExtension(
+      test_data_dir_.AppendASCII("management/self_uninstall_helper")));
+  ASSERT_TRUE(LoadExtension(
+      test_data_dir_.AppendASCII("management/self_uninstall_noperm")));
+  ASSERT_TRUE(listener1.WaitUntilSatisfied());
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest,
                        UninstallWithConfirmDialog) {
   ExtensionService* service = ExtensionSystem::Get(browser()->profile())->
       extension_service();
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc
index 0203b2f..6e98c56 100644
--- a/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -108,6 +108,11 @@
   ASSERT_TRUE(RunExtensionSubtest("management/test", "basics.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, NoPermission) {
+  LoadExtensions();
+  ASSERT_TRUE(RunExtensionSubtest("management/no_permission", "test.html"));
+}
+
 // Disabled: http://crbug.com/174411
 #if defined(OS_WIN)
 #define MAYBE_Uninstall DISABLED_Uninstall
diff --git a/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc b/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc
index 8ee3735..2af4bfb 100644
--- a/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc
+++ b/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc
@@ -2,20 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/command_line.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_process_manager.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/extensions/platform_app_browsertest_util.h"
+#include "chrome/browser/media_galleries/media_file_system_registry.h"
+#include "chrome/browser/media_galleries/media_galleries_preferences.h"
 #include "chrome/browser/media_galleries/media_galleries_test_util.h"
 #include "chrome/browser/storage_monitor/storage_info.h"
 #include "chrome/browser/storage_monitor/storage_monitor.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_switches.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/common/switches.h"
 
 using extensions::PlatformAppBrowserTest;
 
@@ -36,12 +42,38 @@
  public:
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
+    command_line->AppendSwitch(
+        extensions::switches::kEnableExperimentalExtensionApis);
   }
 };
 
 }  // namespace
 
+// This function is to ensure at least one (fake) media gallery exists for
+// testing platforms with no default media galleries, such as CHROMEOS.
+void MakeFakeMediaGalleryForTest(Profile* profile, const base::FilePath& path) {
+  base::RunLoop runloop;
+  chrome::StorageMonitor::GetInstance()->EnsureInitialized(
+      runloop.QuitClosure());
+  runloop.Run();
+
+  chrome::MediaGalleriesPreferences* preferences =
+      g_browser_process->media_file_system_registry()->GetPreferences(profile);
+
+  chrome::MediaGalleryPrefInfo gallery_info;
+  ASSERT_FALSE(preferences->LookUpGalleryByPath(path, &gallery_info));
+  preferences->AddGallery(gallery_info.device_id,
+                          gallery_info.path,
+                          false /* user_added */,
+                          gallery_info.volume_label,
+                          gallery_info.vendor_name,
+                          gallery_info.model_name,
+                          gallery_info.total_size_in_bytes,
+                          gallery_info.last_attach_time);
+
+  content::RunAllPendingInMessageLoop();
+}
+
 class MediaGalleriesPlatformAppBrowserTest : public PlatformAppBrowserTest {
  protected:
   // Since ExtensionTestMessageListener does not work with RunPlatformAppTest(),
@@ -84,7 +116,7 @@
   chrome::EnsureMediaDirectoriesExists media_directories;
   ASSERT_TRUE(RunPlatformAppTest("api_test/media_galleries/no_access"))
       << message_;
-  RunSecondTestPhase(UTF8ToUTF16(base::StringPrintf(
+  RunSecondTestPhase(base::UTF8ToUTF16(base::StringPrintf(
       kTestGalleries, media_directories.num_galleries())));
 }
 
@@ -106,11 +138,32 @@
   chrome::EnsureMediaDirectoriesExists media_directories;
   ASSERT_TRUE(RunPlatformAppTest("api_test/media_galleries/read_access"))
       << message_;
-  RunSecondTestPhase(UTF8ToUTF16(base::StringPrintf(
+  RunSecondTestPhase(base::UTF8ToUTF16(base::StringPrintf(
       kTestGalleries, media_directories.num_galleries())));
 }
 
 IN_PROC_BROWSER_TEST_F(MediaGalleriesPlatformAppBrowserTest,
+                       MediaGalleriesCopyTo) {
+  chrome::EnsureMediaDirectoriesExists media_directories;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  MakeFakeMediaGalleryForTest(browser()->profile(), temp_dir.path());
+  ASSERT_TRUE(RunPlatformAppTest("api_test/media_galleries/copy_to_access"))
+      << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(MediaGalleriesPlatformAppBrowserTest,
+                       MediaGalleriesCopyToNoAccess) {
+  chrome::EnsureMediaDirectoriesExists media_directories;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  MakeFakeMediaGalleryForTest(browser()->profile(), temp_dir.path());
+  ASSERT_TRUE(RunPlatformAppTest(
+      "api_test/media_galleries/copy_to_access/no_access"))
+      << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(MediaGalleriesPlatformAppBrowserTest,
                        MediaGalleriesAccessAttached) {
   chrome::EnsureMediaDirectoriesExists media_directories;
 
diff --git a/chrome/browser/extensions/api/media_galleries_private/media_galleries_private_event_router.cc b/chrome/browser/extensions/api/media_galleries_private/media_galleries_private_event_router.cc
index 8a2070a..2522239 100644
--- a/chrome/browser/extensions/api/media_galleries_private/media_galleries_private_event_router.cc
+++ b/chrome/browser/extensions/api/media_galleries_private/media_galleries_private_event_router.cc
@@ -86,8 +86,18 @@
   if (!router->HasEventListener(event_names::kOnAttachEventName))
     return;
 
+  chrome::MediaGalleryPrefInfo pref_info;
+  pref_info.display_name = info.name();
+  pref_info.device_id = info.device_id();
+  pref_info.type = chrome::MediaGalleryPrefInfo::kAutoDetected;
+  pref_info.volume_label = info.storage_label();
+  pref_info.vendor_name = info.vendor_name();
+  pref_info.model_name = info.model_name();
+  pref_info.total_size_in_bytes = info.total_size_in_bytes();
+  pref_info.volume_metadata_valid = true;
+
   DeviceAttachmentDetails details;
-  details.device_name = UTF16ToUTF8(info.name());
+  details.device_name = UTF16ToUTF8(pref_info.GetGalleryDisplayName());
   details.device_id = GetTransientIdForDeviceId(info.device_id());
 
   scoped_ptr<base::ListValue> args(new base::ListValue());
diff --git a/chrome/browser/extensions/api/messaging/message_service.cc b/chrome/browser/extensions/api/messaging/message_service.cc
index e1ef8b3..65f74ed 100644
--- a/chrome/browser/extensions/api/messaging/message_service.cc
+++ b/chrome/browser/extensions/api/messaging/message_service.cc
@@ -203,6 +203,16 @@
     return;
   }
 
+  ExtensionService* extension_service =
+      extensions::ExtensionSystem::Get(profile)->extension_service();
+
+  if (profile->IsOffTheRecord() &&
+      !extension_service->IsIncognitoEnabled(target_extension_id)) {
+    DispatchOnDisconnect(
+        source, receiver_port_id, kReceivingEndDoesntExistError);
+    return;
+  }
+
   if (source_extension_id != target_extension_id) {
     // It's an external connection. Check the externally_connectable manifest
     // key if it's present. If it's not, we allow connection from any extension
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.cc b/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.cc
index a93b863..5366620 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.cc
+++ b/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.cc
@@ -93,8 +93,7 @@
   std::string path;
   // JSON parsed checks that all strings are valid UTF8.
   if (!dictionary->GetString("path", &path) ||
-      (path_ = base::FilePath::FromUTF8Unsafe(path)).empty() ||
-      !path_.IsAbsolute()) {
+      (path_ = base::FilePath::FromUTF8Unsafe(path)).empty()) {
     *error_message = "Invalid value for path.";
     return false;
   }
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h b/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h
index 41f45b8..5931e19 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h
+++ b/chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h
@@ -35,11 +35,11 @@
       const base::FilePath& file_path,
       std::string* error_message);
 
-  const std::string& name() { return name_; }
-  const std::string& description() { return description_; }
-  HostInterface interface() { return interface_; }
-  base::FilePath& path() { return path_; }
-  const URLPatternSet& allowed_origins() { return allowed_origins_; }
+  const std::string& name() const { return name_; }
+  const std::string& description() const { return description_; }
+  HostInterface interface() const { return interface_; }
+  const base::FilePath& path() const { return path_; }
+  const URLPatternSet& allowed_origins() const { return allowed_origins_; }
 
  private:
   NativeMessagingHostManifest();
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_host_manifest_unittest.cc b/chrome/browser/extensions/api/messaging/native_messaging_host_manifest_unittest.cc
index d9236ba..58982a2 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_host_manifest_unittest.cc
+++ b/chrome/browser/extensions/api/messaging/native_messaging_host_manifest_unittest.cc
@@ -102,16 +102,6 @@
   EXPECT_FALSE(error_message.empty());
 }
 
-TEST_F(NativeMessagingHostManifestTest, RelativePath) {
-  ASSERT_TRUE(WriteManifest(kTestHostName, "host.exe", kTestOrigin));
-
-  std::string error_message;
-  scoped_ptr<NativeMessagingHostManifest> manifest =
-      NativeMessagingHostManifest::Load(manifest_path_, &error_message);
-  ASSERT_FALSE(manifest);
-  EXPECT_FALSE(error_message.empty());
-}
-
 // Verify that match-all origins are rejected.
 TEST_F(NativeMessagingHostManifestTest, MatchAllOrigin) {
   ASSERT_TRUE(WriteManifest(kTestHostName, kTestHostPath,
diff --git a/chrome/browser/extensions/api/messaging/native_process_launcher.cc b/chrome/browser/extensions/api/messaging/native_process_launcher.cc
index 654158a..2186e1e 100644
--- a/chrome/browser/extensions/api/messaging/native_process_launcher.cc
+++ b/chrome/browser/extensions/api/messaging/native_process_launcher.cc
@@ -74,6 +74,10 @@
     void DoLaunchOnThreadPool(const GURL& origin,
                               const std::string& native_host_name,
                               LaunchedCallback callback);
+    void PostErrorResult(const LaunchedCallback& callback, LaunchResult error);
+    void PostResult(const LaunchedCallback& callback,
+                    base::PlatformFile read_file,
+                    base::PlatformFile write_file);
     void CallCallbackOnIOThread(LaunchedCallback callback,
                                 LaunchResult result,
                                 base::PlatformFile read_file,
@@ -118,12 +122,7 @@
   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
 
   if (!NativeMessagingHostManifest::IsValidName(native_host_name)) {
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread,
-                   this, callback, RESULT_INVALID_NAME,
-                   base::kInvalidPlatformFileValue,
-                   base::kInvalidPlatformFileValue));
+    PostErrorResult(callback, RESULT_INVALID_NAME);
     return;
   }
 
@@ -131,57 +130,66 @@
   scoped_ptr<NativeMessagingHostManifest> manifest;
 
   // First check if the manifest location is specified in the command line.
-  base::FilePath path = GetHostManifestPathFromCommandLine(native_host_name);
-  if (!path.empty()) {
-    manifest = NativeMessagingHostManifest::Load(path, &error_message);
-  } else {
-    // Try loading the manifest from the default location.
-    manifest = FindAndLoadManifest(native_host_name, &error_message);
+  base::FilePath manifest_path =
+      GetHostManifestPathFromCommandLine(native_host_name);
+  if (manifest_path.empty())
+    manifest_path = FindManifest(native_host_name, &error_message);
+
+  if (manifest_path.empty()) {
+    LOG(ERROR) << "Can't find manifest for native messaging host "
+               << native_host_name;
+    PostErrorResult(callback, RESULT_NOT_FOUND);
+    return;
   }
 
-  if (manifest && manifest->name() != native_host_name) {
-    error_message = "Name specified in the manifest does not match.";
-    manifest.reset();
-  }
+  manifest = NativeMessagingHostManifest::Load(manifest_path, &error_message);
 
   if (!manifest) {
     LOG(ERROR) << "Failed to load manifest for native messaging host "
                << native_host_name << ": " << error_message;
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread,
-                   this, callback, RESULT_NOT_FOUND,
-                   base::kInvalidPlatformFileValue,
-                   base::kInvalidPlatformFileValue));
+    PostErrorResult(callback, RESULT_NOT_FOUND);
+    return;
+  }
+
+  if (manifest->name() != native_host_name) {
+    LOG(ERROR) << "Failed to load manifest for native messaging host "
+               << native_host_name
+               << ": Invalid name specified in the manifest.";
+    PostErrorResult(callback, RESULT_NOT_FOUND);
     return;
   }
 
   if (!manifest->allowed_origins().MatchesSecurityOrigin(origin)) {
     // Not an allowed origin.
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread,
-                   this, callback, RESULT_FORBIDDEN,
-                   base::kInvalidPlatformFileValue,
-                   base::kInvalidPlatformFileValue));
+    PostErrorResult(callback, RESULT_FORBIDDEN);
     return;
   }
 
-  CommandLine command_line(manifest->path());
+  base::FilePath host_path = manifest->path();
+  if (!host_path.IsAbsolute()) {
+    // On Windows host path is allowed to be relative to the location of the
+    // manifest file. On all other platforms the path must be absolute.
+#if defined(OS_WIN)
+    host_path = manifest_path.DirName().Append(host_path);
+#else  // defined(OS_WIN)
+    LOG(ERROR) << "Native messaging host path must be absolute for "
+               << native_host_name;
+    PostErrorResult(callback, RESULT_NOT_FOUND);
+    return;
+#endif  // !defined(OS_WIN)
+  }
+
+  CommandLine command_line(host_path);
   command_line.AppendArg(origin.spec());
 
   base::PlatformFile read_file;
   base::PlatformFile write_file;
-  LaunchResult result = RESULT_FAILED_TO_START;
   if (NativeProcessLauncher::LaunchNativeProcess(
           command_line, &read_file, &write_file)) {
-    result = RESULT_SUCCESS;
+    PostResult(callback, read_file, write_file);
+  } else {
+    PostErrorResult(callback, RESULT_FAILED_TO_START);
   }
-
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO, FROM_HERE,
-      base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread,
-                 this, callback, result, read_file, write_file));
 }
 
 void NativeProcessLauncherImpl::Core::CallCallbackOnIOThread(
@@ -201,6 +209,27 @@
   callback.Run(result, read_file, write_file);
 }
 
+void NativeProcessLauncherImpl::Core::PostErrorResult(
+    const LaunchedCallback& callback,
+    LaunchResult error) {
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread,
+                 this, callback, error,
+                 base::kInvalidPlatformFileValue,
+                 base::kInvalidPlatformFileValue));
+}
+
+void NativeProcessLauncherImpl::Core::PostResult(
+    const LaunchedCallback& callback,
+    base::PlatformFile read_file,
+    base::PlatformFile write_file) {
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO, FROM_HERE,
+      base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread,
+                 this, callback, RESULT_SUCCESS, read_file, write_file));
+}
+
 NativeProcessLauncherImpl::NativeProcessLauncherImpl()
   : core_(new Core()) {
 }
diff --git a/chrome/browser/extensions/api/messaging/native_process_launcher.h b/chrome/browser/extensions/api/messaging/native_process_launcher.h
index 0f5122bd..5ab1fce 100644
--- a/chrome/browser/extensions/api/messaging/native_process_launcher.h
+++ b/chrome/browser/extensions/api/messaging/native_process_launcher.h
@@ -19,8 +19,6 @@
 
 namespace extensions {
 
-class NativeMessagingHostManifest;
-
 class NativeProcessLauncher {
  public:
   enum LaunchResult {
@@ -57,10 +55,9 @@
   // The following two methods are platform specific and are implemented in
   // platform-specific .cc files.
 
-  // Loads manifest for the native messaging host |name|.
-  static scoped_ptr<NativeMessagingHostManifest> FindAndLoadManifest(
-      const std::string& native_host_name,
-      std::string* error_message);
+  // Finds manifest file for the native messaging host |native_host_name|.
+  static base::FilePath FindManifest(const std::string& native_host_name,
+                                     std::string* error_message);
 
   // Launches native messaging process.
   static bool LaunchNativeProcess(
diff --git a/chrome/browser/extensions/api/messaging/native_process_launcher_posix.cc b/chrome/browser/extensions/api/messaging/native_process_launcher_posix.cc
index 3cc4bc6..55737ad 100644
--- a/chrome/browser/extensions/api/messaging/native_process_launcher_posix.cc
+++ b/chrome/browser/extensions/api/messaging/native_process_launcher_posix.cc
@@ -9,7 +9,6 @@
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/process/launch.h"
-#include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
 
 namespace extensions {
 
@@ -29,14 +28,11 @@
 }  // namespace
 
 // static
-scoped_ptr<NativeMessagingHostManifest>
-NativeProcessLauncher::FindAndLoadManifest(
+base::FilePath NativeProcessLauncher::FindManifest(
     const std::string& native_host_name,
     std::string* error_message) {
-  base::FilePath manifest_path =
-      base::FilePath(kNativeMessagingDirectory).Append(
-          native_host_name + ".json");
-  return NativeMessagingHostManifest::Load(manifest_path, error_message);
+  return base::FilePath(kNativeMessagingDirectory).Append(
+      native_host_name + ".json");
 }
 
 // static
diff --git a/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc b/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc
index 0eed352..9913f5f 100644
--- a/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc
+++ b/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc
@@ -16,7 +16,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/registry.h"
 #include "base/win/scoped_handle.h"
-#include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
 #include "crypto/random.h"
 
 namespace extensions {
@@ -46,26 +45,30 @@
 }  // namespace
 
 // static
-scoped_ptr<NativeMessagingHostManifest>
-NativeProcessLauncher::FindAndLoadManifest(
+base::FilePath NativeProcessLauncher::FindManifest(
     const std::string& native_host_name,
     std::string* error_message) {
   string16 native_host_name_wide = UTF8ToUTF16(native_host_name);
 
   // First check 32-bit registry and then try 64-bit.
-  string16 manifest_path =
+  string16 manifest_path_str =
       GetManifestPath(native_host_name_wide, KEY_WOW64_32KEY);
-  if (manifest_path.empty())
-    manifest_path = GetManifestPath(native_host_name_wide, KEY_WOW64_64KEY);
+  if (manifest_path_str.empty())
+    manifest_path_str = GetManifestPath(native_host_name_wide, KEY_WOW64_64KEY);
 
-  if (manifest_path.empty()) {
+  if (manifest_path_str.empty()) {
     *error_message = "Native messaging host " + native_host_name +
         " is not registered";
-    return scoped_ptr<NativeMessagingHostManifest>();
+    return base::FilePath();
   }
 
-  return NativeMessagingHostManifest::Load(
-      base::FilePath(manifest_path), error_message);
+  base::FilePath manifest_path(manifest_path_str);
+  if (!manifest_path.IsAbsolute()) {
+    *error_message = "Path to native messaging host manifest must be absolute.";
+    return base::FilePath();
+  }
+
+  return manifest_path;
 }
 
 // static
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_api_chromeos.cc b/chrome/browser/extensions/api/networking_private/networking_private_api_chromeos.cc
index cdaf357..c42285b 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_api_chromeos.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_api_chromeos.cc
@@ -163,7 +163,7 @@
       GetNetworkState(service_path);
   if (!state) {
     error_ = "Error.InvalidParameter";
-    SendResponse(false);
+    return false;
   }
 
   scoped_ptr<base::DictionaryValue> result_dict(new base::DictionaryValue);
@@ -294,7 +294,7 @@
       api::StartConnect::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  const bool ignore_error_state = true;
+  const bool check_error_state = false;
   NetworkHandler::Get()->network_connection_handler()->ConnectToNetwork(
       params->network_guid,  // service path
       base::Bind(
@@ -303,7 +303,7 @@
       base::Bind(
           &NetworkingPrivateStartConnectFunction::ConnectionStartFailed,
           this),
-      ignore_error_state);
+      check_error_state);
   return true;
 }
 
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_api_nonchromeos.cc b/chrome/browser/extensions/api/networking_private/networking_private_api_nonchromeos.cc
index 55beff7..d7b5efa 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_api_nonchromeos.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_api_nonchromeos.cc
@@ -164,7 +164,6 @@
       "  \"Type\": \"WiFi\","
       "  \"WiFi\": {"
       "    \"AutoConnect\": false,"
-      "    \"FrequencyList\": [2400, 5000],"
       "    \"Security\": \"WPA-PSK\","
       "    \"SignalStrength\": 80"
       "  }"
@@ -221,7 +220,6 @@
       "    \"Type\": \"WiFi\","
       "    \"WiFi\": {"
       "      \"AutoConnect\": false,"
-      "      \"FrequencyList\": [2400],"
       "      \"Security\": \"WEP-PSK\","
       "      \"SignalStrength\": 0"
       "    }"
@@ -233,7 +231,6 @@
       "    \"Type\": \"WiFi\","
       "    \"WiFi\": {"
       "      \"AutoConnect\": false,"
-      "      \"FrequencyList\": [2400, 5000],"
       "      \"Security\": \"WPA-PSK\","
       "      \"SignalStrength\": 80"
       "    }"
@@ -321,8 +318,6 @@
        "\"Name\":\"wifi2_PSK\","
        "\"Type\":\"WiFi\","
        "\"WiFi\":{"
-         "\"Frequency\":5000,"
-         "\"FrequencyList\":[2400,5000],"
          "\"SSID\":\"stub_wifi2\","
          "\"Security\":\"WPA-PSK\","
          "\"SignalStrength\":80}}";
@@ -432,4 +427,3 @@
   SendResponse(true);
   return true;
 }
-
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
index 5ab4ef5..55bc808 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
@@ -155,6 +155,10 @@
     service_test->SetServiceProperty("stub_wifi2",
                                      flimflam::kSignalStrengthProperty,
                                      base::FundamentalValue(80));
+    service_test->SetServiceProperty("stub_wifi2",
+                                     flimflam::kConnectableProperty,
+                                     base::FundamentalValue(true));
+
     base::ListValue frequencies2;
     frequencies2.AppendInteger(2400);
     frequencies2.AppendInteger(5000);
@@ -267,6 +271,10 @@
 }
 
 #if defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_P(ExtensionNetworkingPrivateApiTest, GetStateNonExistent) {
+  EXPECT_TRUE(RunNetworkingSubtest("getStateNonExistent")) << message_;
+}
+
 IN_PROC_BROWSER_TEST_P(ExtensionNetworkingPrivateApiTest,
                        GetManagedProperties) {
   ShillServiceClient::TestInterface* service_test =
@@ -358,4 +366,3 @@
                         testing::Bool());
 
 }  // namespace chromeos
-
diff --git a/chrome/browser/extensions/api/notifications/notifications_api.cc b/chrome/browser/extensions/api/notifications/notifications_api.cc
index 7e9477e..257eb1a 100644
--- a/chrome/browser/extensions/api/notifications/notifications_api.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_api.cc
@@ -424,7 +424,8 @@
     }
   }
 
-  g_browser_process->notification_ui_manager()->Add(*notification, profile());
+  g_browser_process->notification_ui_manager()->Update(
+      *notification, profile());
   return true;
 }
 
diff --git a/chrome/browser/extensions/api/notifications/notifications_apitest.cc b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
index 4fae518..c745215 100644
--- a/chrome/browser/extensions/api/notifications/notifications_apitest.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/extensions/api/notifications/notifications_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/features/feature.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
@@ -16,11 +15,6 @@
 #include "ui/message_center/message_center_switches.h"
 #include "ui/message_center/message_center_util.h"
 
-// TODO(kbr): remove: http://crbug.com/222296
-#if defined(OS_MACOSX)
-#import "base/mac/mac_util.h"
-#endif
-
 using extensions::Extension;
 
 namespace utils = extension_function_test_utils;
@@ -29,11 +23,6 @@
 
 class NotificationsApiTest : public ExtensionApiTest {
  public:
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
-  }
-
   const extensions::Extension* LoadExtensionAndWait(
       const std::string& test_name) {
     base::FilePath extdir = test_data_dir_.AppendASCII(test_name);
@@ -51,12 +40,6 @@
 }  // namespace
 
 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestIdUsage) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   // Create a new notification. A lingering output of this block is the
   // notifications ID, which we'll use in later parts of this test.
   std::string notification_id;
@@ -462,12 +445,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestEvents) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(RunExtensionTest("notifications/api/events")) << message_;
 }
 
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api.cc b/chrome/browser/extensions/api/omnibox/omnibox_api.cc
index 9a07ee9..06db5b8 100644
--- a/chrome/browser/extensions/api/omnibox/omnibox_api.cc
+++ b/chrome/browser/extensions/api/omnibox/omnibox_api.cc
@@ -242,8 +242,6 @@
       }
     }
   } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
-    if (content::Details<UnloadedExtensionInfo>(details)->already_disabled)
-      return;
     const Extension* extension =
         content::Details<UnloadedExtensionInfo>(details)->extension;
     if (!OmniboxInfo::GetKeyword(extension).empty()) {
diff --git a/chrome/browser/extensions/api/permissions/permissions_apitest.cc b/chrome/browser/extensions/api/permissions/permissions_apitest.cc
index 1bfea06..d2923d1 100644
--- a/chrome/browser/extensions/api/permissions/permissions_apitest.cc
+++ b/chrome/browser/extensions/api/permissions/permissions_apitest.cc
@@ -8,8 +8,8 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/permissions/permission_set.h"
+#include "extensions/common/switches.h"
 #include "net/dns/mock_host_resolver.h"
 
 namespace extensions {
diff --git a/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc b/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc
index 41e797c..8a69ab3 100644
--- a/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc
+++ b/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc
@@ -152,7 +152,9 @@
                               extension4() };
   for (size_t i = 0; i < kNumInstalledExtensions; ++i) {
     if (extension == extensions[i] && !installed_[i]) {
-      prefs()->OnExtensionInstalled(extension, Extension::ENABLED,
+      prefs()->OnExtensionInstalled(extension,
+                                    Extension::ENABLED,
+                                    Blacklist::NOT_BLACKLISTED,
                                     syncer::StringOrdinal());
       installed_[i] = true;
       break;
diff --git a/chrome/browser/extensions/api/processes/processes_apitest.cc b/chrome/browser/extensions/api/processes/processes_apitest.cc
index d38a683..8342c17 100644
--- a/chrome/browser/extensions/api/processes/processes_apitest.cc
+++ b/chrome/browser/extensions/api/processes/processes_apitest.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/common/chrome_switches.h"
+#include "extensions/common/switches.h"
 
 // Sometimes times out on Mac OS and Windows
 // crbug.com/97499
@@ -21,7 +21,7 @@
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Processes) {
 #endif
   CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
+      extensions::switches::kEnableExperimentalExtensionApis);
 
   ASSERT_TRUE(RunExtensionTest("processes/api")) << message_;
 }
@@ -32,7 +32,7 @@
 
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ProcessesVsTaskManager) {
   CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
+      extensions::switches::kEnableExperimentalExtensionApis);
 
   // Ensure task manager is not yet updating
   TaskManagerModel* model = TaskManager::GetInstance()->model();
diff --git a/chrome/browser/extensions/api/recovery_private/recovery_operation.cc b/chrome/browser/extensions/api/recovery_private/recovery_operation.cc
new file mode 100644
index 0000000..c56bac7
--- /dev/null
+++ b/chrome/browser/extensions/api/recovery_private/recovery_operation.cc
@@ -0,0 +1,84 @@
+// Copyright 2013 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.
+
+#include "base/time/time.h"
+#include "chrome/browser/extensions/api/recovery_private/recovery_operation.h"
+#include "chrome/browser/extensions/api/recovery_private/recovery_operation_manager.h"
+
+namespace extensions {
+namespace recovery {
+
+namespace {
+
+recovery_api::Stage nextStage(recovery_api::Stage stage) {
+  if (stage == recovery_api::STAGE_CONFIRMATION) {
+    return recovery_api::STAGE_DOWNLOAD;
+  } else if (stage == recovery_api::STAGE_DOWNLOAD) {
+    return recovery_api::STAGE_VERIFYDOWNLOAD;
+  } else if (stage == recovery_api::STAGE_VERIFYDOWNLOAD) {
+    return recovery_api::STAGE_WRITE;
+  } else if (stage == recovery_api::STAGE_WRITE) {
+    return recovery_api::STAGE_VERIFYWRITE;
+  } else {
+    return recovery_api::STAGE_NONE;
+  }
+}
+
+} // namespace
+
+RecoveryOperation::RecoveryOperation(RecoveryOperationManager* manager,
+                                     const ExtensionId& extension_id)
+    : manager_(manager), extension_id_(extension_id) {
+}
+
+RecoveryOperation::~RecoveryOperation() {
+  timer_.Stop();
+}
+
+void RecoveryOperation::Start(const WriteStartCallback& callback) {
+  timer_.Start(FROM_HERE,
+               base::TimeDelta::FromMilliseconds(100),
+               this,
+               &RecoveryOperation::UpdateProgress);
+
+  stage_ = recovery_api::STAGE_DOWNLOAD;
+  progress_ = 0;
+
+  callback.Run(true);
+}
+
+void RecoveryOperation::Cancel(const WriteCancelCallback& callback) {
+  timer_.Stop();
+  callback.Run(true);
+}
+
+void RecoveryOperation::Abort() {
+  timer_.Stop();
+  manager_->OnError(extension_id_, stage_, progress_);
+}
+
+void RecoveryOperation::UpdateProgress() {
+  if (stage_ == recovery_api::STAGE_NONE) {
+    return;
+  }
+
+  progress_ += 10;
+
+  if (progress_ > 100) {
+    progress_ = 0;
+    stage_ = nextStage(stage_);
+
+    if (stage_ == recovery_api::STAGE_NONE) {
+      timer_.Stop();
+      manager_->OnComplete(extension_id_);
+      return;
+    }
+  }
+
+  manager_->OnProgress(extension_id_, stage_, progress_);
+}
+
+
+} // namespace recovery
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/recovery_private/recovery_operation.h b/chrome/browser/extensions/api/recovery_private/recovery_operation.h
new file mode 100644
index 0000000..2d543cd
--- /dev/null
+++ b/chrome/browser/extensions/api/recovery_private/recovery_operation.h
@@ -0,0 +1,58 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_RECOVERY_OPERATION_H_
+#define CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_RECOVERY_OPERATION_H_
+
+#include "base/callback.h"
+#include "base/timer/timer.h"
+#include "chrome/common/extensions/api/recovery_private.h"
+
+namespace recovery_api = extensions::api::recovery_private;
+
+namespace extensions {
+namespace recovery {
+
+class RecoveryOperationManager;
+
+// Encapsulates an operation being run on behalf of the
+// RecoveryOperationManager.  Construction of the operation does not start
+// anything.  The operation's Start method should be called to start it, and
+// then the Cancel method will stop it.  The operation will call back to the
+// RecoveryOperationManager periodically or on any significant event.
+class RecoveryOperation {
+ public:
+  typedef base::Callback<void(bool)> WriteStartCallback;
+  typedef base::Callback<void(bool)> WriteCancelCallback;
+  typedef std::string ExtensionId;
+
+  RecoveryOperation(RecoveryOperationManager* manager,
+                    const ExtensionId& extension_id);
+  virtual ~RecoveryOperation();
+
+  // Starts the operation.
+  virtual void Start(const WriteStartCallback& callback);
+  // Stops the operation gracefully, as if by a user action.
+  virtual void Cancel(const WriteCancelCallback& callback);
+  // Aborts the operation, generating an error.
+  virtual void Abort();
+ protected:
+
+  RecoveryOperationManager* manager_;
+  const ExtensionId extension_id_;
+
+  recovery_api::Stage stage_;
+  int progress_;
+
+  // Timer for stubbing.
+  base::RepeatingTimer<RecoveryOperation> timer_;
+
+  // Timer callback for stubbing.
+  void UpdateProgress();
+};
+
+} // namespace recovery
+} // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_RECOVERY_OPERATION_H_
diff --git a/chrome/browser/extensions/api/recovery_private/recovery_operation_manager.cc b/chrome/browser/extensions/api/recovery_private/recovery_operation_manager.cc
new file mode 100644
index 0000000..3c80583
--- /dev/null
+++ b/chrome/browser/extensions/api/recovery_private/recovery_operation_manager.cc
@@ -0,0 +1,209 @@
+// Copyright 2013 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.
+
+#include "base/lazy_instance.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/api/recovery_private/recovery_operation.h"
+#include "chrome/browser/extensions/api/recovery_private/recovery_operation_manager.h"
+#include "chrome/browser/extensions/api/recovery_private/write_from_file_operation.h"
+#include "chrome/browser/extensions/api/recovery_private/write_from_url_operation.h"
+#include "chrome/browser/extensions/event_names.h"
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/extensions/event_router_forwarder.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/extensions/extension_system_factory.h"
+#include "content/public/browser/notification_service.h"
+
+namespace extensions {
+namespace recovery {
+
+namespace {
+
+recovery_api::Stage nextStage(recovery_api::Stage stage) {
+  if (stage == recovery_api::STAGE_CONFIRMATION) {
+    return recovery_api::STAGE_DOWNLOAD;
+  } else if (stage == recovery_api::STAGE_DOWNLOAD) {
+    return recovery_api::STAGE_VERIFYDOWNLOAD;
+  } else if (stage == recovery_api::STAGE_VERIFYDOWNLOAD) {
+    return recovery_api::STAGE_WRITE;
+  } else if (stage == recovery_api::STAGE_WRITE) {
+    return recovery_api::STAGE_VERIFYWRITE;
+  } else {
+    return recovery_api::STAGE_NONE;
+  }
+}
+
+} // namespace
+
+
+RecoveryOperationManager::RecoveryOperationManager(Profile* profile)
+    : profile_(profile) {
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
+                 content::Source<Profile>(profile_));
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
+                 content::Source<Profile>(profile_));
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
+                 content::Source<Profile>(profile_));
+}
+
+RecoveryOperationManager::~RecoveryOperationManager() {
+}
+
+void RecoveryOperationManager::Shutdown() {
+  OperationMap::iterator iter;
+
+  for (iter = operations_.begin();
+       iter != operations_.end();
+       ++iter) {
+    iter->second->Abort();
+  }
+}
+
+void RecoveryOperationManager::StartWriteFromUrl(
+    const ExtensionId& extension_id,
+    const GURL& url,
+    scoped_ptr<std::string> hash,
+    bool saveImageAsDownload,
+    const std::string& storage_unit_id,
+    const StartWriteCallback& callback) {
+  OperationMap::iterator existing_operation = operations_.find(extension_id);
+
+  if (existing_operation != operations_.end())
+    return callback.Run(false);
+
+  linked_ptr<RecoveryOperation> operation(
+      new WriteFromUrlOperation(this, extension_id, url, hash.Pass(),
+                                saveImageAsDownload, storage_unit_id));
+
+  operations_.insert(make_pair(extension_id, operation));
+
+  operation->Start(callback);
+}
+
+void RecoveryOperationManager::StartWriteFromFile(
+    const ExtensionId& extension_id,
+    const std::string& storage_unit_id,
+    const StartWriteCallback& callback) {
+  // Currently unimplemented.
+  callback.Run(false);
+}
+
+void RecoveryOperationManager::CancelWrite(
+    const ExtensionId& extension_id,
+    const CancelWriteCallback& callback) {
+  RecoveryOperation* existing_operation = GetOperation(extension_id);
+
+  if (existing_operation == NULL) {
+    callback.Run(false);
+  } else {
+    existing_operation->Cancel(callback);
+    DeleteOperation(extension_id);
+  }
+}
+
+void RecoveryOperationManager::OnProgress(const ExtensionId& extension_id,
+                                          recovery_api::Stage stage,
+                                          int progress) {
+  recovery_api::ProgressInfo info;
+
+  info.stage = stage;
+  info.percent_complete = progress;
+
+  scoped_ptr<base::ListValue> args(recovery_api::OnWriteProgress::Create(info));
+  scoped_ptr<Event> event(new Event(event_names::kRecoveryOnWriteProgress,
+                                    args.Pass()));
+
+  ExtensionSystem::Get(profile_)->event_router()->
+      DispatchEventToExtension(extension_id, event.Pass());
+}
+
+void RecoveryOperationManager::OnComplete(const ExtensionId& extension_id) {
+
+  scoped_ptr<base::ListValue> args(recovery_api::OnWriteComplete::Create());
+  scoped_ptr<Event> event(new Event(event_names::kRecoveryOnWriteComplete,
+                                    args.Pass()));
+
+  ExtensionSystem::Get(profile_)->event_router()->
+      DispatchEventToExtension(extension_id, event.Pass());
+
+  DeleteOperation(extension_id);
+}
+
+void RecoveryOperationManager::OnError(const ExtensionId& extension_id,
+                                       recovery_api::Stage stage,
+                                       int progress) {
+  recovery_api::ProgressInfo info;
+
+  info.stage = stage;
+  info.percent_complete = progress;
+
+  scoped_ptr<base::ListValue> args(recovery_api::OnWriteError::Create(info));
+  scoped_ptr<Event> event(new Event(event_names::kRecoveryOnWriteError,
+                                    args.Pass()));
+
+  ExtensionSystem::Get(profile_)->event_router()->
+      DispatchEventToExtension(extension_id, event.Pass());
+
+  DeleteOperation(extension_id);
+}
+
+RecoveryOperation* RecoveryOperationManager::GetOperation(
+    const ExtensionId& extension_id) {
+  OperationMap::iterator existing_operation = operations_.find(extension_id);
+
+  if (existing_operation == operations_.end())
+    return NULL;
+
+  return existing_operation->second.get();
+}
+
+void RecoveryOperationManager::DeleteOperation(
+    const ExtensionId& extension_id) {
+  OperationMap::iterator existing_operation = operations_.find(extension_id);
+  if (existing_operation != operations_.end()) {
+    operations_.erase(existing_operation);
+  }
+}
+
+void RecoveryOperationManager::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  switch (type) {
+    case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
+      DeleteOperation(content::Details<const Extension>(details).ptr()->id());
+      break;
+    }
+    case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
+      DeleteOperation(content::Details<const Extension>(details).ptr()->id());
+      break;
+    }
+    case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED: {
+      DeleteOperation(content::Details<const Extension>(details).ptr()->id());
+      break;
+    }
+    default: {
+      NOTREACHED();
+      break;
+    }
+  }
+}
+
+RecoveryOperationManager* RecoveryOperationManager::Get(Profile* profile) {
+  return ProfileKeyedAPIFactory<RecoveryOperationManager>::
+      GetForProfile(profile);
+}
+
+static base::LazyInstance<ProfileKeyedAPIFactory<RecoveryOperationManager> >
+    g_factory = LAZY_INSTANCE_INITIALIZER;
+
+ProfileKeyedAPIFactory<RecoveryOperationManager>*
+    RecoveryOperationManager::GetFactoryInstance() {
+  return &g_factory.Get();
+}
+
+
+} // namespace recovery
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/recovery_private/recovery_operation_manager.h b/chrome/browser/extensions/api/recovery_private/recovery_operation_manager.h
new file mode 100644
index 0000000..3406ca4
--- /dev/null
+++ b/chrome/browser/extensions/api/recovery_private/recovery_operation_manager.h
@@ -0,0 +1,107 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_RECOVERY_OPERATION_MANAGER_H_
+#define CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_RECOVERY_OPERATION_MANAGER_H_
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/stl_util.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/extensions/api/profile_keyed_api_factory.h"
+#include "chrome/browser/extensions/api/recovery_private/recovery_private_api.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/api/recovery_private.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "url/gurl.h"
+
+namespace recovery_api = extensions::api::recovery_private;
+
+namespace extensions {
+namespace recovery {
+
+class RecoveryOperation;
+
+// Manages recovery operations for the current profile.  Including clean-up and
+// message routing.
+class RecoveryOperationManager
+    : public ProfileKeyedAPI,
+      public content::NotificationObserver,
+      public base::SupportsWeakPtr<RecoveryOperationManager> {
+ public:
+  typedef base::Callback<void(bool)> StartWriteCallback;
+  typedef base::Callback<void(bool)> CancelWriteCallback;
+  typedef std::string ExtensionId;
+
+  explicit RecoveryOperationManager(Profile* profile);
+
+  virtual void Shutdown() OVERRIDE;
+
+  // Starts a WriteFromUrl operation.
+  void StartWriteFromUrl(const ExtensionId& extension_id,
+                         const GURL& url,
+                         scoped_ptr<std::string> hash,
+                         bool saveImageAsDownload,
+                         const std::string& storage_unit,
+                         const StartWriteCallback& callback);
+
+  // Starts a WriteFromFile operation.
+  void StartWriteFromFile(const ExtensionId& extension_id,
+                          const std::string& storage_unit_id,
+                          const StartWriteCallback& callback);
+
+  // Cancels the extensions current operation if any.
+  void CancelWrite(const ExtensionId& extension_id,
+                   const CancelWriteCallback& callback);
+
+  // Callback for progress events.
+  void OnProgress(const ExtensionId& extension_id,
+                  recovery_api::Stage stage,
+                  int progress);
+  // Callback for completion events.
+  void OnComplete(const ExtensionId& extension_id);
+
+  // Callback for error events.
+  // TODO: Add error codes.
+  void OnError(const ExtensionId& extension_id,
+               recovery_api::Stage stage,
+               int progress);
+
+  // ProfileKeyedAPI
+  static ProfileKeyedAPIFactory<RecoveryOperationManager>* GetFactoryInstance();
+  static RecoveryOperationManager* Get(Profile* profile);
+
+ private:
+  friend class ProfileKeyedAPIFactory<RecoveryOperationManager>;
+  typedef std::map<ExtensionId, linked_ptr<RecoveryOperation> > OperationMap;
+
+  Profile* profile_;
+  OperationMap operations_;
+  content::NotificationRegistrar registrar_;
+
+  // NotificationObserver
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
+  virtual ~RecoveryOperationManager();
+
+  static const char* service_name() {
+    return "RecoveryOperationManager";
+  }
+
+  RecoveryOperation* GetOperation(const ExtensionId& extension_id);
+  void DeleteOperation(const ExtensionId& extension_id);
+
+  DISALLOW_COPY_AND_ASSIGN(RecoveryOperationManager);
+};
+
+} // namespace recovery
+} // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_RECOVERY_OPERATION_MANAGER_H_
diff --git a/chrome/browser/extensions/api/recovery_private/recovery_private_api.cc b/chrome/browser/extensions/api/recovery_private/recovery_private_api.cc
new file mode 100644
index 0000000..47377b2
--- /dev/null
+++ b/chrome/browser/extensions/api/recovery_private/recovery_private_api.cc
@@ -0,0 +1,112 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/extensions/api/recovery_private/recovery_operation_manager.h"
+#include "chrome/browser/extensions/api/recovery_private/recovery_private_api.h"
+
+namespace recovery_api = extensions::api::recovery_private;
+
+namespace extensions {
+
+const char kInvalidUrl[] = "Invalid URL provided.";
+
+RecoveryPrivateWriteFromUrlFunction::RecoveryPrivateWriteFromUrlFunction() {
+}
+
+RecoveryPrivateWriteFromUrlFunction::~RecoveryPrivateWriteFromUrlFunction() {
+}
+
+bool RecoveryPrivateWriteFromUrlFunction::RunImpl() {
+  scoped_ptr<recovery_api::WriteFromUrl::Params> params(
+      recovery_api::WriteFromUrl::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  bool save_image_as_download = false;
+
+  if (params->options->save_as_download.get()) {
+    save_image_as_download = true;
+  }
+
+  GURL url(params->image_url);
+
+  if (!url.is_valid()) {
+    error_ = kInvalidUrl;
+    return false;
+  }
+
+  recovery::RecoveryOperationManager::Get(profile())->StartWriteFromUrl(
+      extension_id(),
+      url,
+      params->options->image_hash.Pass(),
+      save_image_as_download,
+      params->storage_unit_id,
+      base::Bind(&RecoveryPrivateWriteFromUrlFunction::OnWriteStarted, this));
+
+  return true;
+}
+
+void RecoveryPrivateWriteFromUrlFunction::OnWriteStarted(bool success) {
+  SendResponse(success);
+}
+
+RecoveryPrivateWriteFromFileFunction::RecoveryPrivateWriteFromFileFunction() {
+}
+
+RecoveryPrivateWriteFromFileFunction::~RecoveryPrivateWriteFromFileFunction() {
+}
+
+bool RecoveryPrivateWriteFromFileFunction::RunImpl() {
+  scoped_ptr<recovery_api::WriteFromFile::Params> params(
+      recovery_api::WriteFromFile::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  recovery::RecoveryOperationManager::Get(profile())->StartWriteFromFile(
+      extension_id(),
+      params->storage_unit_id,
+      base::Bind(&RecoveryPrivateWriteFromFileFunction::OnWriteStarted, this));
+
+  return true;
+}
+
+void RecoveryPrivateWriteFromFileFunction::OnWriteStarted(bool success) {
+  SendResponse(success);
+}
+
+RecoveryPrivateCancelWriteFunction::RecoveryPrivateCancelWriteFunction() {
+}
+
+RecoveryPrivateCancelWriteFunction::~RecoveryPrivateCancelWriteFunction() {
+}
+
+bool RecoveryPrivateCancelWriteFunction::RunImpl() {
+  recovery::RecoveryOperationManager::Get(profile())->CancelWrite(
+      extension_id(),
+      base::Bind(&RecoveryPrivateCancelWriteFunction::OnWriteCancelled, this));
+
+  return true;
+}
+
+void RecoveryPrivateCancelWriteFunction::OnWriteCancelled(bool success) {
+  SendResponse(success);
+}
+
+RecoveryPrivateDestroyPartitionsFunction::
+    RecoveryPrivateDestroyPartitionsFunction() {
+}
+
+RecoveryPrivateDestroyPartitionsFunction::
+    ~RecoveryPrivateDestroyPartitionsFunction() {
+}
+
+bool RecoveryPrivateDestroyPartitionsFunction::RunImpl() {
+  scoped_ptr<recovery_api::DestroyPartitions::Params> params(
+      recovery_api::DestroyPartitions::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  SendResponse(true);
+
+  return true;
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/recovery_private/recovery_private_api.h b/chrome/browser/extensions/api/recovery_private/recovery_private_api.h
new file mode 100644
index 0000000..56d4698
--- /dev/null
+++ b/chrome/browser/extensions/api/recovery_private/recovery_private_api.h
@@ -0,0 +1,61 @@
+// Copyright 2013 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.
+#ifndef CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_RECOVERY_PRIVATE_API_H_
+#define CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_RECOVERY_PRIVATE_API_H_
+
+#include "chrome/browser/extensions/extension_function.h"
+#include "chrome/common/extensions/api/recovery_private.h"
+
+namespace extensions {
+
+class RecoveryPrivateWriteFromUrlFunction : public AsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("recoveryPrivate.writeFromUrl",
+                             RECOVERYPRIVATE_WRITEFROMURL)
+  RecoveryPrivateWriteFromUrlFunction();
+
+ private:
+  virtual ~RecoveryPrivateWriteFromUrlFunction();
+  virtual bool RunImpl() OVERRIDE;
+  void OnWriteStarted(bool success);
+};
+
+class RecoveryPrivateWriteFromFileFunction : public AsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("recoveryPrivate.writeFromFile",
+                             RECOVERYPRIVATE_WRITEFROMFILE)
+  RecoveryPrivateWriteFromFileFunction();
+
+ private:
+  virtual ~RecoveryPrivateWriteFromFileFunction();
+  virtual bool RunImpl() OVERRIDE;
+  void OnWriteStarted(bool success);
+};
+
+class RecoveryPrivateCancelWriteFunction : public AsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("recoveryPrivate.cancelWrite",
+                             RECOVERYPRIVATE_CANCELWRITE)
+  RecoveryPrivateCancelWriteFunction();
+
+ private:
+  virtual ~RecoveryPrivateCancelWriteFunction();
+  virtual bool RunImpl() OVERRIDE;
+  void OnWriteCancelled(bool success);
+};
+
+class RecoveryPrivateDestroyPartitionsFunction : public AsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("recoveryPrivate.destroyPartitions",
+                             RECOVERYPRIVATE_DESTROYPARTITIONS)
+  RecoveryPrivateDestroyPartitionsFunction();
+
+ private:
+  virtual ~RecoveryPrivateDestroyPartitionsFunction();
+  virtual bool RunImpl() OVERRIDE;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_RECOVERY_PRIVATE_API_H_
diff --git a/chrome/browser/extensions/api/recovery_private/write_from_file_operation.cc b/chrome/browser/extensions/api/recovery_private/write_from_file_operation.cc
new file mode 100644
index 0000000..a2d24bf
--- /dev/null
+++ b/chrome/browser/extensions/api/recovery_private/write_from_file_operation.cc
@@ -0,0 +1,24 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/extensions/api/recovery_private/write_from_file_operation.h"
+
+namespace extensions {
+namespace recovery {
+
+WriteFromFileOperation::WriteFromFileOperation(
+    RecoveryOperationManager* manager,
+    const ExtensionId& extension_id,
+    const std::string& path,
+    const std::string& storage_unit_id)
+  : RecoveryOperation(manager, extension_id),
+    path_(path),
+    storage_unit_id_(storage_unit_id) {
+}
+
+WriteFromFileOperation::~WriteFromFileOperation() {
+}
+
+} // namespace recovery
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/recovery_private/write_from_file_operation.h b/chrome/browser/extensions/api/recovery_private/write_from_file_operation.h
new file mode 100644
index 0000000..5ec3372
--- /dev/null
+++ b/chrome/browser/extensions/api/recovery_private/write_from_file_operation.h
@@ -0,0 +1,30 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_WRITE_FROM_FILE_OPERATION_H_
+#define CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_WRITE_FROM_FILE_OPERATION_H_
+
+#include "chrome/browser/extensions/api/recovery_private/recovery_operation.h"
+
+namespace extensions {
+namespace recovery {
+
+// Encapsulates a write of an image from a local file.
+class WriteFromFileOperation : public RecoveryOperation {
+ public:
+  WriteFromFileOperation(RecoveryOperationManager* manager,
+                        const ExtensionId& extension_id,
+                        const std::string& path,
+                        const std::string& storage_unit_id);
+  virtual ~WriteFromFileOperation();
+ private:
+  const std::string path_;
+  const std::string storage_unit_id_;
+};
+
+
+} // namespace recovery
+} // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_WRITE_FROM_FILE_OPERATION_H_
diff --git a/chrome/browser/extensions/api/recovery_private/write_from_url_operation.cc b/chrome/browser/extensions/api/recovery_private/write_from_url_operation.cc
new file mode 100644
index 0000000..054c9ae
--- /dev/null
+++ b/chrome/browser/extensions/api/recovery_private/write_from_url_operation.cc
@@ -0,0 +1,29 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/extensions/api/recovery_private/write_from_url_operation.h"
+
+namespace extensions {
+namespace recovery {
+
+WriteFromUrlOperation::WriteFromUrlOperation(RecoveryOperationManager* manager,
+                                             const ExtensionId& extension_id,
+                                             const GURL& url,
+                                             scoped_ptr<std::string> hash,
+                                             bool saveImageAsDownload,
+                                             const std::string& storage_unit_id)
+  : RecoveryOperation(manager, extension_id),
+    url_(url),
+    hash_(hash.Pass()),
+    saveImageAsDownload_(saveImageAsDownload),
+    storage_unit_id_(storage_unit_id) {
+  // "use" this field so that clang does not complain at this time.
+  (void)saveImageAsDownload_;
+}
+
+WriteFromUrlOperation::~WriteFromUrlOperation() {
+}
+
+} // namespace recovery
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/recovery_private/write_from_url_operation.h b/chrome/browser/extensions/api/recovery_private/write_from_url_operation.h
new file mode 100644
index 0000000..e212cd2
--- /dev/null
+++ b/chrome/browser/extensions/api/recovery_private/write_from_url_operation.h
@@ -0,0 +1,35 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_WRITE_FROM_URL_OPERATION_H_
+#define CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_WRITE_FROM_URL_OPERATION_H_
+
+#include "chrome/browser/extensions/api/recovery_private/recovery_operation.h"
+#include "url/gurl.h"
+
+namespace extensions {
+namespace recovery {
+
+// Encapsulates a write of an image accessed via URL.
+class WriteFromUrlOperation : public RecoveryOperation {
+ public:
+  WriteFromUrlOperation(RecoveryOperationManager* manager,
+                        const ExtensionId& extension_id,
+                        const GURL& url,
+                        scoped_ptr<std::string> hash,
+                        bool saveImageAsDownload,
+                        const std::string& storage_unit_id);
+  virtual ~WriteFromUrlOperation();
+ private:
+  const GURL url_;
+  scoped_ptr<std::string> hash_;
+  const bool saveImageAsDownload_;
+  const std::string storage_unit_id_;
+};
+
+
+} // namespace recovery
+} // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_RECOVERY_PRIVATE_WRITE_FROM_URL_OPERATION_H_
diff --git a/chrome/browser/extensions/api/session_restore/session_restore_apitest.cc b/chrome/browser/extensions/api/session_restore/session_restore_apitest.cc
index 25f4e32..0836d84 100644
--- a/chrome/browser/extensions/api/session_restore/session_restore_apitest.cc
+++ b/chrome/browser/extensions/api/session_restore/session_restore_apitest.cc
@@ -2,11 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "chrome/browser/extensions/extension_apitest.h"
-
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
+#include "chrome/test/base/test_switches.h"
 
 // Flaky on ChromeOS, times out on OSX Debug http://crbug.com/251199
 #if defined(OS_CHROMEOS) || (defined(OS_MACOSX) && !defined(NDEBUG))
@@ -17,7 +15,7 @@
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_SessionRestoreApis) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.cc b/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.cc
new file mode 100644
index 0000000..a370dd2
--- /dev/null
+++ b/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2013 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.

+

+#include "chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h"

+

+#include "base/rand_util.h"

+#include "base/strings/string_number_conversions.h"

+#include "base/values.h"

+

+#include "chrome/browser/sync/glue/device_info.h"

+

+using base::DictionaryValue;

+using base::Value;

+using browser_sync::DeviceInfo;

+

+namespace extensions {

+

+std::string GetPublicIdFromGUID(

+    const base::DictionaryValue& id_mapping,

+    const std::string& guid) {

+  for (DictionaryValue::Iterator it(id_mapping);

+       !it.IsAtEnd();

+       it.Advance()) {

+    const Value& value = it.value();

+    std::string guid_in_value;

+    if (!value.GetAsString(&guid_in_value)) {

+      LOG(ERROR) << "Badly formatted dictionary";

+      continue;

+    }

+    if (guid_in_value == guid) {

+      return it.key();

+    }

+  }

+

+  return std::string();

+}

+

+std::string GetGUIDFromPublicId(

+    const base::DictionaryValue& id_mapping,

+    const std::string& id) {

+  std::string guid;

+  id_mapping.GetString(id, &guid);

+  return guid;

+}

+

+// Finds out a random unused id. First finds a random id.

+// If the id is in use, increments the id until it finds an unused id.

+std::string GetRandomId(

+  const DictionaryValue& mapping,

+  int device_count) {

+  // Set the max value for rand to be twice the device count.

+  int max = device_count * 2;

+  int rand_value = base::RandInt(0, max);

+  std::string string_value;

+  const Value *out_value;

+

+  do {

+    string_value = base::IntToString(rand_value);

+    rand_value++;

+  } while (mapping.Get(string_value, &out_value));

+

+  return string_value;

+}

+

+void CreateMappingForUnmappedDevices(

+    std::vector<browser_sync::DeviceInfo*>* device_info,

+    base::DictionaryValue* value) {

+  for (unsigned int i = 0; i < device_info->size(); ++i) {

+    DeviceInfo* device = (*device_info)[i];

+    std::string local_id = GetPublicIdFromGUID(*value,

+                                               device->guid());

+

+    // If the device does not have a local id, set one.

+    if (local_id.empty()) {

+      local_id = GetRandomId(*value, device_info->size());

+      value->SetString(local_id, device->guid());

+    }

+    device->set_public_id(local_id);

+  }

+}

+

+}  // namespace  extensions

diff --git a/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h b/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h
new file mode 100644
index 0000000..e1ec845
--- /dev/null
+++ b/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2013 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.

+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_SIGNEDIN_DEVICES_ID_MAPPING_HELPER_H__

+#define CHROME_BROWSER_EXTENSIONS_API_SIGNEDIN_DEVICES_ID_MAPPING_HELPER_H__

+

+#include <string>

+#include <vector>

+

+namespace base {

+class DictionaryValue;

+}  // namespace base

+

+namespace browser_sync {

+class DeviceInfo;

+}  // namespace browser_sync

+

+namespace extensions {

+

+// In order to not expose unique GUIDs for devices to third pary apps,

+// the unique GUIDs are mapped to local ids and local ids are exposed to apps.

+// The functions in this file are helper routines to do the mapping.

+

+// Gets public id from GUID, given a dictionary that has the mapping.

+// If it cannot find the GUID the public id returned will be empty.

+std::string GetPublicIdFromGUID(

+    const base::DictionaryValue& id_mapping,

+    const std::string& guid);

+

+// Gets the GUID from public id given a dictionary that has the mapping.

+// If it cannot find the public id, the GUID returned will be empty.

+std::string GetGUIDFromPublicId(

+    const base::DictionaryValue& id_mapping,

+    const std::string& id);

+

+// Creates public id for devices that don't have a public id. To create mappings

+// from scratch an empty dictionary must be passed. The dictionary will be

+// updated with the mappings. The |device_info| objects will also be updated

+// with the public ids.

+// The dictionary would have the public id as the key and the

+// device guid as the value.

+void CreateMappingForUnmappedDevices(

+    std::vector<browser_sync::DeviceInfo*>* device_info,

+    base::DictionaryValue* value);

+

+}  // namespace extensions

+

+#endif  // CHROME_BROWSER_EXTENSIONS_API_SIGNEDIN_DEVICES_ID_MAPPING_HELPER_H__

diff --git a/chrome/browser/extensions/api/signedin_devices/id_mapping_helper_unittest.cc b/chrome/browser/extensions/api/signedin_devices/id_mapping_helper_unittest.cc
new file mode 100644
index 0000000..b7cada1
--- /dev/null
+++ b/chrome/browser/extensions/api/signedin_devices/id_mapping_helper_unittest.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2013 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.

+

+#include <string>

+

+#include "base/guid.h"

+#include "base/memory/scoped_ptr.h"

+#include "base/memory/scoped_vector.h"

+#include "base/values.h"

+#include "chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h"

+#include "chrome/browser/sync/glue/device_info.h"

+#include "testing/gmock/include/gmock/gmock.h"

+#include "testing/gtest/include/gtest/gtest.h"

+

+using browser_sync::DeviceInfo;

+

+namespace extensions {

+bool VerifyDictionary(

+    const std::string& path,

+    const std::string& expected_value,

+    const base::DictionaryValue& dictionary) {

+  std::string out;

+  if (dictionary.GetString(path, &out)) {

+    return (out == expected_value);

+  }

+

+  return false;

+}

+

+TEST(IdMappingHelperTest, SetIdsForDevices) {

+  ScopedVector<DeviceInfo> devices;

+

+  devices.push_back(new DeviceInfo(

+      base::GenerateGUID(), "abc Device", "XYZ v1", "XYZ SyncAgent v1",

+      sync_pb::SyncEnums_DeviceType_TYPE_LINUX));

+

+  devices.push_back(new DeviceInfo(

+      base::GenerateGUID(), "def Device", "XYZ v1", "XYZ SyncAgent v1",

+      sync_pb::SyncEnums_DeviceType_TYPE_LINUX));

+

+  base::DictionaryValue dictionary;

+

+  CreateMappingForUnmappedDevices(&(devices.get()), &dictionary);

+

+  std::string public_id1 = devices[0]->public_id();

+  std::string public_id2 = devices[1]->public_id();

+

+  EXPECT_FALSE(public_id1.empty());

+  EXPECT_FALSE(public_id2.empty());

+

+  EXPECT_NE(public_id1, public_id2);

+

+  // Now add a third device.

+  devices.push_back(new DeviceInfo(

+      base::GenerateGUID(), "ghi Device", "XYZ v1", "XYZ SyncAgent v1",

+      sync_pb::SyncEnums_DeviceType_TYPE_LINUX));

+

+  CreateMappingForUnmappedDevices(&(devices.get()), &dictionary);

+

+  // Now make sure the existing ids are not changed.

+  EXPECT_EQ(public_id1, devices[0]->public_id());

+  EXPECT_EQ(public_id2, devices[1]->public_id());

+

+  // Now make sure the id for third device is non empty and different.

+  std::string public_id3 = devices[2]->public_id();

+  EXPECT_FALSE(public_id3.empty());

+  EXPECT_NE(public_id3, public_id1);

+  EXPECT_NE(public_id3, public_id2);

+

+  // Verify the dictionary.

+  EXPECT_TRUE(VerifyDictionary(public_id1, devices[0]->guid(), dictionary));

+  EXPECT_TRUE(VerifyDictionary(public_id2, devices[1]->guid(), dictionary));

+  EXPECT_TRUE(VerifyDictionary(public_id3, devices[2]->guid(), dictionary));

+

+  EXPECT_EQ(dictionary.size(), 3U);

+}

+}  // namespace extensions

diff --git a/chrome/browser/extensions/api/signedin_devices/signedin_devices_api.cc b/chrome/browser/extensions/api/signedin_devices/signedin_devices_api.cc
new file mode 100644
index 0000000..47a132b
--- /dev/null
+++ b/chrome/browser/extensions/api/signedin_devices/signedin_devices_api.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2013 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.
+
+#include "chrome/browser/extensions/api/signedin_devices/signedin_devices_api.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/glue/device_info.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+
+using base::DictionaryValue;
+using browser_sync::DeviceInfo;
+
+namespace extensions {
+
+static const char kPrefStringForIdMapping[] = "id_mapping_dictioanry";
+
+// Gets the dictionary that stores the id mapping. The dictionary is stored
+// in the |ExtensionPrefs|.
+const base::DictionaryValue* GetIdMappingDictionary(
+    ExtensionPrefs* extension_prefs,
+    const std::string& extension_id) {
+  const base::DictionaryValue* out_value = NULL;
+  if (!extension_prefs->ReadPrefAsDictionary(
+          extension_id,
+          kPrefStringForIdMapping,
+          &out_value) || out_value == NULL) {
+    // Looks like this is the first call to get the dictionary. Let us create
+    // a dictionary and set it in to |extension_prefs|.
+    scoped_ptr<DictionaryValue> dictionary(new DictionaryValue());
+    out_value = dictionary.get();
+    extension_prefs->UpdateExtensionPref(
+        extension_id,
+        kPrefStringForIdMapping,
+        dictionary.release());
+  }
+
+  return out_value;
+}
+
+// Helper routine to get all signed in devices. The helper takes in
+// the pointers for |ProfileSyncService| and |Extensionprefs|. This
+// makes it easier to test by passing mock values for these pointers.
+ScopedVector<DeviceInfo> GetAllSignedInDevices(
+    const std::string& extension_id,
+    ProfileSyncService* pss,
+    ExtensionPrefs* extension_prefs) {
+  ScopedVector<DeviceInfo> devices = pss->GetAllSignedInDevices();
+  const DictionaryValue* mapping_dictionary = GetIdMappingDictionary(
+      extension_prefs,
+      extension_id);
+
+  CHECK(mapping_dictionary);
+
+  // |mapping_dictionary| is const. So make an editable copy.
+  scoped_ptr<DictionaryValue> editable_mapping_dictionary(
+      mapping_dictionary->DeepCopy());
+
+  CreateMappingForUnmappedDevices(&(devices.get()),
+                                  editable_mapping_dictionary.get());
+
+  // Write into |ExtensionPrefs| which will get persisted in disk.
+  extension_prefs->UpdateExtensionPref(extension_id,
+                                       kPrefStringForIdMapping,
+                                       editable_mapping_dictionary.release());
+  return devices.Pass();
+}
+
+ScopedVector<DeviceInfo> GetAllSignedInDevices(
+    const std::string& extension_id,
+    Profile* profile) {
+  // Get the profile sync service and extension prefs pointers
+  // and call the helper.
+  ProfileSyncService* pss = ProfileSyncServiceFactory::GetForProfile(profile);
+  ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile);
+
+  return GetAllSignedInDevices(extension_id,
+                               pss,
+                               extension_prefs);
+}
+
+}  // namespace extensions
+
diff --git a/chrome/browser/extensions/api/signedin_devices/signedin_devices_api.h b/chrome/browser/extensions/api/signedin_devices/signedin_devices_api.h
new file mode 100644
index 0000000..7ebc64f
--- /dev/null
+++ b/chrome/browser/extensions/api/signedin_devices/signedin_devices_api.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_SIGNEDIN_DEVICES_SIGNEDIN_DEVICES_API_H__
+#define CHROME_BROWSER_EXTENSIONS_API_SIGNEDIN_DEVICES_SIGNEDIN_DEVICES_API_H__
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_vector.h"
+
+namespace browser_sync {
+class DeviceInfo;
+}  // namespace browser_sync
+
+namespace extensions {
+class ExtensionPrefs;
+}  // namespace extensions
+
+class Profile;
+class ProfileSyncService;
+
+namespace extensions {
+
+// Gets the list of signed in devices. The returned scoped vector will be
+// filled with the list of devices associated with the account signed into this
+// |profile|. This function needs the |extension_id| because the
+// public device ids are set per extension.
+ScopedVector<browser_sync::DeviceInfo> GetAllSignedInDevices(
+    const std::string& extension_id,
+    Profile* profile);
+
+ScopedVector<browser_sync::DeviceInfo> GetAllSignedInDevices(
+    const std::string& extension_id,
+    ProfileSyncService* pss,
+    ExtensionPrefs* extension_prefs);
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_SIGNEDIN_DEVICES_SIGNEDIN_DEVICES_API_H__
diff --git a/chrome/browser/extensions/api/signedin_devices/signedin_devices_api_unittest.cc b/chrome/browser/extensions/api/signedin_devices/signedin_devices_api_unittest.cc
new file mode 100644
index 0000000..ab21083
--- /dev/null
+++ b/chrome/browser/extensions/api/signedin_devices/signedin_devices_api_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2013 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.
+
+#include <string>
+
+#include <vector>
+
+#include "base/guid.h"
+#include "base/message_loop/message_loop.h"
+#include "chrome/browser/extensions/api/signedin_devices/signedin_devices_api.h"
+#include "chrome/browser/extensions/test_extension_prefs.h"
+#include "chrome/browser/sync/glue/device_info.h"
+#include "chrome/browser/sync/profile_sync_service_mock.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_manifest_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using extensions::Extension;
+using extensions::TestExtensionPrefs;
+using browser_sync::DeviceInfo;
+using testing::Return;
+
+namespace extensions {
+
+TEST(SignedinDevicesAPITest, GetSignedInDevices) {
+  ProfileSyncServiceMock pss_mock;
+  base::MessageLoop message_loop_;
+  TestExtensionPrefs extension_prefs(
+      message_loop_.message_loop_proxy().get());
+
+  // Add a couple of devices and make sure we get back public ids for them.
+  std::string extension_name = "test";
+  scoped_refptr<Extension> extension_test =
+      extension_prefs.AddExtension(extension_name);
+
+  DeviceInfo device_info1(
+      base::GenerateGUID(),
+      "abc Device", "XYZ v1", "XYZ SyncAgent v1",
+      sync_pb::SyncEnums_DeviceType_TYPE_LINUX);
+
+  DeviceInfo device_info2(
+      base::GenerateGUID(),
+      "def Device", "XYZ v2", "XYZ SyncAgent v2",
+      sync_pb::SyncEnums_DeviceType_TYPE_LINUX);
+
+  std::vector<DeviceInfo*> devices;
+  devices.push_back(&device_info1);
+  devices.push_back(&device_info2);
+
+  EXPECT_CALL(pss_mock, GetAllSignedInDevicesMock()).
+              WillOnce(Return(&devices));
+
+  ScopedVector<DeviceInfo> output1 = GetAllSignedInDevices(
+      extension_test.get()->id(),
+      &pss_mock,
+      extension_prefs.prefs());
+
+  std::string public_id1 = device_info1.public_id();
+  std::string public_id2 = device_info2.public_id();
+
+  EXPECT_FALSE(public_id1.empty());
+  EXPECT_FALSE(public_id2.empty());
+  EXPECT_NE(public_id1, public_id2);
+
+  // Now clear output1 so its destructor will not destroy the pointers for
+  // |device_info1| and |device_info2|.
+  output1.weak_clear();
+
+  // Add a third device and make sure the first 2 ids are retained and a new
+  // id is generated for the third device.
+  DeviceInfo device_info3(
+      base::GenerateGUID(),
+      "def Device", "jkl v2", "XYZ SyncAgent v2",
+      sync_pb::SyncEnums_DeviceType_TYPE_LINUX);
+
+  devices.push_back(&device_info3);
+
+  EXPECT_CALL(pss_mock, GetAllSignedInDevicesMock()).
+              WillOnce(Return(&devices));
+
+  ScopedVector<DeviceInfo> output2 = GetAllSignedInDevices(
+      extension_test.get()->id(),
+      &pss_mock,
+      extension_prefs.prefs());
+
+  EXPECT_EQ(device_info1.public_id(), public_id1);
+  EXPECT_EQ(device_info2.public_id(), public_id2);
+
+  std::string public_id3 = device_info3.public_id();
+  EXPECT_FALSE(public_id3.empty());
+  EXPECT_NE(public_id3, public_id1);
+  EXPECT_NE(public_id3, public_id2);
+
+  // Now clear output2 so that its destructor does not destroy the
+  // |DeviceInfo| pointers.
+  output2.weak_clear();
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/socket/socket_apitest.cc b/chrome/browser/extensions/api/socket/socket_apitest.cc
index 0d0f8c8..e984bd5 100644
--- a/chrome/browser/extensions/api/socket/socket_apitest.cc
+++ b/chrome/browser/extensions/api/socket/socket_apitest.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "net/dns/mock_host_resolver.h"
@@ -44,14 +43,6 @@
                         new extensions::MockHostResolverCreator()) {
   }
 
-  // We need this while the socket.{listen,accept} methods require the
-  // enable-experimental-extension-apis flag. After that we should remove it,
-  // as well as the "experimental" permission in the test apps' manifests.
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
-  }
-
   virtual void SetUpOnMainThread() OVERRIDE {
     extensions::HostResolverWrapper::GetInstance()->SetHostResolverForTesting(
         resolver_creator_->CreateMockHostResolver());
diff --git a/chrome/browser/extensions/api/storage/settings_test_util.cc b/chrome/browser/extensions/api/storage/settings_test_util.cc
index 3d8d110..07b635e 100644
--- a/chrome/browser/extensions/api/storage/settings_test_util.cc
+++ b/chrome/browser/extensions/api/storage/settings_test_util.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/extensions/api/storage/settings_frontend.h"
 #include "chrome/browser/extensions/extension_system_factory.h"
 #include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/permissions/permissions_data.h"
 
 namespace extensions {
 
diff --git a/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc b/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
index 071d1d0..1d3011f 100644
--- a/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
+++ b/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/message_loop/message_loop.h"
 #include "base/prefs/pref_service.h"
 #include "chrome/browser/download/download_prefs.h"
@@ -14,6 +15,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/extensions/mime_types_handler.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_manager.h"
@@ -26,10 +28,6 @@
 #include "net/test/embedded_test_server/http_response.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::BrowserContext;
 using content::BrowserThread;
 using content::DownloadItem;
@@ -208,7 +206,7 @@
 IN_PROC_BROWSER_TEST_F(StreamsPrivateApiTest, Navigate) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc b/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc
index f0e141e..73c772f 100644
--- a/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc
+++ b/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "chrome/browser/extensions/event_names.h"
@@ -14,6 +15,7 @@
 #include "chrome/browser/sync_file_system/sync_file_system_service.h"
 #include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
 #include "chrome/common/chrome_version_info.h"
+#include "chrome/test/base/test_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "webkit/browser/fileapi/file_system_url.h"
@@ -21,10 +23,6 @@
 #include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
 #include "webkit/browser/quota/quota_manager.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using ::testing::_;
 using ::testing::Eq;
 using ::testing::Ne;
@@ -123,7 +121,7 @@
 IN_PROC_BROWSER_TEST_F(SyncFileSystemApiTest, MAYBE_GetFileStatuses) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/api/system_cpu/system_cpu_apitest.cc b/chrome/browser/extensions/api/system_cpu/system_cpu_apitest.cc
index ba2f913..1448727 100644
--- a/chrome/browser/extensions/api/system_cpu/system_cpu_apitest.cc
+++ b/chrome/browser/extensions/api/system_cpu/system_cpu_apitest.cc
@@ -1,18 +1,14 @@
 // Copyright 2013 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.
-#include "base/command_line.h"
+
 #include "chrome/browser/extensions/api/system_cpu/cpu_info_provider.h"
 #include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/ui_test_utils.h"
 
 namespace extensions {
 
 using api::system_cpu::CpuInfo;
 
-const char kExtensionId[] = "lfakdgdkbaleijdcpbfbngfphpmgfdfn";
-
 class MockCpuInfoProviderImpl : public CpuInfoProvider {
  public:
   MockCpuInfoProviderImpl() {}
@@ -33,12 +29,6 @@
   SystemCpuApiTest() {}
   virtual ~SystemCpuApiTest() {}
 
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(switches::kWhitelistedExtensionID,
-                                    kExtensionId);
-  }
-
   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
     ExtensionApiTest::SetUpInProcessBrowserTestFixture();
   }
diff --git a/chrome/browser/extensions/api/system_indicator/system_indicator_manager.cc b/chrome/browser/extensions/api/system_indicator/system_indicator_manager.cc
index 4297abe..a7601f0 100644
--- a/chrome/browser/extensions/api/system_indicator/system_indicator_manager.cc
+++ b/chrome/browser/extensions/api/system_indicator/system_indicator_manager.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/extensions/api/system_indicator/system_indicator_manager.h"
 
 #include "base/memory/linked_ptr.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/event_names.h"
 #include "chrome/browser/extensions/event_router.h"
@@ -30,11 +31,10 @@
 class ExtensionIndicatorIcon : public StatusIconObserver,
                                public ExtensionActionIconFactory::Observer {
  public:
-  ExtensionIndicatorIcon(const Extension* extension,
-                         const ExtensionAction* action,
-                         Profile* profile,
-                         StatusTray* status_tray,
-                         StatusIcon* status_icon);
+  static ExtensionIndicatorIcon* Create(const Extension* extension,
+                                        const ExtensionAction* action,
+                                        Profile* profile,
+                                        StatusTray* status_tray);
   virtual ~ExtensionIndicatorIcon();
 
   // StatusIconObserver implementation.
@@ -44,6 +44,11 @@
   virtual void OnIconUpdated() OVERRIDE;
 
  private:
+  ExtensionIndicatorIcon(const Extension* extension,
+                         const ExtensionAction* action,
+                         Profile* profile,
+                         StatusTray* status_tray);
+
   const extensions::Extension* extension_;
   StatusTray* status_tray_;
   StatusIcon* icon_;
@@ -51,23 +56,27 @@
   ExtensionActionIconFactory icon_factory_;
 };
 
-ExtensionIndicatorIcon::ExtensionIndicatorIcon(const Extension* extension,
-                                               const ExtensionAction* action,
-                                               Profile* profile,
-                                               StatusTray* status_tray,
-                                               StatusIcon* status_icon)
-    : extension_(extension),
-      status_tray_(status_tray),
-      icon_(status_icon),
-      profile_(profile),
-      icon_factory_(profile, extension, action, this) {
-  icon_->AddObserver(this);
-  OnIconUpdated();
+ExtensionIndicatorIcon* ExtensionIndicatorIcon::Create(
+    const Extension* extension,
+    const ExtensionAction* action,
+    Profile* profile,
+    StatusTray* status_tray) {
+  scoped_ptr<ExtensionIndicatorIcon> extension_icon(
+      new ExtensionIndicatorIcon(extension, action, profile, status_tray));
+
+  // Check if a status icon was successfully created.
+  if (extension_icon->icon_)
+    return extension_icon.release();
+
+  // We could not create a status icon.
+  return NULL;
 }
 
 ExtensionIndicatorIcon::~ExtensionIndicatorIcon() {
-  icon_->RemoveObserver(this);
-  status_tray_->RemoveStatusIcon(icon_);
+  if (icon_) {
+    icon_->RemoveObserver(this);
+    status_tray_->RemoveStatusIcon(icon_);
+  }
 }
 
 void ExtensionIndicatorIcon::OnStatusIconClicked() {
@@ -89,10 +98,30 @@
       icon_factory_.GetIcon(ExtensionAction::kDefaultTabId).AsImageSkia());
 }
 
+ExtensionIndicatorIcon::ExtensionIndicatorIcon(const Extension* extension,
+                                               const ExtensionAction* action,
+                                               Profile* profile,
+                                               StatusTray* status_tray)
+    : extension_(extension),
+      status_tray_(status_tray),
+      icon_(NULL),
+      profile_(profile),
+      icon_factory_(profile, extension, action, this) {
+  // Get the icon image and tool tip for the status icon. The extension name is
+  // used as the tool tip.
+  gfx::ImageSkia icon_image =
+      icon_factory_.GetIcon(ExtensionAction::kDefaultTabId).AsImageSkia();
+  string16 tool_tip = UTF8ToUTF16(extension_->name());
+
+  icon_ = status_tray_->CreateStatusIcon(
+      StatusTray::OTHER_ICON, icon_image, tool_tip);
+  if (icon_)
+    icon_->AddObserver(this);
+}
+
 SystemIndicatorManager::SystemIndicatorManager(Profile* profile,
                                                StatusTray* status_tray)
-    : profile_(profile),
-      status_tray_(status_tray) {
+    : profile_(profile), status_tray_(status_tray) {
   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
                  content::Source<Profile>(profile_->GetOriginalProfile()));
   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_SYSTEM_INDICATOR_UPDATED,
@@ -167,17 +196,10 @@
     return;
   }
 
-  StatusIcon* indicator_icon =
-      status_tray_->CreateStatusIcon(StatusTray::OTHER_ICON);
-  if (indicator_icon != NULL) {
-    ExtensionIndicatorIcon* status_icon = new ExtensionIndicatorIcon(
-        extension,
-        extension_action,
-        profile_,
-        status_tray_,
-        indicator_icon);
-    system_indicators_[extension->id()] = make_linked_ptr(status_icon);
-  }
+  ExtensionIndicatorIcon* extension_icon = ExtensionIndicatorIcon::Create(
+      extension, extension_action, profile_, status_tray_);
+  if (extension_icon)
+    system_indicators_[extension->id()] = make_linked_ptr(extension_icon);
 }
 
 void SystemIndicatorManager::RemoveIndicator(const std::string& extension_id) {
diff --git a/chrome/browser/extensions/api/system_info/system_info_api.cc b/chrome/browser/extensions/api/system_info/system_info_api.cc
index e3e8181..22b7fde 100644
--- a/chrome/browser/extensions/api/system_info/system_info_api.cc
+++ b/chrome/browser/extensions/api/system_info/system_info_api.cc
@@ -7,7 +7,6 @@
 #include <set>
 
 #include "base/bind.h"
-#include "base/files/file_path.h"
 #include "base/lazy_instance.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/singleton.h"
@@ -32,16 +31,10 @@
 
 using api::system_storage::StorageFreeSpaceChangeInfo;
 using api::system_storage::StorageUnitInfo;
-using api::system_storage::StorageUnitType;
 using content::BrowserThread;
 
 namespace {
 
-// The display events use the "systemInfo" prefix.
-const char kSystemInfoEventPrefix[] = "systemInfo";
-// The storage events still use the "experimental.systemInfo" prefix.
-const char kExperimentalSystemInfoEventPrefix[] = "experimental.systemInfo";
-
 bool IsDisplayChangedEvent(const std::string& event_name) {
   return event_name == event_names::kOnDisplayChanged;
 }
@@ -65,9 +58,6 @@
   void AddEventListener(const std::string& event_name);
   void RemoveEventListener(const std::string& event_name);
 
-  // Return true if the |event_name| is an event from systemInfo namespace.
-  static bool IsSystemInfoEvent(const std::string& event_name);
-
  private:
   // StorageFreeSpaceObserver:
   virtual void OnFreeSpaceChanged(const std::string& id,
@@ -191,14 +181,6 @@
   }
 }
 
-// static
-bool SystemInfoEventRouter::IsSystemInfoEvent(const std::string& event_name) {
-  // TODO(hshi): simplify this once all systemInfo APIs are out of experimental.
-  return (StartsWithASCII(event_name, kSystemInfoEventPrefix, true) ||
-          StartsWithASCII(event_name, kExperimentalSystemInfoEventPrefix,
-                          true));
-}
-
 // Called on UI thread since the observer is added from UI thread.
 void SystemInfoEventRouter::OnFreeSpaceChanged(
     const std::string& transient_id, double new_value, double old_value) {
@@ -224,7 +206,7 @@
 void SystemInfoEventRouter::OnRemovableStorageDetached(
     const chrome::StorageInfo& info) {
   scoped_ptr<base::ListValue> args(new base::ListValue);
-  args->Append(new base::StringValue(StorageInfoProvider::Get()->
+  args->Append(new base::StringValue(chrome::StorageMonitor::GetInstance()->
                    GetTransientIdForDeviceId(info.device_id())));
 
   DispatchEvent(event_names::kOnStorageDetached, args.Pass());
diff --git a/chrome/browser/extensions/api/system_memory/system_memory_apitest.cc b/chrome/browser/extensions/api/system_memory/system_memory_apitest.cc
index 9440074..9f4e078 100644
--- a/chrome/browser/extensions/api/system_memory/system_memory_apitest.cc
+++ b/chrome/browser/extensions/api/system_memory/system_memory_apitest.cc
@@ -2,20 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/command_line.h"
 #include "base/message_loop/message_loop.h"
 #include "chrome/browser/extensions/api/system_memory/memory_info_provider.h"
 #include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/browser/extensions/extension_test_message_listener.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/ui_test_utils.h"
 
 namespace extensions {
 
 using api::system_memory::MemoryInfo;
 
-const char kExtensionId[] = "lfmcnjhchhgejbpbonjobnlbcgcnmjif";
-
 class MockMemoryInfoProviderImpl : public MemoryInfoProvider {
  public:
   MockMemoryInfoProviderImpl() {}
@@ -34,12 +28,6 @@
   SystemMemoryApiTest() {}
   virtual ~SystemMemoryApiTest() {}
 
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(switches::kWhitelistedExtensionID,
-                                    kExtensionId);
-  }
-
   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
     ExtensionApiTest::SetUpInProcessBrowserTestFixture();
     message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_UI));
diff --git a/chrome/browser/extensions/api/system_storage/storage_info_provider.cc b/chrome/browser/extensions/api/system_storage/storage_info_provider.cc
index 304fb35..0138893 100644
--- a/chrome/browser/extensions/api/system_storage/storage_info_provider.cc
+++ b/chrome/browser/extensions/api/system_storage/storage_info_provider.cc
@@ -21,13 +21,9 @@
 
 namespace systeminfo {
 
-const char kStorageTypeUnknown[] = "unknown";
-const char kStorageTypeFixed[] = "fixed";
-const char kStorageTypeRemovable[] = "removable";
-
 void BuildStorageUnitInfo(const chrome::StorageInfo& info,
                           StorageUnitInfo* unit) {
-  unit->id = StorageInfoProvider::Get()->GetTransientIdForDeviceId(
+  unit->id = StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
                  info.device_id());
   unit->name = UTF16ToUTF8(info.name());
   // TODO(hmin): Might need to take MTP device into consideration.
@@ -52,6 +48,11 @@
       watching_interval_(kDefaultPollingIntervalMs) {
 }
 
+StorageInfoProvider::StorageInfoProvider(size_t watching_interval)
+    : observers_(new ObserverListThreadSafe<StorageFreeSpaceObserver>()),
+      watching_interval_(watching_interval) {
+}
+
 StorageInfoProvider::~StorageInfoProvider() {
 }
 
@@ -80,13 +81,11 @@
   return true;
 }
 
-std::vector<chrome::StorageInfo> StorageInfoProvider::GetAllStorages() const {
-  return StorageMonitor::GetInstance()->GetAllAvailableStorages();
-}
-
 void StorageInfoProvider::GetAllStoragesIntoInfoList() {
   info_.clear();
-  std::vector<chrome::StorageInfo> storage_list = GetAllStorages();
+  std::vector<chrome::StorageInfo> storage_list =
+      StorageMonitor::GetInstance()->GetAllAvailableStorages();
+
   std::vector<chrome::StorageInfo>::const_iterator it = storage_list.begin();
   for (; it != storage_list.end(); ++it) {
     linked_ptr<StorageUnitInfo> unit(new StorageUnitInfo());
@@ -138,8 +137,12 @@
 
 int64 StorageInfoProvider::GetStorageFreeSpaceFromTransientId(
     const std::string& transient_id) {
-  std::vector<chrome::StorageInfo> storage_list = GetAllStorages();
-  std::string device_id = GetDeviceIdForTransientId(transient_id);
+  std::vector<chrome::StorageInfo> storage_list =
+      StorageMonitor::GetInstance()->GetAllAvailableStorages();
+
+  std::string device_id =
+      StorageMonitor::GetInstance()->GetDeviceIdForTransientId(
+          transient_id);
 
   // Lookup the matched storage info by |device_id|.
   for (std::vector<chrome::StorageInfo>::const_iterator it =
@@ -189,7 +192,8 @@
 
 void StorageInfoProvider::CheckWatchedStorages() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  std::vector<chrome::StorageInfo> storage_list = GetAllStorages();
+  std::vector<chrome::StorageInfo> storage_list =
+      StorageMonitor::GetInstance()->GetAllAvailableStorages();
 
   for (std::vector<chrome::StorageInfo>::iterator it = storage_list.begin();
        it != storage_list.end(); ++it) {
@@ -197,7 +201,9 @@
         kWatchingTokenName,
         FROM_HERE,
         base::Bind(&StorageInfoProvider::CheckWatchedStorageOnBlockingPool,
-                   this, GetTransientIdForDeviceId(it->device_id())));
+                   this,
+                   StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
+                       it->device_id())));
   }
 }
 
@@ -252,16 +258,6 @@
   watching_timer_.Stop();
 }
 
-std::string StorageInfoProvider::GetTransientIdForDeviceId(
-    const std::string& device_id) const {
-  return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id);
-}
-
-std::string StorageInfoProvider::GetDeviceIdForTransientId(
-    const std::string& transient_id) const {
-  return StorageMonitor::GetInstance()->GetDeviceIdForTransientId(transient_id);
-}
-
 // static
 StorageInfoProvider* StorageInfoProvider::Get() {
   return StorageInfoProvider::GetInstance<StorageInfoProvider>();
diff --git a/chrome/browser/extensions/api/system_storage/storage_info_provider.h b/chrome/browser/extensions/api/system_storage/storage_info_provider.h
index 61acc32..33c8c67 100644
--- a/chrome/browser/extensions/api/system_storage/storage_info_provider.h
+++ b/chrome/browser/extensions/api/system_storage/storage_info_provider.h
@@ -1,6 +1,7 @@
 // Copyright 2013 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.
+
 #ifndef CHROME_BROWSER_EXTENSIONS_API_SYSTEM_STORAGE_STORAGE_INFO_PROVIDER_H_
 #define CHROME_BROWSER_EXTENSIONS_API_SYSTEM_STORAGE_STORAGE_INFO_PROVIDER_H_
 
@@ -21,10 +22,6 @@
 
 namespace systeminfo {
 
-extern const char kStorageTypeUnknown[];
-extern const char kStorageTypeFixed[];
-extern const char kStorageTypeRemovable[];
-
 // Build StorageUnitInfo struct from chrome::StorageInfo instance. The |unit|
 // parameter is the output value.
 void BuildStorageUnitInfo(const chrome::StorageInfo& info,
@@ -40,6 +37,8 @@
  public:
   StorageInfoProvider();
 
+  explicit StorageInfoProvider(size_t watching_interval);
+
   // Get the single shared instance of StorageInfoProvider.
   static StorageInfoProvider* Get();
 
@@ -55,9 +54,6 @@
   void StartWatchingAllStorages();
   void StopWatchingAllStorages();
 
-  // Returns all available storages, including fixed and removable.
-  virtual std::vector<chrome::StorageInfo> GetAllStorages() const;
-
   // SystemInfoProvider implementations
   virtual void PrepareQueryOnUIThread() OVERRIDE;
   virtual void InitializeProvider(const base::Closure& do_query_info_callback)
@@ -67,21 +63,13 @@
   virtual int64 GetStorageFreeSpaceFromTransientId(
       const std::string& transient_id);
 
-  virtual std::string GetTransientIdForDeviceId(
-      const std::string& device_id) const;
-  virtual std::string GetDeviceIdForTransientId(
-      const std::string& transient_id) const;
-
   const StorageUnitInfoList& storage_unit_info_list() const;
 
  protected:
   virtual ~StorageInfoProvider();
 
-  // TODO(Haojian): Put this method in a testing subclass rather than here.
-  void SetWatchingIntervalForTesting(size_t ms) { watching_interval_ = ms; }
-
   // Put all available storages' information into |info_|.
-  virtual void GetAllStoragesIntoInfoList();
+  void GetAllStoragesIntoInfoList();
 
  private:
   typedef std::map<std::string, double> StorageTransientIdToSizeMap;
diff --git a/chrome/browser/extensions/api/system_storage/storage_info_provider_unittest.cc b/chrome/browser/extensions/api/system_storage/storage_info_provider_unittest.cc
index ec27614..27896be 100644
--- a/chrome/browser/extensions/api/system_storage/storage_info_provider_unittest.cc
+++ b/chrome/browser/extensions/api/system_storage/storage_info_provider_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/stl_util.h"
 #include "chrome/browser/extensions/api/system_storage/storage_info_provider.h"
 #include "chrome/browser/extensions/api/system_storage/test_storage_info_provider.h"
+#include "chrome/browser/storage_monitor/storage_monitor.h"
 #include "chrome/browser/storage_monitor/test_storage_monitor.h"
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_utils.h"
@@ -21,21 +22,19 @@
 using api::system_storage::StorageUnitType;
 using base::MessageLoop;
 using chrome::test::TestStorageMonitor;
+using chrome::StorageMonitor;
 using content::BrowserThread;
 using content::RunAllPendingInMessageLoop;
 using content::RunMessageLoop;
+using test::TestStorageUnitInfo;
 using testing::Return;
 using testing::_;
 
 const struct TestStorageUnitInfo kTestingData[] = {
-  {"device:001", "transient:01", "C:", systeminfo::kStorageTypeUnknown,
-    1000, 10, 0},
-  {"device:002", "transient:02", "d:", systeminfo::kStorageTypeRemovable,
-    2000, 10, 1},
-  {"device:003", "transient:03", "/home", systeminfo::kStorageTypeFixed,
-    3000, 10, 2},
-  {"device:004", "transient:04", "/", systeminfo::kStorageTypeRemovable,
-    4000, 10, 3}
+  {"path:device:001", "C:", 1000, 10, 0},
+  {"path:device:002", "d:", 2000, 10, 1},
+  {"path:device:003", "/home", 3000, 10, 2},
+  {"path:device:004", "/", 4000, 10, 3}
 };
 
 // The watching interval for unit test is 1 milliseconds.
@@ -75,9 +74,11 @@
     // The observer is added on UI thread, so the callback should be also
     // called on UI thread.
     ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    std::string device_id =
+        StorageMonitor::GetInstance()->GetDeviceIdForTransientId(transient_id);
     size_t i = 0;
     for (; i < testing_data_.size(); ++i) {
-      if (testing_data_[i].transient_id == transient_id) {
+      if (testing_data_[i].device_id == device_id) {
         EXPECT_DOUBLE_EQ(new_value-old_value, testing_data_[i].change_step);
         ++change_times_;
         break;
@@ -93,7 +94,7 @@
   int change_times_;
 };
 
-class UnitTestStorageInfoProvider : public TestStorageInfoProvider {
+class UnitTestStorageInfoProvider : public test::TestStorageInfoProvider {
  public :
   UnitTestStorageInfoProvider(const struct TestStorageUnitInfo* testing_data,
                               size_t n)
@@ -107,8 +108,10 @@
   virtual int64 GetStorageFreeSpaceFromTransientId(
       const std::string& transient_id) OVERRIDE {
     int64 available_capacity = -1;
+    std::string device_id =
+        StorageMonitor::GetInstance()->GetDeviceIdForTransientId(transient_id);
     for (size_t i = 0; i < testing_data_.size(); ++i) {
-      if (testing_data_[i].transient_id == transient_id) {
+      if (testing_data_[i].device_id == device_id) {
         available_capacity = testing_data_[i].available_capacity;
         // We simulate free space change by increasing the |available_capacity|
         // with a fixed change step.
@@ -144,6 +147,14 @@
   virtual void SetUp() OVERRIDE;
   virtual void TearDown() OVERRIDE;
 
+  void SetUpAllMockStorageDevices() {
+    for (size_t i = 0; i < arraysize(kTestingData); ++i) {
+      StorageMonitor::GetInstance()->receiver()->ProcessAttach(
+          extensions::test::BuildStorageInfoFromTestStorageUnitInfo(
+              kTestingData[i]));
+    }
+  }
+
   // Run message loop and flush blocking pool to make sure there is no pending
   // tasks on blocking pool.
   static void RunLoopAndFlushBlockingPool();
@@ -152,7 +163,6 @@
   base::MessageLoop message_loop_;
   content::TestBrowserThread ui_thread_;
   scoped_refptr<UnitTestStorageInfoProvider> storage_info_provider_;
-  scoped_ptr<TestStorageMonitor> storage_test_notifications_;
 };
 
 StorageInfoProviderTest::StorageInfoProviderTest()
@@ -165,7 +175,8 @@
 
 void StorageInfoProviderTest::SetUp() {
   ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  storage_test_notifications_.reset(new TestStorageMonitor);
+  chrome::test::TestStorageMonitor::CreateAndInstall();
+  SetUpAllMockStorageDevices();
   storage_info_provider_ = new UnitTestStorageInfoProvider(
       kTestingData, arraysize(kTestingData));
 }
@@ -188,14 +199,17 @@
   // Case 1: watching a storage that the free space is not changed.
   MockStorageObserver observer;
   storage_info_provider_->AddObserver(&observer);
-  storage_info_provider_->StartWatching(kTestingData[0].transient_id);
-  EXPECT_CALL(observer, OnFreeSpaceChanged(kTestingData[0].transient_id, _, _))
+  std::string transient_id =
+      StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
+          kTestingData[0].device_id);
+  storage_info_provider_->StartWatching(transient_id);
+  EXPECT_CALL(observer, OnFreeSpaceChanged(transient_id, _, _))
       .Times(0);
 
   RunLoopAndFlushBlockingPool();
 
   storage_info_provider_->RemoveObserver(&observer);
-  storage_info_provider_->StopWatching(kTestingData[0].device_id);
+  storage_info_provider_->StopWatching(transient_id);
   RunAllPendingAndFlushBlockingPool();
 }
 
@@ -203,10 +217,13 @@
   // Case 2: only watching one storage.
   TestStorageObserver observer;
   storage_info_provider_->AddObserver(&observer);
-  storage_info_provider_->StartWatching(kTestingData[1].transient_id);
+  std::string transient_id =
+    StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
+        kTestingData[1].device_id);
+  storage_info_provider_->StartWatching(transient_id);
   RunLoopAndFlushBlockingPool();
 
-  storage_info_provider_->StopWatching(kTestingData[1].transient_id);
+  storage_info_provider_->StopWatching(transient_id);
   // Give a chance to run StopWatching task on the blocking pool.
   RunAllPendingAndFlushBlockingPool();
 
@@ -214,7 +231,7 @@
   storage_info_provider_->AddObserver(&mock_observer);
   // The watched storage won't get free space change notification.
   EXPECT_CALL(mock_observer,
-      OnFreeSpaceChanged(kTestingData[1].transient_id, _, _)).Times(0);
+      OnFreeSpaceChanged(transient_id, _, _)).Times(0);
   RunAllPendingAndFlushBlockingPool();
 
   storage_info_provider_->RemoveObserver(&observer);
@@ -228,29 +245,44 @@
   storage_info_provider_->AddObserver(&observer);
 
   for (size_t k = 1; k < arraysize(kTestingData); ++k) {
-    storage_info_provider_->StartWatching(kTestingData[k].transient_id);
+    std::string transient_id =
+        StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
+            kTestingData[k].device_id);
+    storage_info_provider_->StartWatching(transient_id);
   }
   RunLoopAndFlushBlockingPool();
 
   // Stop watching the first storage.
-  storage_info_provider_->StopWatching(kTestingData[1].transient_id);
+  storage_info_provider_->StopWatching(
+      StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
+          kTestingData[1].device_id));
   RunAllPendingAndFlushBlockingPool();
 
   MockStorageObserver mock_observer;
   storage_info_provider_->AddObserver(&mock_observer);
   for (size_t k = 2; k < arraysize(kTestingData); ++k) {
+    std::string transient_id =
+        StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
+            kTestingData[k].device_id);
     EXPECT_CALL(mock_observer,
-        OnFreeSpaceChanged(kTestingData[k].transient_id,  _, _))
+        OnFreeSpaceChanged(transient_id,  _, _))
         .WillRepeatedly(Return());
   }
 
   // After stopping watching, the observer won't get change notification.
   EXPECT_CALL(mock_observer,
-      OnFreeSpaceChanged(kTestingData[1].transient_id, _, _)).Times(0);
+      OnFreeSpaceChanged(
+          StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
+              kTestingData[1].device_id),
+          _, _))
+          .Times(0);
   RunLoopAndFlushBlockingPool();
 
   for (size_t k = 1; k < arraysize(kTestingData); ++k) {
-    storage_info_provider_->StopWatching(kTestingData[k].transient_id);
+    std::string transient_id =
+        StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
+            kTestingData[k].device_id);
+    storage_info_provider_->StopWatching(transient_id);
   }
   RunAllPendingAndFlushBlockingPool();
   storage_info_provider_->RemoveObserver(&observer);
@@ -259,12 +291,12 @@
 
 TEST_F(StorageInfoProviderTest, WatchingInvalidStorage) {
   // Case 3: watching an invalid storage.
-  std::string invalid_id("invalid_id");
+  std::string invalid_device_id("invalid_id");
   MockStorageObserver mock_observer;
   storage_info_provider_->AddObserver(&mock_observer);
-  storage_info_provider_->StartWatching(invalid_id);
+  storage_info_provider_->StartWatching(invalid_device_id);
   EXPECT_CALL(mock_observer,
-      OnFreeSpaceChanged(invalid_id, _, _)).Times(0);
+      OnFreeSpaceChanged(invalid_device_id, _, _)).Times(0);
   RunAllPendingAndFlushBlockingPool();
   storage_info_provider_->RemoveObserver(&mock_observer);
 }
diff --git a/chrome/browser/extensions/api/system_storage/system_storage_api.cc b/chrome/browser/extensions/api/system_storage/system_storage_api.cc
index 0efeb91..db28ad8 100644
--- a/chrome/browser/extensions/api/system_storage/system_storage_api.cc
+++ b/chrome/browser/extensions/api/system_storage/system_storage_api.cc
@@ -54,7 +54,7 @@
   DCHECK(chrome::StorageMonitor::GetInstance()->IsInitialized());
   chrome::StorageMonitor* monitor = chrome::StorageMonitor::GetInstance();
   std::string device_id_str =
-      StorageInfoProvider::Get()->GetDeviceIdForTransientId(
+      chrome::StorageMonitor::GetInstance()->GetDeviceIdForTransientId(
           transient_device_id);
 
   if (device_id_str == "") {
diff --git a/chrome/browser/extensions/api/system_storage/system_storage_apitest.cc b/chrome/browser/extensions/api/system_storage/system_storage_apitest.cc
index 33198b6..a56461e 100644
--- a/chrome/browser/extensions/api/system_storage/system_storage_apitest.cc
+++ b/chrome/browser/extensions/api/system_storage/system_storage_apitest.cc
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/command_line.h"
+#include <vector>
+
 #include "base/message_loop/message_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/api/system_storage/storage_info_provider.h"
@@ -11,8 +12,7 @@
 #include "chrome/browser/extensions/extension_test_message_listener.h"
 #include "chrome/browser/storage_monitor/storage_info.h"
 #include "chrome/browser/storage_monitor/storage_monitor.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/browser/storage_monitor/test_storage_monitor.h"
 
 namespace {
 
@@ -21,24 +21,14 @@
 using extensions::api::system_storage::StorageUnitInfo;
 using extensions::StorageInfoProvider;
 using extensions::StorageUnitInfoList;
-using extensions::systeminfo::kStorageTypeFixed;
-using extensions::systeminfo::kStorageTypeRemovable;
-using extensions::systeminfo::kStorageTypeUnknown;
-using extensions::TestStorageUnitInfo;
-using extensions::TestStorageInfoProvider;
+using extensions::test::TestStorageUnitInfo;
+using extensions::test::TestStorageInfoProvider;
+using extensions::test::kRemovableStorageData;
 
 const struct TestStorageUnitInfo kTestingData[] = {
-  {"dcim:device:0004", "transient:0004", "0xbeaf", kStorageTypeUnknown,
-    4098, 1000, 0},
-  {"path:device:002", "transient:002", "/home", kStorageTypeFixed,
-    4098, 1000, 10},
-  {"path:device:003", "transient:003", "/data", kStorageTypeFixed,
-    10000, 1000, 4097}
-};
-
-const struct TestStorageUnitInfo kRemovableStorageData[] = {
-  {"dcim:device:0004", "transient:0004", "/media/usb1",
-    kStorageTypeRemovable, 4098, 1000, 1}
+  {"dcim:device:001", "0xbeaf", 4098, 1000, 0},
+  {"path:device:002", "/home", 4098, 1000, 10},
+  {"path:device:003", "/data", 10000, 1000, 4097}
 };
 
 }  // namespace
@@ -48,9 +38,8 @@
   SystemStorageApiTest() {}
   virtual ~SystemStorageApiTest() {}
 
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
+  virtual void SetUpOnMainThread() OVERRIDE {
+    chrome::test::TestStorageMonitor::CreateForBrowserTests();
   }
 
   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
@@ -58,16 +47,19 @@
     message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_UI));
   }
 
-  void AttachRemovableStorage(const std::string& device_id) {
-    for (size_t i = 0; i < arraysize(kRemovableStorageData); ++i) {
-      if (kRemovableStorageData[i].device_id != device_id)
-        continue;
-
-      StorageMonitor::GetInstance()->receiver()->ProcessAttach(
-          TestStorageInfoProvider::BuildStorageInfo(kRemovableStorageData[i]));
+  void SetUpAllMockStorageDevices() {
+    for (size_t i = 0; i < arraysize(kTestingData); ++i) {
+      AttachRemovableStorage(kTestingData[i]);
     }
   }
 
+  void AttachRemovableStorage(
+      const struct TestStorageUnitInfo& removable_storage_info) {
+    StorageMonitor::GetInstance()->receiver()->ProcessAttach(
+        extensions::test::BuildStorageInfoFromTestStorageUnitInfo(
+            removable_storage_info));
+  }
+
   void DetachRemovableStorage(const std::string& id) {
     StorageMonitor::GetInstance()->receiver()->ProcessDetach(id);
   }
@@ -77,31 +69,48 @@
 };
 
 IN_PROC_BROWSER_TEST_F(SystemStorageApiTest, Storage) {
+  SetUpAllMockStorageDevices();
   TestStorageInfoProvider* provider =
-      new TestStorageInfoProvider(kTestingData, arraysize(kTestingData));
+      new TestStorageInfoProvider(kTestingData,
+                                  arraysize(kTestingData));
   StorageInfoProvider::InitializeForTesting(provider);
+  std::vector<linked_ptr<ExtensionTestMessageListener> > device_ids_listeners;
+  for (size_t i = 0; i < arraysize(kTestingData); ++i) {
+    linked_ptr<ExtensionTestMessageListener> listener(
+        new ExtensionTestMessageListener(
+            StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
+                kTestingData[i].device_id),
+            false));
+    device_ids_listeners.push_back(listener);
+  }
   ASSERT_TRUE(RunPlatformAppTest("system/storage")) << message_;
+  for (size_t i = 0; i < device_ids_listeners.size(); ++i)
+    EXPECT_TRUE(device_ids_listeners[i]->WaitUntilSatisfied());
 }
 
 IN_PROC_BROWSER_TEST_F(SystemStorageApiTest, StorageAttachment) {
-  TestStorageInfoProvider* provider =
-      new TestStorageInfoProvider(kRemovableStorageData,
-                                  arraysize(kRemovableStorageData));
-  StorageInfoProvider::InitializeForTesting(provider);
-
   ResultCatcher catcher;
   ExtensionTestMessageListener attach_listener("attach", false);
   ExtensionTestMessageListener detach_listener("detach", false);
 
   EXPECT_TRUE(LoadExtension(
       test_data_dir_.AppendASCII("system/storage_attachment")));
-
   // Simulate triggering onAttached event.
   ASSERT_TRUE(attach_listener.WaitUntilSatisfied());
-  AttachRemovableStorage(kRemovableStorageData[0].device_id);
+
+  AttachRemovableStorage(kRemovableStorageData);
+
+  std::string removable_storage_transient_id =
+      StorageMonitor::GetInstance()->GetTransientIdForDeviceId(
+          kRemovableStorageData.device_id);
+  ExtensionTestMessageListener detach_device_id_listener(
+      removable_storage_transient_id, false);
+
   // Simulate triggering onDetached event.
   ASSERT_TRUE(detach_listener.WaitUntilSatisfied());
-  DetachRemovableStorage(kRemovableStorageData[0].device_id);
+  DetachRemovableStorage(kRemovableStorageData.device_id);
+
+  ASSERT_TRUE(detach_device_id_listener.WaitUntilSatisfied());
 
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
diff --git a/chrome/browser/extensions/api/system_storage/system_storage_eject_apitest.cc b/chrome/browser/extensions/api/system_storage/system_storage_eject_apitest.cc
index dd8589d..c91ca29 100644
--- a/chrome/browser/extensions/api/system_storage/system_storage_eject_apitest.cc
+++ b/chrome/browser/extensions/api/system_storage/system_storage_eject_apitest.cc
@@ -16,20 +16,15 @@
 #include "chrome/browser/storage_monitor/storage_info.h"
 #include "chrome/browser/storage_monitor/storage_monitor.h"
 #include "chrome/browser/storage_monitor/test_storage_monitor.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/test/test_utils.h"
 
 namespace {
 
-using extensions::TestStorageUnitInfo;
-using extensions::TestStorageInfoProvider;
-
-struct TestStorageUnitInfo kRemovableStorageData[] = {
-  { "dcim:device:0004", "transient:0004", "/media/usb1",
-    extensions::systeminfo::kStorageTypeRemovable, 0, 0, 0}
-};
+using extensions::test::TestStorageUnitInfo;
+using extensions::test::TestStorageInfoProvider;
+using extensions::test::kRemovableStorageData;
 
 }  // namespace
 
@@ -39,12 +34,6 @@
   virtual ~SystemStorageEjectApiTest() {}
 
  protected:
-  // ExtensionApiTest overrides.
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
-  }
-
   virtual void SetUpOnMainThread() OVERRIDE {
     monitor_ = chrome::test::TestStorageMonitor::CreateForBrowserTests();
     ExtensionApiTest::SetUpOnMainThread();
@@ -69,14 +58,15 @@
   void Attach() {
     DCHECK(chrome::StorageMonitor::GetInstance()->IsInitialized());
     chrome::StorageMonitor::GetInstance()->receiver()->ProcessAttach(
-        TestStorageInfoProvider::BuildStorageInfo(kRemovableStorageData[0]));
+        extensions::test::BuildStorageInfoFromTestStorageUnitInfo(
+            kRemovableStorageData));
     content::RunAllPendingInMessageLoop();
   }
 
   void Detach() {
     DCHECK(chrome::StorageMonitor::GetInstance()->IsInitialized());
     chrome::StorageMonitor::GetInstance()->receiver()->ProcessDetach(
-        kRemovableStorageData[0].device_id);
+        kRemovableStorageData.device_id);
     content::RunAllPendingInMessageLoop();
   }
 
@@ -89,35 +79,25 @@
 
 
 IN_PROC_BROWSER_TEST_F(SystemStorageEjectApiTest, EjectTest) {
-  TestStorageInfoProvider* provider =
-      new TestStorageInfoProvider(kRemovableStorageData,
-                                  arraysize(kRemovableStorageData));
-  extensions::StorageInfoProvider::InitializeForTesting(provider);
-
   content::RenderViewHost* host = GetHost();
   ExecuteCmdAndCheckReply(host, "addAttachListener()", "add_attach_ok");
 
   // Attach / detach
   const std::string expect_attach_msg =
       base::StringPrintf("%s,%s", "attach_test_ok",
-                         kRemovableStorageData[0].name);
+                         kRemovableStorageData.name);
   ExtensionTestMessageListener attach_finished_listener(expect_attach_msg,
                                                         false  /* no reply */);
   Attach();
   EXPECT_TRUE(attach_finished_listener.WaitUntilSatisfied());
 
   ExecuteCmdAndCheckReply(host, "ejectTest()", "eject_ok");
-  EXPECT_EQ(kRemovableStorageData[0].device_id, monitor_->ejected_device());
+  EXPECT_EQ(kRemovableStorageData.device_id, monitor_->ejected_device());
 
   Detach();
 }
 
 IN_PROC_BROWSER_TEST_F(SystemStorageEjectApiTest, EjectBadDeviceTest) {
-  TestStorageInfoProvider* provider =
-      new TestStorageInfoProvider(kRemovableStorageData,
-                                  arraysize(kRemovableStorageData));
-  extensions::StorageInfoProvider::InitializeForTesting(provider);
-
   ExecuteCmdAndCheckReply(GetHost(), "ejectFailTest()", "eject_no_such_device");
 
   EXPECT_EQ("", monitor_->ejected_device());
diff --git a/chrome/browser/extensions/api/system_storage/test_storage_info_provider.cc b/chrome/browser/extensions/api/system_storage/test_storage_info_provider.cc
index 17ec94e..3a3e0eb 100644
--- a/chrome/browser/extensions/api/system_storage/test_storage_info_provider.cc
+++ b/chrome/browser/extensions/api/system_storage/test_storage_info_provider.cc
@@ -5,31 +5,24 @@
 #include "chrome/browser/extensions/api/system_storage/test_storage_info_provider.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/storage_monitor/storage_monitor.h"
 
 namespace extensions {
+namespace test {
 
 using api::system_storage::ParseStorageUnitType;
 using api::system_storage::StorageUnitInfo;
-using systeminfo::kStorageTypeFixed;
-using systeminfo::kStorageTypeRemovable;
-using systeminfo::kStorageTypeUnknown;
 
 // Watching interval for testing.
 const size_t kTestingIntervalMS = 10;
 
-TestStorageInfoProvider::TestStorageInfoProvider(
-    const struct TestStorageUnitInfo* testing_data, size_t n)
-      : testing_data_(testing_data, testing_data + n) {
-    SetWatchingIntervalForTesting(kTestingIntervalMS);
-}
+const struct TestStorageUnitInfo kRemovableStorageData = {
+    "dcim:device:001", "/media/usb1", 4098, 1000, 1
+};
 
-TestStorageInfoProvider::~TestStorageInfoProvider() {
-}
-
-// static
-chrome::StorageInfo TestStorageInfoProvider::BuildStorageInfo(
+chrome::StorageInfo BuildStorageInfoFromTestStorageUnitInfo(
     const TestStorageUnitInfo& unit) {
-  chrome::StorageInfo info(
+  return chrome::StorageInfo(
       unit.device_id,
       UTF8ToUTF16(unit.name),
       base::FilePath::StringType(), /* no location */
@@ -37,35 +30,25 @@
       string16(), /* no storage vendor */
       string16(), /* no storage model */
       unit.capacity);
-  return info;
 }
 
-void TestStorageInfoProvider::GetAllStoragesIntoInfoList() {
-  info_.clear();
-  for (size_t i = 0; i < testing_data_.size(); ++i) {
-    linked_ptr<StorageUnitInfo> unit(new StorageUnitInfo());
-    unit->id = testing_data_[i].transient_id;
-    unit->name = testing_data_[i].name;
-    unit->type = ParseStorageUnitType(testing_data_[i].type);
-    unit->capacity = testing_data_[i].capacity;
-    info_.push_back(unit);
-  }
+TestStorageInfoProvider::TestStorageInfoProvider(
+    const struct TestStorageUnitInfo* testing_data, size_t n)
+        : StorageInfoProvider(kTestingIntervalMS),
+          testing_data_(testing_data, testing_data + n) {
 }
 
-std::vector<chrome::StorageInfo>
-TestStorageInfoProvider::GetAllStorages() const {
-  std::vector<chrome::StorageInfo> results;
-  for (size_t i = 0; i < testing_data_.size(); ++i)
-    results.push_back(BuildStorageInfo(testing_data_[i]));
-
-  return results;
+TestStorageInfoProvider::~TestStorageInfoProvider() {
 }
 
 int64 TestStorageInfoProvider::GetStorageFreeSpaceFromTransientId(
     const std::string& transient_id) {
   int64 available_capacity = -1;
+  std::string device_id =
+      chrome::StorageMonitor::GetInstance()->GetDeviceIdForTransientId(
+          transient_id);
   for (size_t i = 0; i < testing_data_.size(); ++i) {
-    if (testing_data_[i].transient_id == transient_id) {
+    if (testing_data_[i].device_id == device_id) {
       available_capacity = testing_data_[i].available_capacity;
       // We simulate free space change by increasing the |available_capacity|
       // with a fixed change step.
@@ -76,22 +59,5 @@
   return available_capacity;
 }
 
-std::string TestStorageInfoProvider::GetTransientIdForDeviceId(
-    const std::string& device_id) const {
-  for (size_t i = 0; i < testing_data_.size(); ++i) {
-    if (testing_data_[i].device_id == device_id)
-      return testing_data_[i].transient_id;
-  }
-  return std::string();
-}
-
-std::string TestStorageInfoProvider::GetDeviceIdForTransientId(
-    const std::string& transient_id) const {
-  for (size_t i = 0; i < testing_data_.size(); ++i) {
-    if (testing_data_[i].transient_id == transient_id)
-      return testing_data_[i].device_id;
-  }
-  return std::string();
-}
-
+}  // namespace test
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/system_storage/test_storage_info_provider.h b/chrome/browser/extensions/api/system_storage/test_storage_info_provider.h
index ef3d05e..7a390b7 100644
--- a/chrome/browser/extensions/api/system_storage/test_storage_info_provider.h
+++ b/chrome/browser/extensions/api/system_storage/test_storage_info_provider.h
@@ -11,12 +11,11 @@
 #include "chrome/browser/storage_monitor/storage_info.h"
 
 namespace extensions {
+namespace test {
 
 struct TestStorageUnitInfo {
   const char* device_id;
-  const char* transient_id;
   const char* name;
-  const char* type;
   // Total amount of the storage device space, in bytes.
   double capacity;
   // The available amount of the storage space, in bytes.
@@ -27,35 +26,26 @@
   int change_step;
 };
 
-// StorageInfoProvider for unit_tests and browser_tests.
-// Override related methods to avoid calling out to the actual system.
-//
-// TODO(Haojian) : Improve StorageInfoProvider test. Create a interface that
-// represents system storage functions, with a googlemock implementation.
+extern const struct TestStorageUnitInfo kRemovableStorageData;
+
+chrome::StorageInfo BuildStorageInfoFromTestStorageUnitInfo(
+    const TestStorageUnitInfo& unit);
+
 class TestStorageInfoProvider : public extensions::StorageInfoProvider {
  public:
   TestStorageInfoProvider(const struct TestStorageUnitInfo* testing_data,
                           size_t n);
 
-  static chrome::StorageInfo BuildStorageInfo(const TestStorageUnitInfo& unit);
-
-  virtual std::string GetTransientIdForDeviceId(
-      const std::string& device_id) const OVERRIDE;
-  virtual std::string GetDeviceIdForTransientId(
-      const std::string& transient_id) const OVERRIDE;
-
  protected:
   virtual ~TestStorageInfoProvider();
 
   // StorageInfoProvider implementations.
-  virtual void GetAllStoragesIntoInfoList() OVERRIDE;
-  virtual std::vector<chrome::StorageInfo> GetAllStorages() const OVERRIDE;
   virtual int64 GetStorageFreeSpaceFromTransientId(
       const std::string& transient_id) OVERRIDE;
 
   std::vector<struct TestStorageUnitInfo> testing_data_;
 };
 
+}  // namespace test
 }  // namespace extensions
 #endif  // CHROME_BROWSER_EXTENSIONS_API_SYSTEM_STORAGE_TEST_STORAGE_INFO_PROVIDER_H_
-
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
index f40ccef..cb45dfc 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/basictypes.h"
+#include "base/command_line.h"
 #if defined(OS_MACOSX)
 #include "base/mac/mac_util.h"
 #endif
@@ -21,14 +22,11 @@
 #include "chrome/common/extensions/features/complex_feature.h"
 #include "chrome/common/extensions/features/feature.h"
 #include "chrome/common/extensions/features/simple_feature.h"
+#include "chrome/test/base/test_switches.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/common/content_switches.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 namespace chrome {
 
 namespace {
@@ -56,7 +54,7 @@
 IN_PROC_BROWSER_TEST_F(TabCaptureApiTest, MAYBE_ApiTests) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/api/usb/usb_api.cc b/chrome/browser/extensions/api/usb/usb_api.cc
index 223f831..a6c8ae8 100644
--- a/chrome/browser/extensions/api/usb/usb_api.cc
+++ b/chrome/browser/extensions/api/usb/usb_api.cc
@@ -326,6 +326,14 @@
   return descriptor.ToValue().release();
 }
 
+void GetUsbService(base::Callback<void(UsbService* service)> callback) {
+  BrowserThread::PostTaskAndReplyWithResult(
+      BrowserThread::FILE,
+      FROM_HERE,
+      base::Bind(UsbService::GetInstance),
+      callback);
+}
+
 }  // namespace
 
 namespace extensions {
@@ -440,21 +448,48 @@
     return;
   }
 
-  UsbService::GetInstance()->FindDevices(
-      vendor_id,
-      product_id,
-      interface_id,
-      &devices_,
-      base::Bind(&UsbFindDevicesFunction::OnCompleted, this));
+  GetUsbService(base::Bind(&UsbFindDevicesFunction::EnumerateDevices,
+                           this,
+                           vendor_id,
+                           product_id,
+                           interface_id));
 }
 
-void UsbFindDevicesFunction::OnCompleted() {
-  for (size_t i = 0; i < devices_.size(); ++i) {
-    UsbDeviceHandle* const device = devices_[i].get();
+void UsbFindDevicesFunction::EnumerateDevices(
+    uint16_t vendor_id,
+    uint16_t product_id,
+    int interface_id,
+    UsbService* service) {
+  BrowserThread::PostTask(
+      BrowserThread::FILE,
+      FROM_HERE,
+      base::Bind(&UsbService::FindDevices,
+                 base::Unretained(service),
+                 vendor_id,
+                 product_id,
+                 interface_id,
+                 base::Bind(&UsbFindDevicesFunction::OnEnumerationCompleted,
+                            this)));
+}
+
+
+void UsbFindDevicesFunction::OnEnumerationCompleted(
+    ScopedDeviceVector devices) {
+  BrowserThread::PostTask(
+      BrowserThread::IO,
+      FROM_HERE,
+      base::Bind(&UsbFindDevicesFunction::OnCompleted,
+                 this,
+                 base::Passed(devices.Pass())));
+}
+
+void UsbFindDevicesFunction::OnCompleted(
+    ScopedDeviceVector devices) {
+  for (size_t i = 0; i < devices->size(); ++i) {
+    UsbDeviceHandle* const device = devices->at(i).get();
     UsbDeviceResource* const resource =
         new UsbDeviceResource(extension_->id(), device);
 
-    Device js_device;
     result_->Append(PopulateDevice(manager_->Add(resource),
                                    parameters_->options.vendor_id,
                                    parameters_->options.product_id));
diff --git a/chrome/browser/extensions/api/usb/usb_api.h b/chrome/browser/extensions/api/usb/usb_api.h
index 80941fc..65c713e 100644
--- a/chrome/browser/extensions/api/usb/usb_api.h
+++ b/chrome/browser/extensions/api/usb/usb_api.h
@@ -16,6 +16,7 @@
 #include "net/base/io_buffer.h"
 
 class UsbDeviceHandle;
+class UsbService;
 
 namespace extensions {
 
@@ -71,10 +72,21 @@
   virtual void AsyncWorkStart() OVERRIDE;
 
  private:
-  void OnCompleted();
+  typedef scoped_ptr<std::vector<scoped_refptr<UsbDeviceHandle> > >
+      ScopedDeviceVector;
+
+  // Wait for GetDeviceService to return and start enumeration on FILE thread.
+  void EnumerateDevices(uint16_t vendor_id,
+                        uint16_t product_id,
+                        int interface_id,
+                        UsbService* service);
+  // Relay the result on IO thread to OnCompleted.
+  void OnEnumerationCompleted(ScopedDeviceVector devices);
+
+  // Create ApiResources and reply.
+  void OnCompleted(ScopedDeviceVector devices);
 
   scoped_ptr<base::ListValue> result_;
-  std::vector<scoped_refptr<UsbDeviceHandle> > devices_;
   scoped_ptr<extensions::api::usb::FindDevices::Params> parameters_;
 };
 
diff --git a/chrome/browser/extensions/api/usb/usb_apitest.cc b/chrome/browser/extensions/api/usb/usb_apitest.cc
index 56d75b6..af0039c 100644
--- a/chrome/browser/extensions/api/usb/usb_apitest.cc
+++ b/chrome/browser/extensions/api/usb/usb_apitest.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/extensions/api/usb/usb_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_switches.h"
 #include "net/base/io_buffer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -66,11 +65,6 @@
 
 class UsbApiTest : public ExtensionApiTest {
  public:
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
-  }
-
   virtual void SetUpOnMainThread() OVERRIDE {
     mock_device_ = new MockUsbDeviceHandle();
     extensions::UsbFindDevicesFunction::SetDeviceForTest(mock_device_.get());
@@ -146,4 +140,3 @@
   EXPECT_CALL(*mock_device_.get(), Close()).Times(AnyNumber());
   ASSERT_TRUE(RunExtensionTest("usb/invalid_length_transfer"));
 }
-
diff --git a/chrome/browser/extensions/api/usb/usb_manual_apitest.cc b/chrome/browser/extensions/api/usb/usb_manual_apitest.cc
index f5f5c5f..be08a8a 100644
--- a/chrome/browser/extensions/api/usb/usb_manual_apitest.cc
+++ b/chrome/browser/extensions/api/usb/usb_manual_apitest.cc
@@ -4,16 +4,10 @@
 
 #include "chrome/browser/extensions/api/permissions/permissions_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/common/chrome_switches.h"
 
 namespace {
 
 class UsbManualApiTest : public ExtensionApiTest {
- public:
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
-  }
 };
 
 }  // namespace
diff --git a/chrome/browser/extensions/api/web_request/web_request_api.cc b/chrome/browser/extensions/api/web_request/web_request_api.cc
index 882034a..478c990 100644
--- a/chrome/browser/extensions/api/web_request/web_request_api.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_api.cc
@@ -17,6 +17,8 @@
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_content_browser_client.h"
+#include "chrome/browser/extensions/activity_log/activity_action_constants.h"
+#include "chrome/browser/extensions/activity_log/activity_actions.h"
 #include "chrome/browser/extensions/activity_log/activity_log.h"
 #include "chrome/browser/extensions/activity_log/web_request_constants.h"
 #include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
@@ -1634,12 +1636,16 @@
     Profile* profile = static_cast<Profile*>(profile_id);
     if (!g_browser_process->profile_manager()->IsValidProfile(profile))
       return;
-    extensions::ActivityLog::GetInstance(profile)->LogWebRequestAction(
-        extension_id,
-        is_incognito ?  GURL(extensions::APIAction::kIncognitoUrl) : url,
-        api_call,
-        details.Pass(),
-        "");
+    scoped_refptr<extensions::Action> action =
+        new extensions::Action(extension_id,
+                               base::Time::Now(),
+                               extensions::Action::ACTION_WEB_REQUEST,
+                               api_call);
+    action->set_page_url(url);
+    action->set_page_incognito(is_incognito);
+    action->mutable_other()->Set(activity_log_constants::kActionWebRequest,
+                                 details.release());
+    extensions::ActivityLog::GetInstance(profile)->LogAction(action);
   }
 }
 
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 063a07d..7c7acb8 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/login/login_prompt.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/features/feature.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_registrar.h"
@@ -59,10 +58,6 @@
 class ExtensionWebRequestApiTest : public ExtensionApiTest {
  public:
   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
-    // TODO(battre): remove this when declarative webRequest API becomes stable.
-    CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kEnableExperimentalExtensionApis);
-
     ExtensionApiTest::SetUpInProcessBrowserTestFixture();
     host_resolver()->AddRule("*", "127.0.0.1");
   }
@@ -284,8 +279,7 @@
       message_;
 }
 
-// TODO(dslomov): update expectations and re-eanble, http://crbug.com/257128.
-IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, DISABLED_PostData2) {
+IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, MAYBE_PostData2) {
   // Test HTML form POST data access with the multipart and plaintext encoding.
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_post2.html")) <<
diff --git a/chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_api.cc b/chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_api.cc
deleted file mode 100644
index 451654c..0000000
--- a/chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_api.cc
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_api.h"
-
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/values.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/internal_auth.h"
-#include "chrome/browser/io_thread.h"
-#include "chrome/common/extensions/extension.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_service.h"
-#include "net/base/escape.h"
-#include "net/base/net_errors.h"
-#include "net/base/net_log.h"
-#include "net/base/net_util.h"
-#include "net/dns/single_request_host_resolver.h"
-
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/web_socket_proxy_controller.h"
-#endif
-
-namespace extensions {
-
-WebSocketProxyPrivate::WebSocketProxyPrivate()
-    : port_(-1),
-      listening_port_(-1),
-      do_tls_(false),
-      is_finalized_(false) {
-}
-
-WebSocketProxyPrivate::~WebSocketProxyPrivate() {
-}
-
-void WebSocketProxyPrivate::Finalize() {
-  CustomFinalize();
-
-  if (is_finalized_)
-    return;
-  is_finalized_ = true;
-  SendResponse(listening_port_ > 0);
-  Release();
-}
-
-void WebSocketProxyPrivate::Observe(
-    int type, const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-#if defined(OS_CHROMEOS)
-  DCHECK_EQ(chrome::NOTIFICATION_WEB_SOCKET_PROXY_STARTED, type);
-#else
-  NOTREACHED();
-#endif
-
-  timer_.Stop();  // Cancel timeout timer.
-  ResolveHost();
-}
-
-void WebSocketProxyPrivate::ResolveHost() {
-#if defined(OS_CHROMEOS)
-  IOThread* io_thread = g_browser_process->io_thread();
-  content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
-      base::Bind(&WebSocketProxyPrivate::ResolveHostIOPart, this, io_thread));
-#endif
-}
-
-void WebSocketProxyPrivate::ResolveHostIOPart(IOThread* io_thread) {
-#if defined(OS_CHROMEOS)
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  DCHECK(resolver_ == NULL);
-  if (io_thread && io_thread->globals()) {
-    net::HostResolver* host_resolver =
-        io_thread->globals()->host_resolver.get();
-    if (host_resolver) {
-      resolver_.reset(new net::SingleRequestHostResolver(host_resolver));
-      net::HostResolver::RequestInfo info(net::HostPortPair(
-          hostname_, port_));
-      int result = resolver_->Resolve(info, &addr_,
-          base::Bind(&WebSocketProxyPrivate::OnHostResolution, this),
-          net::BoundNetLog());
-      if (result != net::ERR_IO_PENDING)
-        OnHostResolution(result);
-      return;
-    }
-  }
-  NOTREACHED();
-  OnHostResolution(net::ERR_UNEXPECTED);
-#endif
-}
-
-bool WebSocketProxyPrivate::RunImpl() {
-  AddRef();
-  SetResult(Value::CreateStringValue(std::string()));
-
-#if defined(OS_CHROMEOS)
-  bool delay_response = false;
-
-  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &hostname_));
-  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(1, &port_));
-
-  listening_port_ = chromeos::WebSocketProxyController::GetPort();
-  if (listening_port_ < 1) {
-    delay_response = true;
-    registrar_.Add(
-        this, chrome::NOTIFICATION_WEB_SOCKET_PROXY_STARTED,
-        content::NotificationService::AllSources());
-  }
-  map_["hostname"] = hostname_;
-  map_["port"] = base::IntToString(port_);
-  map_["extension_id"] = extension_id();
-
-  if (delay_response) {
-    const int kTimeout = 12;
-    timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimeout),
-        this, &WebSocketProxyPrivate::ResolveHost);
-  } else {
-    ResolveHost();
-  }
-#else
-  Finalize();
-#endif
-
-  return true;
-}
-
-void WebSocketProxyPrivate::OnHostResolution(int result) {
-#if defined(OS_CHROMEOS)
-  if (result == 0 && !addr_.empty()) {
-    std::string ip = addr_.front().ToStringWithoutPort();
-    if (!ip.empty() && ip.find(':') != std::string::npos && ip[0] != '[')
-      ip = '[' + ip + ']';
-    map_["addr"] = ip;
-  }
-  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
-      base::Bind(&WebSocketProxyPrivate::Finalize, this));
-#endif
-}
-
-WebSocketProxyPrivateGetURLForTCPFunction::
-    WebSocketProxyPrivateGetURLForTCPFunction() {
-}
-
-WebSocketProxyPrivateGetURLForTCPFunction::
-    ~WebSocketProxyPrivateGetURLForTCPFunction() {
-}
-
-void WebSocketProxyPrivateGetURLForTCPFunction::CustomFinalize() {
-#if defined(OS_CHROMEOS)
-  std::string passport = chrome::InternalAuthGeneration::GeneratePassport(
-      "web_socket_proxy", map_);
-  std::string query = std::string("hostname=") +
-      net::EscapeQueryParamValue(hostname_, false) + "&port=" + map_["port"] +
-      "&tls=" + map_["tls"] + "&passport=" +
-      net::EscapeQueryParamValue(passport, false);
-  if (ContainsKey(map_, "addr"))
-    query += std::string("&addr=") + map_["addr"];
-
-  if (listening_port_ < 1)
-    listening_port_ = chromeos::WebSocketProxyController::GetPort();
-  StringValue* url = Value::CreateStringValue(std::string(
-      "ws://127.0.0.1:" + base::IntToString(listening_port_) +
-      "/tcpproxy?" + query));
-  SetResult(url);
-#endif
-}
-
-bool WebSocketProxyPrivateGetURLForTCPFunction::RunImpl() {
-#if defined(OS_CHROMEOS)
-  base::DictionaryValue* qualification = NULL;
-  if (args_->GetDictionary(2, &qualification)) {
-    const char kTlsOption[] = "tls";
-    if (qualification->HasKey(kTlsOption)) {
-      EXTENSION_FUNCTION_VALIDATE(qualification->GetBoolean(
-          kTlsOption, &do_tls_));
-    }
-  }
-  map_["tls"] = do_tls_ ? "true" : "false";
-#endif
-
-  return WebSocketProxyPrivate::RunImpl();
-}
-
-WebSocketProxyPrivateGetPassportForTCPFunction::
-    WebSocketProxyPrivateGetPassportForTCPFunction() {
-  // This obsolete API uses fixed port to listen websocket connections.
-  listening_port_ = 10101;
-}
-
-WebSocketProxyPrivateGetPassportForTCPFunction::
-    ~WebSocketProxyPrivateGetPassportForTCPFunction() {
-}
-
-void WebSocketProxyPrivateGetPassportForTCPFunction::CustomFinalize() {
-#if defined(OS_CHROMEOS)
-  std::string passport = chrome::InternalAuthGeneration::GeneratePassport(
-      "web_socket_proxy", map_) + std::string(":");
-  if (ContainsKey(map_, "addr"))
-    passport += map_["addr"];
-  SetResult(Value::CreateStringValue(passport));
-#endif
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_api.h b/chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_api.h
deleted file mode 100644
index bec791e..0000000
--- a/chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_api.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_API_WEB_SOCKET_PROXY_PRIVATE_WEB_SOCKET_PROXY_PRIVATE_API_H_
-#define CHROME_BROWSER_EXTENSIONS_API_WEB_SOCKET_PROXY_PRIVATE_WEB_SOCKET_PROXY_PRIVATE_API_H_
-
-#include "base/timer/timer.h"
-#include "chrome/browser/extensions/extension_function.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "net/base/address_list.h"
-
-class IOThread;
-
-namespace net {
-class SingleRequestHostResolver;
-}
-
-namespace extensions {
-
-// Base class for web socket proxy functions.
-class WebSocketProxyPrivate
-    : public AsyncExtensionFunction, public content::NotificationObserver {
- public:
-  WebSocketProxyPrivate();
-
- protected:
-  virtual ~WebSocketProxyPrivate();
-
-  // Custom finalization.
-  virtual void CustomFinalize() = 0;
-
-  // ExtensionFunction implementation.
-  virtual bool RunImpl() OVERRIDE;
-
-  // content::NotificationObserver implementation.
-  virtual void Observe(
-      int type, const content::NotificationSource& source,
-      const content::NotificationDetails& details) OVERRIDE;
-
-  // Destination hostname.
-  std::string hostname_;
-  // Destination IP address.
-  net::AddressList addr_;
-  // Destination port.
-  int port_;
-  // Proxy accepts websocket connections on this port.
-  int listening_port_;
-  // Whether TLS should be used.
-  bool do_tls_;
-  // Requested parameters of connection.
-  std::map<std::string, std::string> map_;
-
- private:
-  // Finalizes and sends respond. Overwrite 'CustomFinalize' in inherited
-  // classes.
-  void Finalize();
-
-  // Callback for DNS resolution.
-  void OnHostResolution(int result);
-
-  // Posts task to the IO thread, which will make dns resolution.
-  void ResolveHost();
-  // Executes on IO thread. Performs DNS resolution.
-  void ResolveHostIOPart(IOThread* io_thread);
-
-  // Used to signal timeout (when waiting for proxy initial launch).
-  base::OneShotTimer<WebSocketProxyPrivate> timer_;
-  // Used to register for notifications.
-  content::NotificationRegistrar registrar_;
-  // Used to cancel host resolution when out of scope.
-  scoped_ptr<net::SingleRequestHostResolver> resolver_;
-  // Callback which is called when host is resolved.
-  bool is_finalized_;
-};
-
-// New API function for web socket proxy, which should be used.
-class WebSocketProxyPrivateGetURLForTCPFunction
-    : public WebSocketProxyPrivate {
- public:
-  DECLARE_EXTENSION_FUNCTION("webSocketProxyPrivate.getURLForTCP",
-                             WEBSOCKETPROXYPRIVATE_GETURLFORTCP)
-
-  WebSocketProxyPrivateGetURLForTCPFunction();
-
- protected:
-  virtual ~WebSocketProxyPrivateGetURLForTCPFunction();
-
-  // ExtensionFunction implementation.
-  virtual bool RunImpl() OVERRIDE;
-
-  // WebSocketProxyPrivate implementation:
-  virtual void CustomFinalize() OVERRIDE;
-};
-
-// Legacy API function for web socket proxy, to be eliminated.
-class WebSocketProxyPrivateGetPassportForTCPFunction
-    : public WebSocketProxyPrivate {
- public:
-  DECLARE_EXTENSION_FUNCTION("webSocketProxyPrivate.getPassportForTCP",
-                             WEBSOCKETPROXYPRIVATE_GETPASSPORTFORTCP)
-
-  WebSocketProxyPrivateGetPassportForTCPFunction();
-
- protected:
-  virtual ~WebSocketProxyPrivateGetPassportForTCPFunction();
-
-  // WebSocketProxyPrivate implementation:
-  virtual void CustomFinalize() OVERRIDE;
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_API_WEB_SOCKET_PROXY_PRIVATE_WEB_SOCKET_PROXY_PRIVATE_API_H_
diff --git a/chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_apitest.cc b/chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_apitest.cc
deleted file mode 100644
index c417921..0000000
--- a/chrome/browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_apitest.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/common/chrome_switches.h"
-
-namespace extensions {
-
-class ExtensionWebSocketProxyPrivateApiTest : public ExtensionApiTest {
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(
-        switches::kWhitelistedExtensionID, "mknjjldhaihcdajjbihghhiehamnpcak");
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(ExtensionWebSocketProxyPrivateApiTest, Pass) {
-  // Currently WebSocket-to-TCP proxy is operational only on ChromeOS platform.
-#if defined(OS_CHROMEOS)
-  ASSERT_TRUE(StartEmbeddedTestServer());
-  ASSERT_TRUE(RunExtensionTest("web_socket_proxy_private")) << message_;
-  // Check if API still works on subsequent calls.
-  ASSERT_TRUE(RunExtensionTest("web_socket_proxy_private")) << message_;
-#endif
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index 0655d6e..f108480 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -206,10 +206,10 @@
   return g_pending_approvals.Get().PopApproval(profile, extension_id);
 }
 
-InstallBundleFunction::InstallBundleFunction() {}
-InstallBundleFunction::~InstallBundleFunction() {}
+WebstorePrivateInstallBundleFunction::WebstorePrivateInstallBundleFunction() {}
+WebstorePrivateInstallBundleFunction::~WebstorePrivateInstallBundleFunction() {}
 
-bool InstallBundleFunction::RunImpl() {
+bool WebstorePrivateInstallBundleFunction::RunImpl() {
   base::ListValue* extensions = NULL;
   EXTENSION_FUNCTION_VALIDATE(args_->GetList(0, &extensions));
 
@@ -225,8 +225,9 @@
   return true;
 }
 
-bool InstallBundleFunction::ReadBundleInfo(base::ListValue* extensions,
-                                           BundleInstaller::ItemList* items) {
+bool WebstorePrivateInstallBundleFunction::
+    ReadBundleInfo(base::ListValue* extensions,
+    BundleInstaller::ItemList* items) {
   for (size_t i = 0; i < extensions->GetSize(); ++i) {
     base::DictionaryValue* details = NULL;
     EXTENSION_FUNCTION_VALIDATE(extensions->GetDictionary(i, &details));
@@ -245,13 +246,14 @@
   return true;
 }
 
-void InstallBundleFunction::OnBundleInstallApproved() {
+void WebstorePrivateInstallBundleFunction::OnBundleInstallApproved() {
   bundle_->CompleteInstall(
       &(dispatcher()->delegate()->GetAssociatedWebContents()->GetController()),
       this);
 }
 
-void InstallBundleFunction::OnBundleInstallCanceled(bool user_initiated) {
+void WebstorePrivateInstallBundleFunction::OnBundleInstallCanceled(
+    bool user_initiated) {
   if (user_initiated)
     error_ = "user_canceled";
   else
@@ -262,18 +264,20 @@
   Release();  // Balanced in RunImpl().
 }
 
-void InstallBundleFunction::OnBundleInstallCompleted() {
+void WebstorePrivateInstallBundleFunction::OnBundleInstallCompleted() {
   SendResponse(true);
 
   Release();  // Balanced in RunImpl().
 }
 
-BeginInstallWithManifestFunction::BeginInstallWithManifestFunction()
+WebstorePrivateBeginInstallWithManifest3Function::
+    WebstorePrivateBeginInstallWithManifest3Function()
     : use_app_installed_bubble_(false), enable_launcher_(false) {}
 
-BeginInstallWithManifestFunction::~BeginInstallWithManifestFunction() {}
+WebstorePrivateBeginInstallWithManifest3Function::
+    ~WebstorePrivateBeginInstallWithManifest3Function() {}
 
-bool BeginInstallWithManifestFunction::RunImpl() {
+bool WebstorePrivateBeginInstallWithManifest3Function::RunImpl() {
   base::DictionaryValue* details = NULL;
   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details));
   CHECK(details);
@@ -349,7 +353,8 @@
 }
 
 
-void BeginInstallWithManifestFunction::SetResultCode(ResultCode code) {
+void WebstorePrivateBeginInstallWithManifest3Function::SetResultCode(
+    ResultCode code) {
   switch (code) {
     case ERROR_NONE:
       SetResult(Value::CreateStringValue(std::string()));
@@ -386,7 +391,7 @@
   }
 }
 
-void BeginInstallWithManifestFunction::OnWebstoreParseSuccess(
+void WebstorePrivateBeginInstallWithManifest3Function::OnWebstoreParseSuccess(
     const std::string& id,
     const SkBitmap& icon,
     base::DictionaryValue* parsed_manifest) {
@@ -424,7 +429,7 @@
   SigninCompletedOrNotNeeded();
 }
 
-void BeginInstallWithManifestFunction::OnWebstoreParseFailure(
+void WebstorePrivateBeginInstallWithManifest3Function::OnWebstoreParseFailure(
     const std::string& id,
     WebstoreInstallHelper::Delegate::InstallHelperResultCode result_code,
     const std::string& error_message) {
@@ -452,7 +457,7 @@
   Release();
 }
 
-void BeginInstallWithManifestFunction::SigninFailed(
+void WebstorePrivateBeginInstallWithManifest3Function::SigninFailed(
     const GoogleServiceAuthError& error) {
   signin_tracker_.reset();
 
@@ -465,13 +470,14 @@
   Release();
 }
 
-void BeginInstallWithManifestFunction::SigninSuccess() {
+void WebstorePrivateBeginInstallWithManifest3Function::SigninSuccess() {
   signin_tracker_.reset();
 
   SigninCompletedOrNotNeeded();
 }
 
-void BeginInstallWithManifestFunction::SigninCompletedOrNotNeeded() {
+void WebstorePrivateBeginInstallWithManifest3Function::
+    SigninCompletedOrNotNeeded() {
   content::WebContents* web_contents = GetAssociatedWebContents();
   if (!web_contents)  // The browser window has gone away.
     return;
@@ -484,7 +490,7 @@
   // Control flow finishes up in InstallUIProceed or InstallUIAbort.
 }
 
-void BeginInstallWithManifestFunction::InstallUIProceed() {
+void WebstorePrivateBeginInstallWithManifest3Function::InstallUIProceed() {
   // This gets cleared in CrxInstaller::ConfirmInstall(). TODO(asargent) - in
   // the future we may also want to add time-based expiration, where a whitelist
   // entry is only valid for some number of minutes.
@@ -512,7 +518,8 @@
   Release();
 }
 
-void BeginInstallWithManifestFunction::InstallUIAbort(bool user_initiated) {
+void WebstorePrivateBeginInstallWithManifest3Function::InstallUIAbort(
+    bool user_initiated) {
   error_ = kUserCancelledError;
   SetResultCode(USER_CANCELLED);
   g_pending_installs.Get().EraseInstall(profile_, id_);
@@ -537,11 +544,13 @@
   Release();
 }
 
-CompleteInstallFunction::CompleteInstallFunction() {}
+WebstorePrivateCompleteInstallFunction::
+    WebstorePrivateCompleteInstallFunction() {}
 
-CompleteInstallFunction::~CompleteInstallFunction() {}
+WebstorePrivateCompleteInstallFunction::
+    ~WebstorePrivateCompleteInstallFunction() {}
 
-bool CompleteInstallFunction::RunImpl() {
+bool WebstorePrivateCompleteInstallFunction::RunImpl() {
   std::string id;
   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &id));
   if (!extensions::Extension::IdIsValid(id)) {
@@ -580,7 +589,7 @@
   return true;
 }
 
-void CompleteInstallFunction::OnExtensionInstallSuccess(
+void WebstorePrivateCompleteInstallFunction::OnExtensionInstallSuccess(
     const std::string& id) {
   if (test_webstore_installer_delegate)
     test_webstore_installer_delegate->OnExtensionInstallSuccess(id);
@@ -593,7 +602,7 @@
   Release();
 }
 
-void CompleteInstallFunction::OnExtensionInstallFailure(
+void WebstorePrivateCompleteInstallFunction::OnExtensionInstallFailure(
     const std::string& id,
     const std::string& error,
     WebstoreInstaller::FailureReason reason) {
@@ -611,64 +620,68 @@
   Release();
 }
 
-EnableAppLauncherFunction::EnableAppLauncherFunction() {}
+WebstorePrivateEnableAppLauncherFunction::
+    WebstorePrivateEnableAppLauncherFunction() {}
 
-EnableAppLauncherFunction::~EnableAppLauncherFunction() {}
+WebstorePrivateEnableAppLauncherFunction::
+    ~WebstorePrivateEnableAppLauncherFunction() {}
 
-bool EnableAppLauncherFunction::RunImpl() {
+bool WebstorePrivateEnableAppLauncherFunction::RunImpl() {
   AppListService::Get()->EnableAppList(profile());
   SendResponse(true);
   return true;
 }
 
-bool GetBrowserLoginFunction::RunImpl() {
+bool WebstorePrivateGetBrowserLoginFunction::RunImpl() {
   SetResult(CreateLoginResult(profile_->GetOriginalProfile()));
   return true;
 }
 
-bool GetStoreLoginFunction::RunImpl() {
+bool WebstorePrivateGetStoreLoginFunction::RunImpl() {
   SetResult(Value::CreateStringValue(GetWebstoreLogin(profile_)));
   return true;
 }
 
-bool SetStoreLoginFunction::RunImpl() {
+bool WebstorePrivateSetStoreLoginFunction::RunImpl() {
   std::string login;
   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &login));
   SetWebstoreLogin(profile_, login);
   return true;
 }
 
-GetWebGLStatusFunction::GetWebGLStatusFunction() {
+WebstorePrivateGetWebGLStatusFunction::WebstorePrivateGetWebGLStatusFunction() {
   feature_checker_ = new GPUFeatureChecker(
       gpu::GPU_FEATURE_TYPE_WEBGL,
-      base::Bind(&GetWebGLStatusFunction::OnFeatureCheck,
+      base::Bind(&WebstorePrivateGetWebGLStatusFunction::OnFeatureCheck,
           base::Unretained(this)));
 }
 
-GetWebGLStatusFunction::~GetWebGLStatusFunction() {}
+WebstorePrivateGetWebGLStatusFunction::
+    ~WebstorePrivateGetWebGLStatusFunction() {}
 
-void GetWebGLStatusFunction::CreateResult(bool webgl_allowed) {
+void WebstorePrivateGetWebGLStatusFunction::CreateResult(bool webgl_allowed) {
   SetResult(Value::CreateStringValue(
       webgl_allowed ? "webgl_allowed" : "webgl_blocked"));
 }
 
-bool GetWebGLStatusFunction::RunImpl() {
+bool WebstorePrivateGetWebGLStatusFunction::RunImpl() {
   feature_checker_->CheckGPUFeatureAvailability();
   return true;
 }
 
-void GetWebGLStatusFunction::OnFeatureCheck(bool feature_allowed) {
+void WebstorePrivateGetWebGLStatusFunction::
+    OnFeatureCheck(bool feature_allowed) {
   CreateResult(feature_allowed);
   SendResponse(true);
 }
 
-bool GetIsLauncherEnabledFunction::RunImpl() {
+bool WebstorePrivateGetIsLauncherEnabledFunction::RunImpl() {
   SetResult(Value::CreateBooleanValue(apps::IsAppLauncherEnabled()));
   SendResponse(true);
   return true;
 }
 
-bool IsInIncognitoModeFunction::RunImpl() {
+bool WebstorePrivateIsInIncognitoModeFunction::RunImpl() {
   SetResult(
       Value::CreateBooleanValue(profile_ != profile_->GetOriginalProfile()));
   SendResponse(true);
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.h b/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
index 2337bc3..19320bf 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
@@ -42,13 +42,13 @@
       Profile* profile, const std::string& extension_id);
 };
 
-class InstallBundleFunction : public AsyncExtensionFunction,
+class WebstorePrivateInstallBundleFunction : public AsyncExtensionFunction,
                               public extensions::BundleInstaller::Delegate {
  public:
   DECLARE_EXTENSION_FUNCTION("webstorePrivate.installBundle",
                              WEBSTOREPRIVATE_INSTALLBUNDLE)
 
-  InstallBundleFunction();
+  WebstorePrivateInstallBundleFunction();
 
   // BundleInstaller::Delegate:
   virtual void OnBundleInstallApproved() OVERRIDE;
@@ -56,7 +56,7 @@
   virtual void OnBundleInstallCompleted() OVERRIDE;
 
  protected:
-  virtual ~InstallBundleFunction();
+  virtual ~WebstorePrivateInstallBundleFunction();
 
   // ExtensionFunction:
   virtual bool RunImpl() OVERRIDE;
@@ -69,7 +69,7 @@
   scoped_refptr<extensions::BundleInstaller> bundle_;
 };
 
-class BeginInstallWithManifestFunction
+class WebstorePrivateBeginInstallWithManifest3Function
     : public AsyncExtensionFunction,
       public ExtensionInstallPrompt::Delegate,
       public WebstoreInstallHelper::Delegate,
@@ -112,7 +112,7 @@
     ALREADY_INSTALLED,
   };
 
-  BeginInstallWithManifestFunction();
+  WebstorePrivateBeginInstallWithManifest3Function();
 
   // WebstoreInstallHelper::Delegate:
   virtual void OnWebstoreParseSuccess(
@@ -129,7 +129,7 @@
   virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
 
  protected:
-  virtual ~BeginInstallWithManifestFunction();
+  virtual ~WebstorePrivateBeginInstallWithManifest3Function();
 
   // ExtensionFunction:
   virtual bool RunImpl() OVERRIDE;
@@ -167,14 +167,14 @@
   scoped_ptr<SigninTracker> signin_tracker_;
 };
 
-class CompleteInstallFunction
+class WebstorePrivateCompleteInstallFunction
     : public AsyncExtensionFunction,
       public WebstoreInstaller::Delegate {
  public:
   DECLARE_EXTENSION_FUNCTION("webstorePrivate.completeInstall",
                              WEBSTOREPRIVATE_COMPLETEINSTALL)
 
-  CompleteInstallFunction();
+  WebstorePrivateCompleteInstallFunction();
 
   // WebstoreInstaller::Delegate:
   virtual void OnExtensionInstallSuccess(const std::string& id) OVERRIDE;
@@ -184,7 +184,7 @@
       WebstoreInstaller::FailureReason reason) OVERRIDE;
 
  protected:
-  virtual ~CompleteInstallFunction();
+  virtual ~WebstorePrivateCompleteInstallFunction();
 
   // ExtensionFunction:
   virtual bool RunImpl() OVERRIDE;
@@ -193,66 +193,66 @@
   scoped_ptr<WebstoreInstaller::Approval> approval_;
 };
 
-class EnableAppLauncherFunction
+class WebstorePrivateEnableAppLauncherFunction
     : public AsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("webstorePrivate.enableAppLauncher",
                              WEBSTOREPRIVATE_ENABLEAPPLAUNCHER)
 
-  EnableAppLauncherFunction();
+  WebstorePrivateEnableAppLauncherFunction();
 
  protected:
-  virtual ~EnableAppLauncherFunction();
+  virtual ~WebstorePrivateEnableAppLauncherFunction();
 
   // ExtensionFunction:
   virtual bool RunImpl() OVERRIDE;
 };
 
-class GetBrowserLoginFunction : public SyncExtensionFunction {
+class WebstorePrivateGetBrowserLoginFunction : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("webstorePrivate.getBrowserLogin",
                              WEBSTOREPRIVATE_GETBROWSERLOGIN)
 
  protected:
-  virtual ~GetBrowserLoginFunction() {}
+  virtual ~WebstorePrivateGetBrowserLoginFunction() {}
 
   // ExtensionFunction:
   virtual bool RunImpl() OVERRIDE;
 };
 
-class GetStoreLoginFunction : public SyncExtensionFunction {
+class WebstorePrivateGetStoreLoginFunction : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("webstorePrivate.getStoreLogin",
                              WEBSTOREPRIVATE_GETSTORELOGIN)
 
  protected:
-  virtual ~GetStoreLoginFunction() {}
+  virtual ~WebstorePrivateGetStoreLoginFunction() {}
 
   // ExtensionFunction:
   virtual bool RunImpl() OVERRIDE;
 };
 
-class SetStoreLoginFunction : public SyncExtensionFunction {
+class WebstorePrivateSetStoreLoginFunction : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("webstorePrivate.setStoreLogin",
                              WEBSTOREPRIVATE_SETSTORELOGIN)
 
  protected:
-  virtual ~SetStoreLoginFunction() {}
+  virtual ~WebstorePrivateSetStoreLoginFunction() {}
 
   // ExtensionFunction:
   virtual bool RunImpl() OVERRIDE;
 };
 
-class GetWebGLStatusFunction : public AsyncExtensionFunction {
+class WebstorePrivateGetWebGLStatusFunction : public AsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("webstorePrivate.getWebGLStatus",
                              WEBSTOREPRIVATE_GETWEBGLSTATUS)
 
-  GetWebGLStatusFunction();
+  WebstorePrivateGetWebGLStatusFunction();
 
  protected:
-  virtual ~GetWebGLStatusFunction();
+  virtual ~WebstorePrivateGetWebGLStatusFunction();
 
   void OnFeatureCheck(bool feature_allowed);
 
@@ -265,15 +265,16 @@
   scoped_refptr<GPUFeatureChecker> feature_checker_;
 };
 
-class GetIsLauncherEnabledFunction : public AsyncExtensionFunction {
+class WebstorePrivateGetIsLauncherEnabledFunction
+    : public AsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("webstorePrivate.getIsLauncherEnabled",
                              WEBSTOREPRIVATE_GETISLAUNCHERENABLED)
 
-  GetIsLauncherEnabledFunction() {}
+  WebstorePrivateGetIsLauncherEnabledFunction() {}
 
  protected:
-  virtual ~GetIsLauncherEnabledFunction() {}
+  virtual ~WebstorePrivateGetIsLauncherEnabledFunction() {}
 
   // ExtensionFunction:
   virtual bool RunImpl() OVERRIDE;
@@ -282,15 +283,15 @@
   void OnIsLauncherCheckCompleted(bool is_enabled);
 };
 
-class IsInIncognitoModeFunction : public AsyncExtensionFunction {
+class WebstorePrivateIsInIncognitoModeFunction : public AsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("webstorePrivate.isInIncognitoMode",
                              WEBSTOREPRIVATE_ISININCOGNITOMODEFUNCTION)
 
-  IsInIncognitoModeFunction() {}
+  WebstorePrivateIsInIncognitoModeFunction() {}
 
  protected:
-  virtual ~IsInIncognitoModeFunction() {}
+  virtual ~WebstorePrivateIsInIncognitoModeFunction() {}
 
   // ExtensionFunction:
   virtual bool RunImpl() OVERRIDE;
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
index b4f80d2..af1689f 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
@@ -328,8 +328,8 @@
     static const char kEmptyArgs[] = "[]";
     static const char kWebGLStatusAllowed[] = "webgl_allowed";
     static const char kWebGLStatusBlocked[] = "webgl_blocked";
-    scoped_refptr<GetWebGLStatusFunction> function =
-        new GetWebGLStatusFunction();
+    scoped_refptr<WebstorePrivateGetWebGLStatusFunction> function =
+        new WebstorePrivateGetWebGLStatusFunction();
     scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
             function.get(), kEmptyArgs, browser()));
     ASSERT_TRUE(result);
diff --git a/chrome/browser/extensions/app_process_apitest.cc b/chrome/browser/extensions/app_process_apitest.cc
index a1c30b5..b1a2b08 100644
--- a/chrome/browser/extensions/app_process_apitest.cc
+++ b/chrome/browser/extensions/app_process_apitest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_host.h"
@@ -20,6 +21,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_file_util.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_service.h"
@@ -32,10 +34,6 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "sync/api/string_ordinal.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::NavigationController;
 using content::RenderViewHost;
 using content::WebContents;
@@ -310,6 +308,7 @@
   service->OnExtensionInstalled(extension.get(),
                                 syncer::StringOrdinal::CreateInitialOrdinal(),
                                 false /* no requirement errors */,
+                                extensions::Blacklist::NOT_BLACKLISTED,
                                 false /* don't wait for idle */);
   ASSERT_TRUE(extension.get());
   ASSERT_TRUE(extension->from_bookmark());
@@ -606,7 +605,7 @@
 IN_PROC_BROWSER_TEST_F(AppApiTest, OpenAppFromIframe) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/blacklist.cc b/chrome/browser/extensions/blacklist.cc
index 87c7e28..e0f6dbb 100644
--- a/chrome/browser/extensions/blacklist.cc
+++ b/chrome/browser/extensions/blacklist.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_prefs.h"
-#include "chrome/browser/safe_browsing/database_manager.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/safe_browsing_util.h"
 #include "chrome/common/pref_names.h"
@@ -116,7 +115,8 @@
 
 void IsNotEmpty(const Blacklist::IsBlacklistedCallback& callback,
                 const std::set<std::string>& set) {
-  callback.Run(!set.empty());
+  callback.Run(set.empty() ? Blacklist::NOT_BLACKLISTED
+                           : Blacklist::BLACKLISTED);
 }
 
 }  // namespace
diff --git a/chrome/browser/extensions/blacklist.h b/chrome/browser/extensions/blacklist.h
index 3d6dd8b..ed7acf4 100644
--- a/chrome/browser/extensions/blacklist.h
+++ b/chrome/browser/extensions/blacklist.h
@@ -52,10 +52,15 @@
     DISALLOW_COPY_AND_ASSIGN(ScopedDatabaseManagerForTest);
   };
 
+  enum BlacklistState {
+    NOT_BLACKLISTED,
+    BLACKLISTED,
+  };
+
   typedef base::Callback<void(const std::set<std::string>&)>
       GetBlacklistedIDsCallback;
 
-  typedef base::Callback<void(bool)> IsBlacklistedCallback;
+  typedef base::Callback<void(BlacklistState)> IsBlacklistedCallback;
 
   // |prefs_| must outlive this.
   explicit Blacklist(ExtensionPrefs* prefs);
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 1c4fe8d..c0b626b 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -392,7 +392,8 @@
   // Component extensions with background pages are not enabled during tests
   // because they generate a lot of background behavior that can interfere.
   if (!enable_background_extensions_during_testing &&
-      command_line->HasSwitch(switches::kTestType)) {
+      (command_line->HasSwitch(switches::kTestType) ||
+          command_line->HasSwitch(switches::kMetricsRecordingOnly))) {
     return;
   }
 
@@ -413,6 +414,10 @@
 #endif
   }
 
+#if defined(GOOGLE_CHROME_BUILD)
+    Add(IDR_FEEDBACK_MANIFEST, base::FilePath(FILE_PATH_LITERAL("feedback")));
+#endif  // defined(GOOGLE_CHROME_BUILD)
+
 #if defined(OS_CHROMEOS)
   if (!skip_session_components) {
     Add(IDR_WALLPAPERMANAGER_MANIFEST,
@@ -455,6 +460,8 @@
 
     Add(IDR_CONNECTIVITY_DIAGNOSTICS_MANIFEST,
         base::FilePath(extension_misc::kConnectivityDiagnosticsPath));
+    Add(IDR_CONNECTIVITY_DIAGNOSTICS_LAUNCHER_MANIFEST,
+        base::FilePath(extension_misc::kConnectivityDiagnosticsLauncherPath));
   }
 
   // Load ChromeVox extension now if spoken feedback is enabled.
diff --git a/chrome/browser/extensions/convert_web_app_browsertest.cc b/chrome/browser/extensions/convert_web_app_browsertest.cc
index f094e14..4294e3e 100644
--- a/chrome/browser/extensions/convert_web_app_browsertest.cc
+++ b/chrome/browser/extensions/convert_web_app_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <string>
 
+#include "base/command_line.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
@@ -16,6 +17,7 @@
 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
 #include "chrome/common/extensions/permissions/permission_set.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_observer.h"
@@ -24,10 +26,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 namespace extensions {
 
 class ExtensionFromWebAppTest
@@ -65,7 +63,7 @@
 IN_PROC_BROWSER_TEST_F(ExtensionFromWebAppTest, MAYBE_Basic) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index 588e2f1..a80d1e0 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -100,13 +100,13 @@
       client_(client),
       apps_require_extension_mime_type_(false),
       allow_silent_install_(false),
-      bypass_blacklist_for_test_(false),
       install_cause_(extension_misc::INSTALL_CAUSE_UNSET),
       creation_flags_(Extension::NO_FLAGS),
       off_store_install_allow_reason_(OffStoreInstallDisallowed),
       did_handle_successfully_(true),
       error_on_unsupported_requirements_(false),
       has_requirement_errors_(false),
+      blacklist_state_(extensions::Blacklist::NOT_BLACKLISTED),
       install_wait_for_idle_(true),
       update_from_settings_page_(false),
       installer_(service_weak->profile()) {
@@ -429,6 +429,9 @@
 void CrxInstaller::OnRequirementsChecked(
     std::vector<std::string> requirement_errors) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (!service_weak_)
+    return;
+
   if (!requirement_errors.empty()) {
     if (error_on_unsupported_requirements_) {
       ReportFailureFromUIThread(CrxInstallerError(
@@ -437,6 +440,36 @@
     }
     has_requirement_errors_ = true;
   }
+
+  ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted(
+      extension()->id(),
+      base::Bind(&CrxInstaller::OnBlacklistChecked, this));
+}
+
+void CrxInstaller::OnBlacklistChecked(
+    extensions::Blacklist::BlacklistState blacklist_state) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (!service_weak_)
+    return;
+
+  blacklist_state_ = blacklist_state;
+
+  if (blacklist_state_ == extensions::Blacklist::BLACKLISTED &&
+      !allow_silent_install_) {
+    // User tried to install a blacklisted extension. Show an error and
+    // refuse to install it.
+    ReportFailureFromUIThread(extensions::CrxInstallerError(
+        l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED,
+                                   UTF8ToUTF16(extension()->name()))));
+    UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
+                              extension()->location(),
+                              Manifest::NUM_LOCATIONS);
+    return;
+  }
+
+  // NOTE: extension may still be blacklisted, but we're forced to silently
+  // install it. In this case, ExtensionService::OnExtensionInstalled needs to
+  // deal with it.
   ConfirmInstall();
 }
 
@@ -673,43 +706,12 @@
     }
   }
 
-  // Install the extension if it's not blacklisted, but notify either way.
-  base::Closure on_success =
-      base::Bind(&ExtensionService::OnExtensionInstalled,
-                 service_weak_,
-                 extension(),
-                 page_ordinal_,
-                 has_requirement_errors_,
-                 install_wait_for_idle_);
-  if (bypass_blacklist_for_test_) {
-    HandleIsBlacklistedResponse(on_success, false);
-  } else {
-    ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted(
-        extension()->id(),
-        base::Bind(&CrxInstaller::HandleIsBlacklistedResponse,
-                   this,
-                   on_success));
-  }
-}
-
-void CrxInstaller::HandleIsBlacklistedResponse(
-    const base::Closure& on_success,
-    bool is_blacklisted) {
-  if (is_blacklisted) {
-    string16 error = l10n_util::GetStringFUTF16(
-        IDS_EXTENSION_IS_BLACKLISTED,
-        UTF8ToUTF16(extension()->name()));
-    make_scoped_ptr(ExtensionInstallUI::Create(profile()))->OnInstallFailure(
-        extensions::CrxInstallerError(error));
-    // Show error via reporter to make tests happy.
-    ExtensionErrorReporter::GetInstance()->ReportError(error, false);  // quiet
-    UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
-                              extension()->location(),
-                              Manifest::NUM_LOCATIONS);
-  } else {
-    on_success.Run();
-  }
-  NotifyCrxInstallComplete(!is_blacklisted);
+  service_weak_->OnExtensionInstalled(extension(),
+                                      page_ordinal_,
+                                      has_requirement_errors_,
+                                      blacklist_state_,
+                                      install_wait_for_idle_);
+  NotifyCrxInstallComplete(true);
 }
 
 void CrxInstaller::NotifyCrxInstallComplete(bool success) {
@@ -726,7 +728,6 @@
 
   if (success)
     ConfirmReEnable();
-
 }
 
 void CrxInstaller::CleanupTempFiles() {
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index dd2e9216..6f71868 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -12,6 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/version.h"
+#include "chrome/browser/extensions/blacklist.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
 #include "chrome/browser/extensions/extension_installer.h"
 #include "chrome/browser/extensions/sandboxed_unpacker.h"
@@ -131,10 +132,6 @@
   bool allow_silent_install() const { return allow_silent_install_; }
   void set_allow_silent_install(bool val) { allow_silent_install_ = val; }
 
-  void set_bypass_blacklist_for_test(bool val) {
-    bypass_blacklist_for_test_ = val;
-  }
-
   bool is_gallery_install() const {
     return (creation_flags_ & Extension::FROM_WEBSTORE) > 0;
   }
@@ -231,6 +228,10 @@
   // Runs on the UI thread. Callback from RequirementsChecker.
   void OnRequirementsChecked(std::vector<std::string> requirement_errors);
 
+  // Runs on the UI thread. Callback from Blacklist.
+  void OnBlacklistChecked(
+      extensions::Blacklist::BlacklistState blacklist_state);
+
   // Runs on the UI thread. Confirms the installation to the ExtensionService.
   void ConfirmInstall();
 
@@ -243,8 +244,6 @@
   void ReportFailureFromUIThread(const CrxInstallerError& error);
   void ReportSuccessFromFileThread();
   void ReportSuccessFromUIThread();
-  void HandleIsBlacklistedResponse(const base::Closure& on_success,
-                                   bool success);
   void NotifyCrxInstallComplete(bool success);
 
   // Deletes temporary directory and crx file if needed.
@@ -349,9 +348,6 @@
   // dialog.
   bool allow_silent_install_;
 
-  // Allows for bypassing the blacklist check. Only use for tests.
-  bool bypass_blacklist_for_test_;
-
   // The value of the content type header sent with the CRX.
   // Ignorred unless |require_extension_mime_type_| is true.
   std::string original_mime_type_;
@@ -381,6 +377,8 @@
 
   bool has_requirement_errors_;
 
+  extensions::Blacklist::BlacklistState blacklist_state_;
+
   bool install_wait_for_idle_;
 
   // Sequenced task runner where file I/O operations will be performed.
diff --git a/chrome/browser/extensions/crx_installer_browsertest.cc b/chrome/browser/extensions/crx_installer_browsertest.cc
index 1f0ef9c..112772f 100644
--- a/chrome/browser/extensions/crx_installer_browsertest.cc
+++ b/chrome/browser/extensions/crx_installer_browsertest.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_file_util.h"
 #include "chrome/common/extensions/feature_switch.h"
@@ -20,6 +19,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/test/download_test_observer.h"
+#include "extensions/common/switches.h"
 #include "grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -107,7 +107,6 @@
                              approval.get()       /* keep ownership */));
     installer->set_allow_silent_install(true);
     installer->set_is_gallery_install(true);
-    installer->set_bypass_blacklist_for_test(true);
     installer->InstallCrx(PackExtension(ext_path));
     content::RunMessageLoop();
 
@@ -136,6 +135,29 @@
             mock_prompt->extension()->id());
     ASSERT_TRUE(permissions.get());
   }
+
+  // Creates and returns a popup ExtensionHost for an extension and waits
+  // for a url to load in the host's web contents.
+  // The caller is responsible for cleaning up the returned ExtensionHost.
+  ExtensionHost* OpenUrlInExtensionPopupHost(const Extension* extension,
+                                             const GURL& url) {
+    ExtensionSystem* extension_system = extensions::ExtensionSystem::Get(
+        browser()->profile());
+    ExtensionProcessManager* epm = extension_system->process_manager();
+    ExtensionHost* extension_host =
+        epm->CreatePopupHost(extension, url, browser());
+
+    extension_host->CreateRenderViewSoon();
+    if (!extension_host->IsRenderViewLive()) {
+      content::WindowedNotificationObserver observer(
+          content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
+          content::Source<content::WebContents>(
+              extension_host->host_contents()));
+      observer.Wait();
+    }
+
+    return extension_host;
+  }
 };
 
 #if defined(OS_CHROMEOS)
@@ -302,4 +324,72 @@
   EXPECT_FALSE(service->GetExtensionById(extension_id, false));
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest,
+                       InstallDelayedUntilNextUpdate) {
+  const std::string extension_id("ldnnhddmnhbkjipkidpdiheffobcpfmf");
+  base::FilePath crx_path = test_data_dir_.AppendASCII("delayed_install");
+  ExtensionSystem* extension_system = extensions::ExtensionSystem::Get(
+      browser()->profile());
+  ExtensionService* service = extension_system->extension_service();
+  ASSERT_TRUE(service);
+
+  // Install version 1 of the test extension. This extension does not have
+  // a background page but does have a browser action.
+  ASSERT_TRUE(InstallExtension(crx_path.AppendASCII("v1.crx"), 1));
+  const extensions::Extension* extension =
+     service->GetExtensionById(extension_id, false);
+  ASSERT_TRUE(extension);
+  ASSERT_EQ(extension_id, extension->id());
+  ASSERT_EQ("1.0", extension->version()->GetString());
+
+  // Make test extension non-idle by opening the extension's browser action
+  // popup. This should cause the installation to be delayed.
+  std::string popup_url = std::string("chrome-extension://")
+      + extension_id + std::string("/popup.html");
+  scoped_ptr<ExtensionHost> extension_host = scoped_ptr<ExtensionHost>(
+      OpenUrlInExtensionPopupHost(extension, GURL(popup_url)));
+
+  // Install version 2 of the extension and check that it is indeed delayed.
+  ASSERT_TRUE(UpdateExtensionWaitForIdle(
+      extension_id, crx_path.AppendASCII("v2.crx"), 0));
+
+  ASSERT_EQ(1u, service->delayed_installs()->size());
+  extension = service->GetExtensionById(extension_id, false);
+  ASSERT_EQ("1.0", extension->version()->GetString());
+
+  // Make the extension idle again by navigating away from the extension's
+  // browser action page. This should not trigger the delayed install.
+  extension_system->process_manager()->UnregisterRenderViewHost(
+      extension_host->render_view_host());
+  ASSERT_EQ(1u, service->delayed_installs()->size());
+
+  // Install version 3 of the extension. Because the extension is idle,
+  // this install should succeed.
+  ASSERT_TRUE(UpdateExtensionWaitForIdle(
+      extension_id, crx_path.AppendASCII("v3.crx"), 0));
+  extension = service->GetExtensionById(extension_id, false);
+  ASSERT_EQ("3.0", extension->version()->GetString());
+
+  // The version 2 delayed install should be cleaned up, and finishing
+  // delayed extension installation shouldn't break anything.
+  ASSERT_EQ(0u, service->delayed_installs()->size());
+  service->MaybeFinishDelayedInstallations();
+  extension = service->GetExtensionById(extension_id, false);
+  ASSERT_EQ("3.0", extension->version()->GetString());
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, Blacklist) {
+  extensions::Blacklist* blacklist =
+      ExtensionSystem::Get(profile())->blacklist();
+
+  // Fake the blacklisting of the extension we're about to install by
+  // pretending that we get a blacklist update which includes it.
+  const std::string kId = "gllekhaobjnhgeagipipnkpmmmpchacm";
+  blacklist->SetFromUpdater(std::vector<std::string>(1, kId), "some-version");
+
+  base::FilePath crx_path = test_data_dir_.AppendASCII("theme_hidpi_crx")
+                                          .AppendASCII("theme_hidpi.crx");
+  EXPECT_FALSE(InstallExtension(crx_path, 0));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/data_deleter.cc b/chrome/browser/extensions/data_deleter.cc
index 46518b0..8d53a42 100644
--- a/chrome/browser/extensions/data_deleter.cc
+++ b/chrome/browser/extensions/data_deleter.cc
@@ -16,6 +16,7 @@
 
 using content::BrowserContext;
 using content::BrowserThread;
+using content::StoragePartition;
 
 namespace extensions {
 
@@ -28,7 +29,7 @@
 
   const GURL& site = Extension::GetBaseURLFromExtensionId(extension_id);
 
-  content::StoragePartition* partition =
+  StoragePartition* partition =
       BrowserContext::GetStoragePartitionForSite(profile, site);
 
   if (storage_origin.SchemeIs(extensions::kExtensionScheme)) {
@@ -41,16 +42,21 @@
     // preserve this code path without checking for isolation because it's
     // simpler than special casing.  This code should go away once we merge
     // the various URLRequestContexts (http://crbug.com/159193).
-    partition->AsyncClearDataForOrigin(
-        content::StoragePartition::kAllStorage,
+    partition->ClearDataForOrigin(
+        StoragePartition::REMOVE_DATA_MASK_ALL &
+            (~StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE),
+        StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
         storage_origin,
         profile->GetRequestContextForExtensions());
   } else {
     // We don't need to worry about the media request context because that
     // shares the same cookie store as the main request context.
-    partition->AsyncClearDataForOrigin(content::StoragePartition::kAllStorage,
-                                       storage_origin,
-                                       partition->GetURLRequestContext());
+    partition->ClearDataForOrigin(
+        StoragePartition::REMOVE_DATA_MASK_ALL &
+            (~StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE),
+        StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
+        storage_origin,
+        partition->GetURLRequestContext());
   }
 
   // Begin removal of the settings for the current extension.
diff --git a/chrome/browser/extensions/event_names.cc b/chrome/browser/extensions/event_names.cc
index ce147c7..2fb55be 100644
--- a/chrome/browser/extensions/event_names.cc
+++ b/chrome/browser/extensions/event_names.cc
@@ -106,6 +106,10 @@
 const char kDeveloperPrivateOnItemStateChanged[] =
     "developerPrivate.onItemStateChanged";
 
+const char kRecoveryOnWriteProgress[] = "recoveryPrivate.onWriteProgress";
+const char kRecoveryOnWriteComplete[] = "recoveryPrivate.onWriteComplete";
+const char kRecoveryOnWriteError[] = "recoveryPrivate.onWriteError";
+
 }  // namespace event_names
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/event_names.h b/chrome/browser/extensions/event_names.h
index 0428921..9e005e2 100644
--- a/chrome/browser/extensions/event_names.h
+++ b/chrome/browser/extensions/event_names.h
@@ -125,6 +125,11 @@
 // DeveloperPrivate.
 extern const char kDeveloperPrivateOnItemStateChanged[];
 
+// ImageWriter
+extern const char kRecoveryOnWriteProgress[];
+extern const char kRecoveryOnWriteComplete[];
+extern const char kRecoveryOnWriteError[];
+
 }  // namespace event_names
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/event_router.cc b/chrome/browser/extensions/event_router.cc
index e3673d5..f0a8650 100644
--- a/chrome/browser/extensions/event_router.cc
+++ b/chrome/browser/extensions/event_router.cc
@@ -109,8 +109,10 @@
     Profile* profile = reinterpret_cast<Profile*>(profile_id);
     if (!g_browser_process->profile_manager()->IsValidProfile(profile))
       return;
-    ActivityLog::GetInstance(profile)->LogEventAction(
-        extension_id, event_name, event_args.get(), std::string());
+    scoped_refptr<Action> action = new Action(
+        extension_id, base::Time::Now(), Action::ACTION_API_EVENT, event_name);
+    action->set_args(event_args.Pass());
+    ActivityLog::GetInstance(profile)->LogAction(action);
   }
 }
 
diff --git a/chrome/browser/extensions/event_router_forwarder_unittest.cc b/chrome/browser/extensions/event_router_forwarder_unittest.cc
index c77ecbf..0e6619a 100644
--- a/chrome/browser/extensions/event_router_forwarder_unittest.cc
+++ b/chrome/browser/extensions/event_router_forwarder_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
 #include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_device_source.h"
 #include "base/test/thread_test_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -92,9 +93,11 @@
         profile_manager_(
             TestingBrowserProcess::GetGlobal()) {
 #if defined(OS_MACOSX)
-    base::PowerMonitor::AllocateSystemIOPorts();
+    base::PowerMonitorDeviceSource::AllocateSystemIOPorts();
 #endif
-    dummy.reset(new base::PowerMonitor);
+    scoped_ptr<base::PowerMonitorSource> power_monitor_source(
+      new base::PowerMonitorDeviceSource());
+    dummy.reset(new base::PowerMonitor(power_monitor_source.Pass()));
   }
 
   virtual void SetUp() {
diff --git a/chrome/browser/extensions/extension_action_manager.cc b/chrome/browser/extensions/extension_action_manager.cc
index c92cad2..ade5095 100644
--- a/chrome/browser/extensions/extension_action_manager.cc
+++ b/chrome/browser/extensions/extension_action_manager.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/extensions/api/system_indicator/system_indicator_manager.h"
 #include "chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h"
 #include "chrome/browser/extensions/extension_action.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
@@ -106,13 +107,24 @@
     std::map<std::string, linked_ptr<ExtensionAction> >* map,
     const std::string& extension_id,
     ActionInfo::Type action_type,
-    const ActionInfo* action_info) {
+    const ActionInfo* action_info,
+    Profile* profile) {
   std::map<std::string, linked_ptr<ExtensionAction> >::const_iterator it =
       map->find(extension_id);
   if (it != map->end())
     return it->second.get();
   if (!action_info)
     return NULL;
+
+  // Only create action info for enabled extensions.
+  // This avoids bugs where actions are recreated just after being removed
+  // in response to NOTIFICATION_EXTENSION_UNLOADED in
+  // ExtensionActionManager::Observe()
+  ExtensionService* service =
+      ExtensionSystem::Get(profile)->extension_service();
+  if (!service->GetExtensionById(extension_id, false))
+    return NULL;
+
   linked_ptr<ExtensionAction> action(new ExtensionAction(
       extension_id, action_type, *action_info));
   (*map)[extension_id] = action;
@@ -129,7 +141,8 @@
     return NULL;
   return GetOrCreateOrNull(&page_actions_, extension.id(),
                            ActionInfo::TYPE_PAGE,
-                           ActionInfo::GetPageActionInfo(&extension));
+                           ActionInfo::GetPageActionInfo(&extension),
+                           profile_);
 }
 
 ExtensionAction* ExtensionActionManager::GetBrowserAction(
@@ -144,7 +157,7 @@
     action_type = ActionInfo::TYPE_PAGE;
   }
   return GetOrCreateOrNull(&browser_actions_, extension.id(),
-                           action_type, action_info);
+                           action_type, action_info, profile_);
 }
 
 ExtensionAction* ExtensionActionManager::GetSystemIndicator(
@@ -158,14 +171,16 @@
 
   return GetOrCreateOrNull(&system_indicators_, extension.id(),
                            ActionInfo::TYPE_SYSTEM_INDICATOR,
-                           ActionInfo::GetSystemIndicatorInfo(&extension));
+                           ActionInfo::GetSystemIndicatorInfo(&extension),
+                           profile_);
 }
 
 ExtensionAction* ExtensionActionManager::GetScriptBadge(
     const extensions::Extension& extension) const {
   return GetOrCreateOrNull(&script_badges_, extension.id(),
                            ActionInfo::TYPE_SCRIPT_BADGE,
-                           ActionInfo::GetScriptBadgeInfo(&extension));
+                           ActionInfo::GetScriptBadgeInfo(&extension),
+                           profile_);
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_blacklist_browsertest.cc b/chrome/browser/extensions/extension_blacklist_browsertest.cc
index a75706c..3f57cb6 100644
--- a/chrome/browser/extensions/extension_blacklist_browsertest.cc
+++ b/chrome/browser/extensions/extension_blacklist_browsertest.cc
@@ -3,151 +3,19 @@
 // found in the LICENSE file.
 
 #include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/blacklist.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_notification_observer.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_constants.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
 
 namespace extensions {
 
 namespace {
 
-// Records notifications, but only for extensions with specific IDs.
-class FilteringNotificationObserver : public content::NotificationObserver {
- public:
-  FilteringNotificationObserver(
-      content::NotificationSource source,
-      const std::set<std::string>& extension_ids)
-      : extension_ids_(extension_ids) {
-    registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, source);
-    registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, source);
-    registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, source);
-  }
-
-  // Checks then clears notifications for our extensions.
-  testing::AssertionResult CheckNotifications(chrome::NotificationType type) {
-    return CheckNotifications(std::vector<chrome::NotificationType>(1, type));
-  }
-
-  // Checks then clears notifications for our extensions.
-  testing::AssertionResult CheckNotifications(chrome::NotificationType t1,
-                                              chrome::NotificationType t2) {
-    std::vector<chrome::NotificationType> types;
-    types.push_back(t1);
-    types.push_back(t2);
-    return CheckNotifications(types);
-  }
-
-  // Checks then clears notifications for our extensions.
-  testing::AssertionResult CheckNotifications(chrome::NotificationType t1,
-                                              chrome::NotificationType t2,
-                                              chrome::NotificationType t3) {
-    std::vector<chrome::NotificationType> types;
-    types.push_back(t1);
-    types.push_back(t2);
-    types.push_back(t3);
-    return CheckNotifications(types);
-  }
-
-  // Checks then clears notifications for our extensions.
-  testing::AssertionResult CheckNotifications(chrome::NotificationType t1,
-                                              chrome::NotificationType t2,
-                                              chrome::NotificationType t3,
-                                              chrome::NotificationType t4,
-                                              chrome::NotificationType t5,
-                                              chrome::NotificationType t6) {
-    std::vector<chrome::NotificationType> types;
-    types.push_back(t1);
-    types.push_back(t2);
-    types.push_back(t3);
-    types.push_back(t4);
-    types.push_back(t5);
-    types.push_back(t6);
-    return CheckNotifications(types);
-  }
-
- private:
-  // content::NotificationObserver implementation.
-  virtual void Observe(int type,
-                       const content::NotificationSource& source,
-                       const content::NotificationDetails& details) OVERRIDE {
-    switch (type) {
-      case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
-        const Extension* extension =
-            content::Details<const InstalledExtensionInfo>(details)->extension;
-        if (extension_ids_.count(extension->id()))
-          notifications_.push_back(static_cast<chrome::NotificationType>(type));
-        break;
-      }
-
-      case chrome::NOTIFICATION_EXTENSION_LOADED: {
-        const Extension* extension =
-            content::Details<const Extension>(details).ptr();
-        if (extension_ids_.count(extension->id()))
-          notifications_.push_back(static_cast<chrome::NotificationType>(type));
-        break;
-      }
-
-      case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
-        UnloadedExtensionInfo* reason =
-            content::Details<UnloadedExtensionInfo>(details).ptr();
-        if (extension_ids_.count(reason->extension->id())) {
-          notifications_.push_back(static_cast<chrome::NotificationType>(type));
-          // The only way that extensions are unloaded in these tests is
-          // by blacklisting.
-          EXPECT_EQ(extension_misc::UNLOAD_REASON_BLACKLIST,
-                    reason->reason);
-        }
-        break;
-      }
-
-      default:
-        NOTREACHED();
-        break;
-    }
-  }
-
-  // Checks then clears notifications for our extensions.
-  testing::AssertionResult CheckNotifications(
-      const std::vector<chrome::NotificationType>& types) {
-    testing::AssertionResult result = (notifications_ == types) ?
-        testing::AssertionSuccess() :
-        testing::AssertionFailure() << "Expected " << Str(types) << ", " <<
-                                       "Got " << Str(notifications_);
-    notifications_.clear();
-    return result;
-  }
-
-  std::string Str(const std::vector<chrome::NotificationType>& types) {
-    std::string str = "[";
-    bool needs_comma = false;
-    for (std::vector<chrome::NotificationType>::const_iterator it =
-         types.begin(); it != types.end(); ++it) {
-      if (needs_comma)
-        str += ",";
-      needs_comma = true;
-      str += base::StringPrintf("%d", *it);
-    }
-    return str + "]";
-  }
-
-  const std::set<std::string> extension_ids_;
-
-  std::vector<chrome::NotificationType> notifications_;
-
-  content::NotificationRegistrar registrar_;
-};
-
 // Stores the paths to CRX files of extensions, and the extension's ID.
 // Use arbitrary extensions; we're just testing blacklisting behavior.
 class CrxInfo {
@@ -243,7 +111,7 @@
 // Stage 1: blacklisting when there weren't any extensions installed when the
 // browser started.
 IN_PROC_BROWSER_TEST_F(ExtensionBlacklistBrowserTest, PRE_Blacklist) {
-  FilteringNotificationObserver notifications(
+  ExtensionNotificationObserver notifications(
       content::NotificationService::AllSources(), GetTestExtensionIDs());
 
   scoped_refptr<const Extension> extension_a =
@@ -361,7 +229,7 @@
 // Stage 2: blacklisting with extensions A and B having been installed,
 // with A actually in the blacklist.
 IN_PROC_BROWSER_TEST_F(ExtensionBlacklistBrowserTest, Blacklist) {
-  FilteringNotificationObserver notifications(
+  ExtensionNotificationObserver notifications(
       content::Source<Profile>(profile()), GetTestExtensionIDs());
 
   scoped_refptr<const Extension> extension_a =
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index ceb7264..05e426c 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -159,7 +159,9 @@
   // The call to OnExtensionInstalled ensures the other extension prefs
   // are set up with the defaults.
   service->extension_prefs()->OnExtensionInstalled(
-      extension, Extension::ENABLED,
+      extension,
+      Extension::ENABLED,
+      extensions::Blacklist::NOT_BLACKLISTED,
       syncer::StringOrdinal::CreateInitialOrdinal());
 
   // Toggling incognito or file access will reload the extension, so wait for
@@ -332,6 +334,20 @@
   }
 };
 
+const Extension* ExtensionBrowserTest::UpdateExtensionWaitForIdle(
+    const std::string& id,
+    const base::FilePath& path,
+    int expected_change) {
+  return InstallOrUpdateExtension(id,
+                                  path,
+                                  INSTALL_UI_TYPE_NONE,
+                                  expected_change,
+                                  Manifest::INTERNAL,
+                                  browser(),
+                                  false,
+                                  true);
+}
+
 const Extension* ExtensionBrowserTest::InstallExtensionFromWebstore(
     const base::FilePath& path,
     int expected_change) {
@@ -341,7 +357,8 @@
                                   expected_change,
                                   Manifest::INTERNAL,
                                   browser(),
-                                  true);
+                                  true,
+                                  false);
 }
 
 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
@@ -350,7 +367,7 @@
     InstallUIType ui_type,
     int expected_change) {
   return InstallOrUpdateExtension(id, path, ui_type, expected_change,
-                                  Manifest::INTERNAL, browser(), false);
+                                  Manifest::INTERNAL, browser(), false, false);
 }
 
 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
@@ -361,7 +378,8 @@
     Browser* browser,
     bool from_webstore) {
   return InstallOrUpdateExtension(id, path, ui_type, expected_change,
-                                  Manifest::INTERNAL, browser, from_webstore);
+                                  Manifest::INTERNAL, browser, from_webstore,
+                                  false);
 }
 
 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
@@ -371,7 +389,7 @@
     int expected_change,
     Manifest::Location install_source) {
   return InstallOrUpdateExtension(id, path, ui_type, expected_change,
-                                  install_source, browser(), false);
+                                  install_source, browser(), false, false);
 }
 
 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
@@ -381,7 +399,8 @@
     int expected_change,
     Manifest::Location install_source,
     Browser* browser,
-    bool from_webstore) {
+    bool from_webstore,
+    bool wait_for_idle) {
   ExtensionService* service = profile()->GetExtensionService();
   service->set_show_extensions_prompts(false);
   size_t num_before = service->extensions()->size();
@@ -412,7 +431,7 @@
     installer->set_expected_id(id);
     installer->set_is_gallery_install(from_webstore);
     installer->set_install_source(install_source);
-    installer->set_install_wait_for_idle(false);
+    installer->set_install_wait_for_idle(wait_for_idle);
     if (!from_webstore) {
       installer->set_off_store_install_allow_reason(
           extensions::CrxInstaller::OffStoreInstallAllowedInTest);
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index 2b750c3..2640696 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -142,6 +142,10 @@
                                     expected_change);
   }
 
+  // Same as UpdateExtension but waits for the extension to be idle first.
+  const extensions::Extension* UpdateExtensionWaitForIdle(
+      const std::string& id, const base::FilePath& path, int expected_change);
+
   // Same as |InstallExtension| but with the normal extension UI showing up
   // (for e.g. info bar on success).
   const extensions::Extension* InstallExtensionWithUI(
@@ -279,7 +283,8 @@
       int expected_change,
       extensions::Manifest::Location install_source,
       Browser* browser,
-      bool from_webstore);
+      bool from_webstore,
+      bool wait_for_idle);
 
   bool WaitForExtensionViewsToLoad();
 
diff --git a/chrome/browser/extensions/extension_creator.cc b/chrome/browser/extensions/extension_creator.cc
index e344338..285aa5c 100644
--- a/chrome/browser/extensions/extension_creator.cc
+++ b/chrome/browser/extensions/extension_creator.cc
@@ -228,7 +228,11 @@
   }
   zip_handle.Close();
 
-  signature_creator->Final(signature);
+  if (!signature_creator->Final(signature)) {
+    error_message_ =
+        l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING);
+    return false;
+  }
   return true;
 }
 
diff --git a/chrome/browser/extensions/extension_disabled_ui.cc b/chrome/browser/extensions/extension_disabled_ui.cc
index d1c3b5c..42767a1 100644
--- a/chrome/browser/extensions/extension_disabled_ui.cc
+++ b/chrome/browser/extensions/extension_disabled_ui.cc
@@ -211,7 +211,7 @@
   }
   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
                  content::Source<Profile>(service->profile()));
-  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_REMOVED,
                  content::Source<Profile>(service->profile()));
 }
 
@@ -318,27 +318,20 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  const Extension* extension = NULL;
-  // The error is invalidated if the extension has been reloaded
-  // or unloaded.
-  if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
-    extension = content::Details<const Extension>(details).ptr();
-  } else {
-    DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_UNLOADED, type);
-    extensions::UnloadedExtensionInfo* info =
-        content::Details<extensions::UnloadedExtensionInfo>(details).ptr();
-    extension = info->extension;
-  }
-  if (extension == extension_) {
-    GlobalErrorServiceFactory::GetForProfile(service_->profile())->
-        RemoveGlobalError(this);
+  // The error is invalidated if the extension has been loaded or removed.
+  DCHECK(type == chrome::NOTIFICATION_EXTENSION_LOADED ||
+         type == chrome::NOTIFICATION_EXTENSION_REMOVED);
+  const Extension* extension = content::Details<const Extension>(details).ptr();
+  if (extension != extension_)
+    return;
+  GlobalErrorServiceFactory::GetForProfile(service_->profile())->
+      RemoveGlobalError(this);
 
-    if (type == chrome::NOTIFICATION_EXTENSION_LOADED)
-      user_response_ = REENABLE;
-    else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED)
-      user_response_ = UNINSTALL;
-    delete this;
-  }
+  if (type == chrome::NOTIFICATION_EXTENSION_LOADED)
+    user_response_ = REENABLE;
+  else if (type == chrome::NOTIFICATION_EXTENSION_REMOVED)
+    user_response_ = UNINSTALL;
+  delete this;
 }
 
 // Globals --------------------------------------------------------------------
diff --git a/chrome/browser/extensions/extension_function.cc b/chrome/browser/extensions/extension_function.cc
index 3e6a397..766a106 100644
--- a/chrome/browser/extensions/extension_function.cc
+++ b/chrome/browser/extensions/extension_function.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/extensions/api/extension_api.h"
 #include "chrome/common/extensions/extension_messages.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/browser/notification_types.h"
@@ -23,6 +24,8 @@
 
 using content::BrowserThread;
 using content::RenderViewHost;
+using extensions::ExtensionAPI;
+using extensions::Feature;
 
 // static
 void ExtensionFunctionDeleteTraits::Destruct(const ExtensionFunction* x) {
@@ -70,7 +73,10 @@
 }
 
 bool ExtensionFunction::HasPermission() {
-  return extension_->HasAPIPermission(name_);
+  Feature::Availability availability =
+      ExtensionAPI::GetSharedInstance()->IsAvailable(
+          name_, extension_, Feature::BLESSED_EXTENSION_CONTEXT, source_url());
+  return availability.is_available();
 }
 
 void ExtensionFunction::OnQuotaExceeded(const std::string& violation_error) {
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index ac2acae..e846c00 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -12,7 +12,9 @@
 #include "base/process/process.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "chrome/browser/extensions/activity_log/activity_action_constants.h"
 #include "chrome/browser/extensions/activity_log/activity_log.h"
+#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
 #include "chrome/browser/extensions/extension_function_registry.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
@@ -36,8 +38,10 @@
 #include "ipc/ipc_message_macros.h"
 #include "webkit/common/resource_type.h"
 
+using extensions::api::activity_log_private::BlockedChromeActivityDetail;
 using extensions::Extension;
 using extensions::ExtensionAPI;
+using extensions::Feature;
 using content::RenderViewHost;
 
 namespace {
@@ -59,15 +63,20 @@
   } else {
     extensions::ActivityLog* activity_log =
         extensions::ActivityLog::GetInstance(profile);
-    activity_log->LogAPIAction(
-        extension_id, api_name, args.get(), std::string());
+    scoped_refptr<extensions::Action> action =
+        new extensions::Action(extension_id,
+                               base::Time::Now(),
+                               extensions::Action::ACTION_API_CALL,
+                               api_name);
+    action->set_args(args.Pass());
+    activity_log->LogAction(action);
   }
 }
 
 void LogFailure(const std::string& extension_id,
                 const std::string& api_name,
                 scoped_ptr<base::ListValue> args,
-                extensions::BlockedAction::Reason reason,
+                BlockedChromeActivityDetail::Reason reason,
                 Profile* profile) {
   // The ActivityLog can only be accessed from the main (UI) thread.  If we're
   // running on the wrong thread, re-dispatch from the main thread.
@@ -83,8 +92,16 @@
   } else {
     extensions::ActivityLog* activity_log =
         extensions::ActivityLog::GetInstance(profile);
-    activity_log->LogBlockedAction(
-        extension_id, api_name, args.get(), reason, std::string());
+    scoped_refptr<extensions::Action> action =
+        new extensions::Action(extension_id,
+                               base::Time::Now(),
+                               extensions::Action::ACTION_API_BLOCKED,
+                               api_name);
+    action->set_args(args.Pass());
+    action->mutable_other()
+        ->SetString(activity_log_constants::kActionBlockedReason,
+                    BlockedChromeActivityDetail::ToString(reason));
+    activity_log->LogAction(action);
   }
 }
 
@@ -269,7 +286,7 @@
     LogFailure(extension->id(),
                params.name,
                args.Pass(),
-               extensions::BlockedAction::ACCESS_DENIED,
+               BlockedChromeActivityDetail::REASON_ACCESS_DENIED,
                profile_cast);
     return;
   }
@@ -289,7 +306,7 @@
     LogFailure(extension->id(),
                params.name,
                args.Pass(),
-               extensions::BlockedAction::ACCESS_DENIED,
+               BlockedChromeActivityDetail::REASON_ACCESS_DENIED,
                profile_cast);
     return;
   }
@@ -309,7 +326,7 @@
     LogFailure(extension->id(),
                params.name,
                args.Pass(),
-               extensions::BlockedAction::QUOTA_EXCEEDED,
+               BlockedChromeActivityDetail::REASON_QUOTA_EXCEEDED,
                profile_cast);
     function->OnQuotaExceeded(violation_error);
   }
@@ -372,7 +389,7 @@
     LogFailure(extension->id(),
                params.name,
                args.Pass(),
-               extensions::BlockedAction::ACCESS_DENIED,
+               BlockedChromeActivityDetail::REASON_ACCESS_DENIED,
                profile());
     return;
   }
@@ -392,7 +409,7 @@
     LogFailure(extension->id(),
                params.name,
                args.Pass(),
-               extensions::BlockedAction::ACCESS_DENIED,
+               BlockedChromeActivityDetail::REASON_ACCESS_DENIED,
                profile());
     return;
   }
@@ -411,7 +428,7 @@
     LogFailure(extension->id(),
                params.name,
                args.Pass(),
-               extensions::BlockedAction::QUOTA_EXCEEDED,
+               BlockedChromeActivityDetail::REASON_QUOTA_EXCEEDED,
                profile());
     function->OnQuotaExceeded(violation_error);
   }
@@ -457,8 +474,8 @@
 // to just the permissions they explicitly request. They should not have access
 // to extension APIs like eg chrome.runtime, chrome.windows, etc. that normally
 // are available without permission.
-// TODO(asargent/kalman) - get rid of this when the features system can express
-// the "non permission" permissions.
+// TODO(mpcomplete): move this to ExtensionFunction::HasPermission (or remove
+// it altogether).
 bool AllowHostedAppAPICall(const Extension& extension,
                            const GURL& source_url,
                            const std::string& function_name) {
@@ -468,11 +485,11 @@
   if (!extension.web_extent().MatchesURL(source_url))
     return false;
 
-  // We just allow the hosted app's explicit permissions, plus chrome.test.
-  scoped_refptr<const extensions::PermissionSet> permissions =
-      extension.GetActivePermissions();
-  return (permissions->HasAccessToFunction(function_name, false) ||
-          StartsWithASCII(function_name, "test.", true /*case_sensitive*/));
+  Feature::Availability availability =
+      ExtensionAPI::GetSharedInstance()->IsAvailable(
+          function_name, &extension, Feature::BLESSED_EXTENSION_CONTEXT,
+          source_url);
+  return availability.is_available();
 }
 
 }  // namespace
diff --git a/chrome/browser/extensions/extension_function_histogram_value.h b/chrome/browser/extensions/extension_function_histogram_value.h
index d3a6e2e..77fcf05 100644
--- a/chrome/browser/extensions/extension_function_histogram_value.h
+++ b/chrome/browser/extensions/extension_function_histogram_value.h
@@ -446,11 +446,11 @@
   WINDOWS_GETALL,
   DELETED_FILEBROWSERPRIVATE_TOGGLEFULLSCREEN,
   APP_CURRENTWINDOWINTERNAL_RESTORE,
-  WEBSOCKETPROXYPRIVATE_GETPASSPORTFORTCP,
+  DELETED_WEBSOCKETPROXYPRIVATE_GETPASSPORTFORTCP,
   PAGEACTION_HIDE,
   DELETED_EXPERIMENTAL_SYSTEMINFO_STORAGE_GET,
   DOWNLOADS_ACCEPTDANGER,
-  WEBSOCKETPROXYPRIVATE_GETURLFORTCP,
+  DELETED_WEBSOCKETPROXYPRIVATE_GETURLFORTCP,
   FILEBROWSERPRIVATE_GETSIZESTATS,
   DOWNLOADS_RESUME,
   COOKIES_GETALLCOOKIESTORES,
@@ -580,6 +580,13 @@
   SYSTEM_STORAGE_REMOVEALLAVAILABLECAPACITYWATCHES,
   DOWNLOADS_REMOVEFILE,
   DOWNLOADS_SHOWDEFAULTFOLDER,
+  INFOBARS_SHOW,
+  DOWNLOADS_SETSHELFENABLED,
+  RECOVERYPRIVATE_WRITEFROMURL,
+  RECOVERYPRIVATE_WRITEFROMFILE,
+  RECOVERYPRIVATE_CANCELWRITE,
+  RECOVERYPRIVATE_DESTROYPARTITIONS,
+  FEEDBACKPRIVATE_GETSTRINGS,
   ENUM_BOUNDARY // Last entry: Add new entries above.
 };
 
diff --git a/chrome/browser/extensions/extension_function_registry.cc b/chrome/browser/extensions/extension_function_registry.cc
index 774328d..ddf7923 100644
--- a/chrome/browser/extensions/extension_function_registry.cc
+++ b/chrome/browser/extensions/extension_function_registry.cc
@@ -4,13 +4,11 @@
 
 #include "chrome/browser/extensions/extension_function_registry.h"
 
-#include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h"
 #include "chrome/browser/extensions/api/identity/experimental_identity_api.h"
 #include "chrome/browser/extensions/api/preference/chrome_direct_setting.h"
 #include "chrome/browser/extensions/api/preference/preference_api.h"
 #include "chrome/browser/extensions/api/runtime/runtime_api.h"
 #include "chrome/browser/extensions/api/web_request/web_request_api.h"
-#include "chrome/browser/extensions/api/webstore_private/webstore_private_api.h"
 #include "chrome/browser/rlz/rlz_extension_api.h"
 #include "chrome/common/extensions/api/generated_api.h"
 
@@ -31,22 +29,6 @@
 
   // Register all functions here.
 
-  // Browsing Data.
-  RegisterFunction<BrowsingDataSettingsFunction>();
-  RegisterFunction<RemoveBrowsingDataFunction>();
-  RegisterFunction<RemoveAppCacheFunction>();
-  RegisterFunction<RemoveCacheFunction>();
-  RegisterFunction<RemoveCookiesFunction>();
-  RegisterFunction<RemoveDownloadsFunction>();
-  RegisterFunction<RemoveFileSystemsFunction>();
-  RegisterFunction<RemoveFormDataFunction>();
-  RegisterFunction<RemoveHistoryFunction>();
-  RegisterFunction<RemoveIndexedDBFunction>();
-  RegisterFunction<RemoveLocalStorageFunction>();
-  RegisterFunction<RemovePluginDataFunction>();
-  RegisterFunction<RemovePasswordsFunction>();
-  RegisterFunction<RemoveWebSQLFunction>();
-
   // RLZ (not supported on ChromeOS yet).
 #if defined(ENABLE_RLZ) && !defined(OS_CHROMEOS)
   RegisterFunction<RlzRecordProductEventFunction>();
@@ -70,18 +52,6 @@
   RegisterFunction<
       extensions::chromedirectsetting::ClearDirectSettingFunction>();
 
-  // WebstorePrivate.
-  RegisterFunction<extensions::GetBrowserLoginFunction>();
-  RegisterFunction<extensions::GetStoreLoginFunction>();
-  RegisterFunction<extensions::SetStoreLoginFunction>();
-  RegisterFunction<extensions::InstallBundleFunction>();
-  RegisterFunction<extensions::BeginInstallWithManifestFunction>();
-  RegisterFunction<extensions::CompleteInstallFunction>();
-  RegisterFunction<extensions::EnableAppLauncherFunction>();
-  RegisterFunction<extensions::GetWebGLStatusFunction>();
-  RegisterFunction<extensions::GetIsLauncherEnabledFunction>();
-  RegisterFunction<extensions::IsInIncognitoModeFunction>();
-
   // Runtime
   RegisterFunction<extensions::RuntimeGetBackgroundPageFunction>();
   RegisterFunction<extensions::RuntimeSetUninstallUrlFunction>();
diff --git a/chrome/browser/extensions/extension_get_views_apitest.cc b/chrome/browser/extensions/extension_get_views_apitest.cc
index 2ce3e7a..aa67e48 100644
--- a/chrome/browser/extensions/extension_get_views_apitest.cc
+++ b/chrome/browser/extensions/extension_get_views_apitest.cc
@@ -2,14 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/command_line.h"
 #include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/common/chrome_switches.h"
 
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_GetViews) {
-  // TODO(finnur): Remove once infobars are no longer experimental (bug 39511).
-  CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
-
   ASSERT_TRUE(RunExtensionTest("get_views")) << message_;
 }
diff --git a/chrome/browser/extensions/extension_install_prompt.cc b/chrome/browser/extensions/extension_install_prompt.cc
index e0c2f6f..37154bf 100644
--- a/chrome/browser/extensions/extension_install_prompt.cc
+++ b/chrome/browser/extensions/extension_install_prompt.cc
@@ -191,7 +191,8 @@
       extension_(NULL),
       bundle_(NULL),
       average_rating_(0.0),
-      rating_count_(0) {
+      rating_count_(0),
+      show_user_count_(false) {
 }
 
 ExtensionInstallPrompt::Prompt::~Prompt() {
@@ -224,10 +225,12 @@
 
 void ExtensionInstallPrompt::Prompt::SetInlineInstallWebstoreData(
     const std::string& localized_user_count,
+    bool show_user_count,
     double average_rating,
     int rating_count) {
   CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
   localized_user_count_ = localized_user_count;
+  show_user_count_ = show_user_count;
   average_rating_ = average_rating;
   rating_count_ = rating_count;
 }
@@ -375,9 +378,14 @@
 
 string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
   CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
-  return l10n_util::GetStringFUTF16(
-      IDS_EXTENSION_USER_COUNT,
-      UTF8ToUTF16(localized_user_count_));
+
+  if (show_user_count_) {
+    return l10n_util::GetStringFUTF16(
+        IDS_EXTENSION_USER_COUNT,
+        UTF8ToUTF16(localized_user_count_));
+  } else {
+    return string16();
+  }
 }
 
 size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
diff --git a/chrome/browser/extensions/extension_install_prompt.h b/chrome/browser/extensions/extension_install_prompt.h
index 6fd0574..1a98bad 100644
--- a/chrome/browser/extensions/extension_install_prompt.h
+++ b/chrome/browser/extensions/extension_install_prompt.h
@@ -77,6 +77,7 @@
     // Sets the permission list details for this prompt.
     void SetPermissionsDetails(const std::vector<string16>& details);
     void SetInlineInstallWebstoreData(const std::string& localized_user_count,
+                                      bool show_user_count,
                                       double average_rating,
                                       int rating_count);
     void SetOAuthIssueAdvice(const IssueAdviceInfo& issue_advice);
@@ -172,6 +173,10 @@
     double average_rating_;
     int rating_count_;
 
+    // Whether we should display the user count (we anticipate this will be
+    // false if localized_user_count_ represents the number zero).
+    bool show_user_count_;
+
     std::vector<base::FilePath> retained_files_;
   };
 
diff --git a/chrome/browser/extensions/extension_install_ui_browsertest.cc b/chrome/browser/extensions/extension_install_ui_browsertest.cc
index d5b86ee..4687be3 100644
--- a/chrome/browser/extensions/extension_install_ui_browsertest.cc
+++ b/chrome/browser/extensions/extension_install_ui_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/strings/string_util.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -19,15 +20,12 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
 #include "chrome/common/url_constants.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/common/id_util.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::WebContents;
 using extensions::Extension;
 
@@ -52,8 +50,12 @@
   // Install the given theme from the data dir and verify expected name.
   void InstallThemeAndVerify(const char* theme_name,
                              const std::string& expected_name) {
+    // If there is already a theme installed, the current theme should be
+    // disabled and the new one installed + enabled.
+    int expected_change = GetTheme() ? 0 : 1;
     const base::FilePath theme_path = test_data_dir_.AppendASCII(theme_name);
-    ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_path, 1, browser()));
+    ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_path, expected_change,
+        browser()));
     const Extension* theme = GetTheme();
     ASSERT_TRUE(theme);
     ASSERT_EQ(theme->name(), expected_name);
@@ -76,7 +78,7 @@
                        MAYBE_TestThemeInstallUndoResetsToDefault) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -90,7 +92,7 @@
   ASSERT_EQ(NULL, GetTheme());
 
   // Set the same theme twice and undo to verify we go back to default theme.
-  ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_crx, 1, browser()));
+  ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(theme_crx, 0, browser()));
   theme = GetTheme();
   ASSERT_TRUE(theme);
   ASSERT_EQ(theme_id, theme->id());
@@ -106,7 +108,7 @@
                        TestThemeInstallUndoResetsToPreviousTheme) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/extension_keybinding_apitest.cc b/chrome/browser/extensions/extension_keybinding_apitest.cc
index 8951a60..07906a1 100644
--- a/chrome/browser/extensions/extension_keybinding_apitest.cc
+++ b/chrome/browser/extensions/extension_keybinding_apitest.cc
@@ -41,8 +41,6 @@
     // treated like BrowserActions and the PageAction test starts failing.
     CommandLine::ForCurrentProcess()->AppendSwitchASCII(
         switches::kScriptBadges, "1");
-    CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kEnableExperimentalExtensionApis);
   }
   virtual ~ScriptBadgesCommandsApiTest() {}
 };
@@ -242,4 +240,4 @@
   ASSERT_TRUE(result);
 }
 
-}  // extensions
+}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_keybinding_apitest_new.cc b/chrome/browser/extensions/extension_keybinding_apitest_new.cc
index 32ae453..21d8da2 100644
--- a/chrome/browser/extensions/extension_keybinding_apitest_new.cc
+++ b/chrome/browser/extensions/extension_keybinding_apitest_new.cc
@@ -41,8 +41,6 @@
     // treated like BrowserActions and the PageAction test starts failing.
     CommandLine::ForCurrentProcess()->AppendSwitchASCII(
         switches::kScriptBadges, "1");
-    CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kEnableExperimentalExtensionApis);
   }
   virtual ~ScriptBadgesCommandsApiTest() {}
 };
@@ -195,4 +193,4 @@
   ASSERT_TRUE(RunExtensionTest("keybinding/synthesized")) << message_;
 }
 
-}  // extensions
+}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_messages_apitest.cc b/chrome/browser/extensions/extension_messages_apitest.cc
index 4275441..466ca2f 100644
--- a/chrome/browser/extensions/extension_messages_apitest.cc
+++ b/chrome/browser/extensions/extension_messages_apitest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/event_router.h"
 #include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_prefs.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/extensions/test_extension_dir.h"
 #include "chrome/browser/profiles/profile.h"
@@ -142,9 +143,14 @@
   };
 
   Result CanConnectAndSendMessages(const std::string& extension_id) {
+    return CanConnectAndSendMessages(browser(), extension_id);
+  }
+
+  Result CanConnectAndSendMessages(Browser* browser,
+                                   const std::string& extension_id) {
     int result;
     CHECK(content::ExecuteScriptAndExtractInt(
-        browser()->tab_strip_model()->GetActiveWebContents(),
+        browser->tab_strip_model()->GetActiveWebContents(),
         "assertions.canConnectAndSendMessages('" + extension_id + "')",
         &result));
     return static_cast<Result>(result);
@@ -208,7 +214,7 @@
   }
 
   const Extension* LoadChromiumConnectableExtension() {
-    web_connectable_dir_.WriteManifest(base::StringPrintf(
+    return LoadExtensionIntoDir(&web_connectable_dir_, base::StringPrintf(
         "{"
         "  \"name\": \"chromium_connectable\","
         "  %s,"
@@ -217,19 +223,15 @@
         "  }"
         "}",
         common_manifest()));
-    WriteBackgroundHtml(&web_connectable_dir_);
-    return LoadExtension(web_connectable_dir_.unpacked_path());
   }
 
   scoped_refptr<const Extension> LoadNotConnectableExtension() {
-    not_connectable_dir_.WriteManifest(base::StringPrintf(
+    return LoadExtensionIntoDir(&not_connectable_dir_, base::StringPrintf(
         "{"
         "  \"name\": \"not_connectable\","
         "  %s"
         "}",
         common_manifest()));
-    WriteBackgroundHtml(&not_connectable_dir_);
-    return LoadExtension(not_connectable_dir_.unpacked_path());
   }
 
   void InitializeTestServer() {
@@ -242,8 +244,10 @@
   }
 
  private:
-  void WriteBackgroundHtml(TestExtensionDir* extension_dir) {
-    extension_dir->WriteFile(FILE_PATH_LITERAL("background.js"),
+  const Extension* LoadExtensionIntoDir(TestExtensionDir* dir,
+                                        const std::string& manifest) {
+    dir->WriteManifest(manifest);
+    dir->WriteFile(FILE_PATH_LITERAL("background.js"),
         "chrome.runtime.onMessageExternal.addListener(\n"
         "    function(message, sender, reply) {\n"
         "  reply({ message: message, sender: sender });\n"
@@ -253,6 +257,7 @@
         "    port.postMessage({ message: message, sender: port.sender });\n"
         "  });\n"
         "});\n");
+    return LoadExtension(dir->unpacked_path());
   }
 
   const char* common_manifest() {
@@ -349,5 +354,30 @@
             CanConnectAndSendMessages(not_connectable->id()));
 }
 
+// Tests connection from incognito tabs. Spanning mode only.
+//
+// TODO(kalman): ensure that we exercise split vs spanning incognito logic
+// somewhere. This is a test that should be shared with the content script logic
+// so it's not really our specific concern for web connectable.
+IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, FromIncognito) {
+  InitializeTestServer();
+
+  const Extension* chromium_connectable = LoadChromiumConnectableExtension();
+  ASSERT_TRUE(chromium_connectable);
+
+  Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord(
+      profile()->GetOffTheRecordProfile(),
+      chromium_org_url());
+
+  // No connection because incognito enabled hasn't been set.
+  const std::string& id = chromium_connectable->id();
+  EXPECT_EQ(COULD_NOT_ESTABLISH_CONNECTION_ERROR,
+            CanConnectAndSendMessages(incognito_browser, id));
+
+  // Then yes.
+  ExtensionPrefs::Get(profile())->SetIsIncognitoEnabled(id, true);
+  EXPECT_EQ(OK, CanConnectAndSendMessages(incognito_browser, id));
+}
+
 }  // namespace
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_notification_observer.cc b/chrome/browser/extensions/extension_notification_observer.cc
new file mode 100644
index 0000000..7f5baf9
--- /dev/null
+++ b/chrome/browser/extensions/extension_notification_observer.cc
@@ -0,0 +1,141 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/extensions/extension_notification_observer.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/common/extensions/extension.h"
+
+namespace extensions {
+
+namespace {
+
+std::string Str(const std::vector<chrome::NotificationType>& types) {
+  std::string str = "[";
+  bool needs_comma = false;
+  for (std::vector<chrome::NotificationType>::const_iterator it =
+       types.begin(); it != types.end(); ++it) {
+    if (needs_comma)
+      str += ",";
+    needs_comma = true;
+    str += base::StringPrintf("%d", *it);
+  }
+  str += "]";
+  return str;
+}
+
+}  // namespace
+
+ExtensionNotificationObserver::ExtensionNotificationObserver(
+    content::NotificationSource source,
+    const std::set<std::string>& extension_ids)
+    : extension_ids_(extension_ids) {
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, source);
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, source);
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, source);
+}
+
+ExtensionNotificationObserver::~ExtensionNotificationObserver() {}
+
+testing::AssertionResult ExtensionNotificationObserver::CheckNotifications() {
+  return CheckNotifications(std::vector<chrome::NotificationType>());
+}
+
+testing::AssertionResult ExtensionNotificationObserver::CheckNotifications(
+    chrome::NotificationType type) {
+  return CheckNotifications(std::vector<chrome::NotificationType>(1, type));
+}
+
+testing::AssertionResult ExtensionNotificationObserver::CheckNotifications(
+    chrome::NotificationType t1,
+    chrome::NotificationType t2) {
+  std::vector<chrome::NotificationType> types;
+  types.push_back(t1);
+  types.push_back(t2);
+  return CheckNotifications(types);
+}
+
+testing::AssertionResult ExtensionNotificationObserver::CheckNotifications(
+    chrome::NotificationType t1,
+    chrome::NotificationType t2,
+    chrome::NotificationType t3) {
+  std::vector<chrome::NotificationType> types;
+  types.push_back(t1);
+  types.push_back(t2);
+  types.push_back(t3);
+  return CheckNotifications(types);
+}
+
+testing::AssertionResult ExtensionNotificationObserver::CheckNotifications(
+    chrome::NotificationType t1,
+    chrome::NotificationType t2,
+    chrome::NotificationType t3,
+    chrome::NotificationType t4,
+    chrome::NotificationType t5,
+    chrome::NotificationType t6) {
+  std::vector<chrome::NotificationType> types;
+  types.push_back(t1);
+  types.push_back(t2);
+  types.push_back(t3);
+  types.push_back(t4);
+  types.push_back(t5);
+  types.push_back(t6);
+  return CheckNotifications(types);
+}
+
+// content::NotificationObserver implementation.
+void ExtensionNotificationObserver::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  switch (type) {
+    case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
+      const Extension* extension =
+          content::Details<const InstalledExtensionInfo>(details)->extension;
+      if (extension_ids_.count(extension->id()))
+        notifications_.push_back(static_cast<chrome::NotificationType>(type));
+      break;
+    }
+
+    case chrome::NOTIFICATION_EXTENSION_LOADED: {
+      const Extension* extension =
+          content::Details<const Extension>(details).ptr();
+      if (extension_ids_.count(extension->id()))
+        notifications_.push_back(static_cast<chrome::NotificationType>(type));
+      break;
+    }
+
+    case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
+      UnloadedExtensionInfo* reason =
+          content::Details<UnloadedExtensionInfo>(details).ptr();
+      if (extension_ids_.count(reason->extension->id())) {
+        notifications_.push_back(static_cast<chrome::NotificationType>(type));
+        // The only way that extensions are unloaded in these tests is
+        // by blacklisting.
+        EXPECT_EQ(extension_misc::UNLOAD_REASON_BLACKLIST,
+                  reason->reason);
+      }
+      break;
+    }
+
+    default:
+      NOTREACHED();
+      break;
+  }
+}
+
+testing::AssertionResult ExtensionNotificationObserver::CheckNotifications(
+    const std::vector<chrome::NotificationType>& types) {
+  testing::AssertionResult result = (notifications_ == types) ?
+      testing::AssertionSuccess() :
+      testing::AssertionFailure() << "Expected " << Str(types) << ", " <<
+                                     "Got " << Str(notifications_);
+  notifications_.clear();
+  return result;
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_notification_observer.h b/chrome/browser/extensions/extension_notification_observer.h
new file mode 100644
index 0000000..facb403
--- /dev/null
+++ b/chrome/browser/extensions/extension_notification_observer.h
@@ -0,0 +1,69 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_NOTIFICATION_OBSERVER_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_NOTIFICATION_OBSERVER_H_
+
+#include <set>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+// Records LOADED, INSTALLED, and UNLOADED notifications for extensions with
+// specific IDs. Use in tests only.
+class ExtensionNotificationObserver : public content::NotificationObserver {
+ public:
+  ExtensionNotificationObserver(content::NotificationSource source,
+                                const std::set<std::string>& extension_ids);
+
+  virtual ~ExtensionNotificationObserver();
+
+  // Each of these methods returns a testing::AssertionSuccess if exactly those
+  // notifications occurred for any extensions in |extension_ids|, and no more,
+  // since the last time any of these methods were called.
+  testing::AssertionResult CheckNotifications() WARN_UNUSED_RESULT;
+  testing::AssertionResult CheckNotifications(
+      chrome::NotificationType type) WARN_UNUSED_RESULT;
+  testing::AssertionResult CheckNotifications(
+      chrome::NotificationType t1,
+      chrome::NotificationType t2) WARN_UNUSED_RESULT;
+  testing::AssertionResult CheckNotifications(
+      chrome::NotificationType t1,
+      chrome::NotificationType t2,
+      chrome::NotificationType t3) WARN_UNUSED_RESULT;
+  testing::AssertionResult CheckNotifications(
+      chrome::NotificationType t1,
+      chrome::NotificationType t2,
+      chrome::NotificationType t3,
+      chrome::NotificationType t4,
+      chrome::NotificationType t5,
+      chrome::NotificationType t6) WARN_UNUSED_RESULT;
+
+ private:
+  // content::NotificationObserver implementation.
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
+  // Checks then clears notifications for our extensions.
+  testing::AssertionResult CheckNotifications(
+      const std::vector<chrome::NotificationType>& types);
+
+  const std::set<std::string> extension_ids_;
+  std::vector<chrome::NotificationType> notifications_;
+  content::NotificationRegistrar registrar_;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_NOTIFICATION_OBSERVER_H_
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index 904527d..6447ad3 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -1163,12 +1163,13 @@
 void ExtensionPrefs::OnExtensionInstalled(
     const Extension* extension,
     Extension::State initial_state,
+    Blacklist::BlacklistState blacklist_state,
     const syncer::StringOrdinal& page_ordinal) {
   ScopedExtensionPrefUpdate update(prefs_, extension->id());
   DictionaryValue* extension_dict = update.Get();
   const base::Time install_time = time_provider_->GetCurrentTime();
   PopulateExtensionInfoPrefs(extension, install_time, initial_state,
-                             extension_dict);
+                             blacklist_state, extension_dict);
   FinishExtensionInfoPrefs(extension->id(), install_time,
                            extension->RequiresSortOrdinal(),
                            page_ordinal, extension_dict);
@@ -1329,11 +1330,12 @@
 void ExtensionPrefs::SetDelayedInstallInfo(
     const Extension* extension,
     Extension::State initial_state,
+    Blacklist::BlacklistState blacklist_state,
     DelayReason delay_reason,
     const syncer::StringOrdinal& page_ordinal) {
   DictionaryValue* extension_dict = new DictionaryValue();
   PopulateExtensionInfoPrefs(extension, time_provider_->GetCurrentTime(),
-                             initial_state, extension_dict);
+                             initial_state, blacklist_state, extension_dict);
 
   // Add transient data that is needed by FinishDelayedInstallInfo(), but
   // should not be in the final extension prefs. All entries here should have
@@ -1766,6 +1768,7 @@
     const Extension* extension,
     const base::Time install_time,
     Extension::State initial_state,
+    Blacklist::BlacklistState blacklist_state,
     DictionaryValue* extension_dict) {
   // Leave the state blank for component extensions so that old chrome versions
   // loading new profiles do not fail in GetInstalledExtensionInfo. Older
@@ -1787,6 +1790,8 @@
   extension_dict->Set(kPrefInstallTime,
                       Value::CreateStringValue(
                           base::Int64ToString(install_time.ToInternalValue())));
+  if (blacklist_state == Blacklist::BLACKLISTED)
+    extension_dict->Set(kPrefBlacklist, Value::CreateBooleanValue(true));
 
   base::FilePath::StringType path = MakePathRelative(install_directory_,
                                                      extension->path());
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index 354420e..6a512a8 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -13,6 +13,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "chrome/browser/extensions/blacklist.h"
 #include "chrome/browser/extensions/extension_scoped_prefs.h"
 #include "chrome/browser/prefs/scoped_user_pref_update.h"
 #include "chrome/common/extensions/extension.h"
@@ -187,6 +188,7 @@
   // for the App.
   void OnExtensionInstalled(const Extension* extension,
                             Extension::State initial_state,
+                            Blacklist::BlacklistState blacklist_state,
                             const syncer::StringOrdinal& page_ordinal);
 
   // Called when an extension is uninstalled, so that prefs get cleaned up.
@@ -370,6 +372,9 @@
 
   // Returns true if the user enabled this extension to be loaded in incognito
   // mode.
+  //
+  // IMPORTANT: you probably want to use ExtensionService::IsIncognitoEnabled
+  // instead of this method.
   bool IsIncognitoEnabled(const std::string& extension_id);
   void SetIsIncognitoEnabled(const std::string& extension_id, bool enabled);
 
@@ -408,6 +413,7 @@
   // to install it.
   void SetDelayedInstallInfo(const Extension* extension,
                              Extension::State initial_state,
+                             Blacklist::BlacklistState blacklist_state,
                              DelayReason delay_reason,
                              const syncer::StringOrdinal& page_ordinal);
 
@@ -585,6 +591,7 @@
   void PopulateExtensionInfoPrefs(const Extension* extension,
                                   const base::Time install_time,
                                   Extension::State initial_state,
+                                  Blacklist::BlacklistState blacklist_state,
                                   base::DictionaryValue* extension_dict);
 
   // Helper function to complete initialization of the values in
diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc
index 08d07d6..c7034d1 100644
--- a/chrome/browser/extensions/extension_prefs_unittest.cc
+++ b/chrome/browser/extensions/extension_prefs_unittest.cc
@@ -451,7 +451,9 @@
         path, Manifest::INTERNAL, manifest, Extension::NO_FLAGS, id, &errors);
     ASSERT_TRUE(extension.get()) << errors;
     ASSERT_EQ(id, extension->id());
-    prefs()->SetDelayedInstallInfo(extension.get(), Extension::ENABLED,
+    prefs()->SetDelayedInstallInfo(extension.get(),
+                                   Extension::ENABLED,
+                                   Blacklist::NOT_BLACKLISTED,
                                    ExtensionPrefs::DELAY_REASON_WAIT_FOR_IDLE,
                                    syncer::StringOrdinal());
   }
@@ -551,9 +553,10 @@
   virtual void Initialize() OVERRIDE {
     extension_ = prefs_.AddExtension("on_extension_installed");
     EXPECT_FALSE(prefs()->IsExtensionDisabled(extension_->id()));
-    prefs()->OnExtensionInstalled(
-        extension_.get(), Extension::DISABLED,
-        syncer::StringOrdinal());
+    prefs()->OnExtensionInstalled(extension_.get(),
+                                  Extension::DISABLED,
+                                  Blacklist::NOT_BLACKLISTED,
+                                  syncer::StringOrdinal());
   }
 
   virtual void Verify() OVERRIDE {
@@ -571,7 +574,9 @@
   virtual void Initialize() OVERRIDE {
     extension_ = prefs_.AddExtension("on_extension_installed");
     EXPECT_FALSE(prefs()->WasAppDraggedByUser(extension_->id()));
-    prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
+    prefs()->OnExtensionInstalled(extension_.get(),
+                                  Extension::ENABLED,
+                                  Blacklist::NOT_BLACKLISTED,
                                   syncer::StringOrdinal());
   }
 
diff --git a/chrome/browser/extensions/extension_resource_request_policy_apitest.cc b/chrome/browser/extensions/extension_resource_request_policy_apitest.cc
index ef4b1c3..6049f6e 100644
--- a/chrome/browser/extensions/extension_resource_request_policy_apitest.cc
+++ b/chrome/browser/extensions/extension_resource_request_policy_apitest.cc
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
@@ -13,10 +15,6 @@
 #include "net/dns/mock_host_resolver.h"
 #include "url/gurl.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 class ExtensionResourceRequestPolicyTest : public ExtensionApiTest {
  protected:
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
@@ -32,7 +30,7 @@
 IN_PROC_BROWSER_TEST_F(ExtensionResourceRequestPolicyTest, OriginPrivileges) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 5439e10..6035d7c 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -66,8 +66,6 @@
 #include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/themes/theme_service.h"
-#include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
 #include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
 #include "chrome/browser/ui/webui/theme_source.h"
@@ -436,6 +434,10 @@
   return &blacklisted_extensions_;
 }
 
+const ExtensionSet* ExtensionService::delayed_installs() const {
+  return &delayed_installs_;
+}
+
 scoped_ptr<const ExtensionSet>
     ExtensionService::GenerateInstalledExtensionsSet() const {
   scoped_ptr<ExtensionSet> installed_extensions(new ExtensionSet());
@@ -1119,16 +1121,6 @@
       content::Source<Profile>(profile_),
       content::Details<UnloadedExtensionInfo>(&details));
 
-#if defined(ENABLE_THEMES)
-  // If the current theme is being unloaded, tell ThemeService to revert back
-  // to the default theme.
-  if (reason != extension_misc::UNLOAD_REASON_UPDATE && extension->is_theme()) {
-    ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile_);
-    if (extension->id() == theme_service->GetThemeID())
-      theme_service->UseDefaultTheme();
-  }
-#endif
-
   for (content::RenderProcessHost::iterator i(
           content::RenderProcessHost::AllHostsIterator());
        !i.IsAtEnd(); i.Advance()) {
@@ -1873,24 +1865,21 @@
   extension_runtime_data_.erase(extension_id);
 
   if (disabled_extensions_.Contains(extension->id())) {
-    UnloadedExtensionInfo details(extension.get(), reason);
-    details.already_disabled = true;
     disabled_extensions_.Remove(extension->id());
-    content::NotificationService::current()->Notify(
-        chrome::NOTIFICATION_EXTENSION_UNLOADED,
-        content::Source<Profile>(profile_),
-        content::Details<UnloadedExtensionInfo>(&details));
     // Make sure the profile cleans up its RequestContexts when an already
     // disabled extension is unloaded (since they are also tracking the disabled
     // extensions).
     system_->UnregisterExtensionWithRequestContexts(extension_id, reason);
-    return;
+  } else {
+    // Remove the extension from our list.
+    extensions_.Remove(extension->id());
+    NotifyExtensionUnloaded(extension.get(), reason);
   }
 
-  // Remove the extension from our list.
-  extensions_.Remove(extension->id());
-
-  NotifyExtensionUnloaded(extension.get(), reason);
+  content::NotificationService::current()->Notify(
+      chrome::NOTIFICATION_EXTENSION_REMOVED,
+      content::Source<Profile>(profile_),
+      content::Details<const Extension>(extension.get()));
 }
 
 void ExtensionService::UnloadAllExtensions() {
@@ -1950,15 +1939,6 @@
               extension_paths))) {
     NOTREACHED();
   }
-
-#if defined(ENABLE_THEMES)
-  // Also garbage-collect themes.  We check |profile_| to be
-  // defensive; in the future, we may call GarbageCollectExtensions()
-  // from somewhere other than Init() (e.g., in a timer).
-  if (profile_) {
-    ThemeServiceFactory::GetForProfile(profile_)->RemoveUnusedThemes();
-  }
-#endif
 }
 
 void ExtensionService::SyncExtensionChangeIfNeeded(const Extension& extension) {
@@ -2034,7 +2014,8 @@
   if (extension_prefs_->IsExtensionBlacklisted(extension->id())) {
     // Only prefs is checked for the blacklist. We rely on callers to check the
     // blacklist before calling into here, e.g. CrxInstaller checks before
-    // installation, we check when loading installed extensions.
+    // installation then threads through the install and pending install flow
+    // of this class, and we check when loading installed extensions.
     blacklisted_extensions_.Insert(extension);
   } else if (!reloading &&
              extension_prefs_->IsExtensionDisabled(extension->id())) {
@@ -2086,6 +2067,7 @@
 
     AddNewOrUpdatedExtension(extension,
                              Extension::ENABLED_COMPONENT,
+                             extensions::Blacklist::NOT_BLACKLISTED,
                              syncer::StringOrdinal());
     return;
   }
@@ -2324,6 +2306,7 @@
     const Extension* extension,
     const syncer::StringOrdinal& page_ordinal,
     bool has_requirement_errors,
+    extensions::Blacklist::BlacklistState blacklist_state,
     bool wait_for_idle) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
@@ -2380,6 +2363,18 @@
     extension_prefs_->ClearDisableReasons(id);
   }
 
+  if (blacklist_state == extensions::Blacklist::BLACKLISTED) {
+    // Installation of a blacklisted extension can happen from sync, policy,
+    // etc, where to maintain consistency we need to install it, just never
+    // load it (see AddExtension). Usually it should be the job of callers to
+    // incercept blacklisted extension earlier (e.g. CrxInstaller, before even
+    // showing the install dialogue).
+    extension_prefs()->AcknowledgeBlacklistedExtension(id);
+    UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.SilentInstall",
+                              extension->location(),
+                              Manifest::NUM_LOCATIONS);
+  }
+
   if (!GetInstalledExtension(extension->id())) {
     UMA_HISTOGRAM_ENUMERATION("Extensions.InstallType",
                               extension->GetType(), 100);
@@ -2401,8 +2396,12 @@
   const Extension::State initial_state =
       initial_enable ? Extension::ENABLED : Extension::DISABLED;
   if (ShouldDelayExtensionUpdate(id, wait_for_idle)) {
-    extension_prefs_->SetDelayedInstallInfo(extension, initial_state,
-        extensions::ExtensionPrefs::DELAY_REASON_WAIT_FOR_IDLE, page_ordinal);
+    extension_prefs_->SetDelayedInstallInfo(
+        extension,
+        initial_state,
+        blacklist_state,
+        extensions::ExtensionPrefs::DELAY_REASON_WAIT_FOR_IDLE,
+        page_ordinal);
 
     // Transfer ownership of |extension|.
     delayed_installs_.Insert(extension);
@@ -2419,32 +2418,42 @@
 
   ImportStatus status = SatisfyImports(extension);
   if (installs_delayed_for_gc()) {
-    extension_prefs_->SetDelayedInstallInfo(extension, initial_state,
-        extensions::ExtensionPrefs::DELAY_REASON_GC, page_ordinal);
+    extension_prefs_->SetDelayedInstallInfo(
+        extension,
+        initial_state,
+        blacklist_state,
+        extensions::ExtensionPrefs::DELAY_REASON_GC,
+        page_ordinal);
     delayed_installs_.Insert(extension);
   } else if (status != IMPORT_STATUS_OK) {
     if (status == IMPORT_STATUS_UNSATISFIED) {
-      extension_prefs_->SetDelayedInstallInfo(extension, initial_state,
+      extension_prefs_->SetDelayedInstallInfo(
+          extension,
+          initial_state,
+          blacklist_state,
           extensions::ExtensionPrefs::DELAY_REASON_WAIT_FOR_IMPORTS,
           page_ordinal);
       delayed_installs_.Insert(extension);
     }
   } else {
-    AddNewOrUpdatedExtension(extension, initial_state, page_ordinal);
+    AddNewOrUpdatedExtension(extension,
+                             initial_state,
+                             blacklist_state,
+                             page_ordinal);
   }
 }
 
 void ExtensionService::AddNewOrUpdatedExtension(
     const Extension* extension,
     Extension::State initial_state,
+    extensions::Blacklist::BlacklistState blacklist_state,
     const syncer::StringOrdinal& page_ordinal) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  extension_prefs_->OnExtensionInstalled(
-      extension,
-      initial_state,
-      page_ordinal);
-
+  extension_prefs_->OnExtensionInstalled(extension,
+                                         initial_state,
+                                         blacklist_state,
+                                         page_ordinal);
+  delayed_installs_.Remove(extension->id());
   FinishInstallation(extension);
 }
 
@@ -2521,20 +2530,6 @@
 
   AddExtension(extension);
 
-#if defined(ENABLE_THEMES)
-  // We do this here since AddExtension() is always called on browser startup,
-  // and we only really care about the last theme installed.
-  // If that ever changes and we have to move this code somewhere
-  // else, it should be somewhere that's not in the startup path.
-  if (extension->is_theme() && extensions_.GetByID(extension->id())) {
-    DCHECK_EQ(extensions_.GetByID(extension->id()), extension);
-    // Now that the theme extension is visible from outside the
-    // ExtensionService, notify the ThemeService about the
-    // newly-installed theme.
-    ThemeServiceFactory::GetForProfile(profile_)->SetTheme(extension);
-  }
-#endif
-
   // If this is a new external extension that was disabled, alert the user
   // so he can reenable it. We do this last so that it has already been
   // added to our list of extensions.
@@ -2566,7 +2561,14 @@
 
 void ExtensionService::UntrackTerminatedExtension(const std::string& id) {
   std::string lowercase_id = StringToLowerASCII(id);
+  const Extension* extension = terminated_extensions_.GetByID(lowercase_id);
   terminated_extensions_.Remove(lowercase_id);
+  if (extension) {
+    content::NotificationService::current()->Notify(
+        chrome::NOTIFICATION_EXTENSION_REMOVED,
+        content::Source<Profile>(profile_),
+        content::Details<const Extension>(extension));
+  }
 }
 
 const Extension* ExtensionService::GetTerminatedExtension(
@@ -3050,7 +3052,8 @@
        it != no_longer_blacklisted.end(); ++it) {
     scoped_refptr<const Extension> extension =
         blacklisted_extensions_.GetByID(*it);
-    DCHECK(extension.get());
+    DCHECK(extension.get()) << "Extension " << *it << " no longer blacklisted, "
+                            << "but it was never blacklisted.";
     if (!extension.get())
       continue;
     blacklisted_extensions_.Remove(*it);
@@ -3063,7 +3066,8 @@
   for (std::set<std::string>::iterator it = not_yet_blacklisted.begin();
        it != not_yet_blacklisted.end(); ++it) {
     scoped_refptr<const Extension> extension = GetInstalledExtension(*it);
-    DCHECK(extension.get());
+    DCHECK(extension.get()) << "Extension " << *it << " needs to be "
+                            << "blacklisted, but it's not installed.";
     if (!extension.get())
       continue;
     blacklisted_extensions_.Insert(extension);
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 4ff3178..1564587 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -200,6 +200,7 @@
   virtual const ExtensionSet* disabled_extensions() const OVERRIDE;
   const ExtensionSet* terminated_extensions() const;
   const ExtensionSet* blacklisted_extensions() const;
+  const ExtensionSet* delayed_installs() const;
 
   // Returns a set of all installed, disabled, blacklisted, and terminated
   // extensions.
@@ -431,12 +432,16 @@
 
   // Informs the service that an extension's files are in place for loading.
   //
-  // Please make sure the Blacklist is checked some time before calling this
-  // method.
+  // |page_ordinal| is the location of the extension in the app launcher.
+  // |has_requirement_errors| is true if requirements of the extension weren't
+  // met (for example graphics capabilities).
+  // |blacklist_state| will be BLACKLISTED if the extension is blacklisted.
+  // |wait_for_idle| may be false to install the extension immediately.
   void OnExtensionInstalled(
       const extensions::Extension* extension,
       const syncer::StringOrdinal& page_ordinal,
       bool has_requirement_errors,
+      extensions::Blacklist::BlacklistState blacklist_state,
       bool wait_for_idle);
 
   // Checks for delayed installation for all pending installs.
@@ -736,9 +741,11 @@
   // the extension is installed, e.g., to update event handlers on background
   // pages; and perform other extension install tasks before calling
   // AddExtension.
-  void AddNewOrUpdatedExtension(const extensions::Extension* extension,
-                                extensions::Extension::State initial_state,
-                                const syncer::StringOrdinal& page_ordinal);
+  void AddNewOrUpdatedExtension(
+      const extensions::Extension* extension,
+      extensions::Extension::State initial_state,
+      extensions::Blacklist::BlacklistState blacklist_state,
+      const syncer::StringOrdinal& page_ordinal);
 
   // Handles sending notification that |extension| was loaded.
   void NotifyExtensionLoaded(const extensions::Extension* extension);
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index e8f452b..18a5652 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -37,6 +37,7 @@
 #include "chrome/browser/extensions/extension_creator.h"
 #include "chrome/browser/extensions/extension_error_reporter.h"
 #include "chrome/browser/extensions/extension_error_ui.h"
+#include "chrome/browser/extensions/extension_notification_observer.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_sorting.h"
 #include "chrome/browser/extensions/extension_special_storage_policy.h"
@@ -69,12 +70,14 @@
 #include "chrome/common/extensions/api/plugins/plugins_handler.h"
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_builder.h"
 #include "chrome/common/extensions/extension_l10n_util.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
 #include "chrome/common/extensions/manifest_url_handler.h"
 #include "chrome/common/extensions/permissions/permission_set.h"
+#include "chrome/common/extensions/value_builder.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/testing_profile.h"
@@ -834,7 +837,9 @@
         EXPECT_EQ(0u, loaded_.size()) << path.value();
       } else {
         EXPECT_EQ(1u, loaded_.size()) << path.value();
-        EXPECT_EQ(expected_extensions_count_, service_->extensions()->size()) <<
+        size_t actual_extension_count = service_->extensions()->size() +
+            service_->disabled_extensions()->size();
+        EXPECT_EQ(expected_extensions_count_, actual_extension_count) <<
             path.value();
         extension = loaded_[0].get();
         EXPECT_TRUE(service_->GetExtensionById(extension->id(), false))
@@ -2202,6 +2207,14 @@
   creator.reset(new ExtensionCreator());
   ASSERT_FALSE(creator->Run(temp_dir2.path(), crx_path, privkey_path,
                             base::FilePath(), ExtensionCreator::kOverwriteCRX));
+
+  // Try packing with a private key that is a valid key, but invalid for the
+  // extension.
+  base::FilePath bad_private_key_dir = data_dir_.AppendASCII("bad_private_key");
+  crx_path = output_directory.AppendASCII("bad_private_key.crx");
+  privkey_path = data_dir_.AppendASCII("bad_private_key.pem");
+  ASSERT_FALSE(creator->Run(bad_private_key_dir, crx_path, base::FilePath(),
+      privkey_path, ExtensionCreator::kOverwriteCRX));
 }
 
 // Test Packaging and installing an extension whose name contains punctuation.
@@ -2346,6 +2359,7 @@
 
 TEST_F(ExtensionServiceTest, InstallTheme) {
   InitializeEmptyExtensionService();
+  service_->Init();
 
   // A theme.
   base::FilePath path = data_dir_.AppendASCII("theme.crx");
@@ -2387,6 +2401,8 @@
 TEST_F(ExtensionServiceTest, LoadLocalizedTheme) {
   // Load.
   InitializeEmptyExtensionService();
+  service_->Init();
+
   base::FilePath extension_path = data_dir_
       .AppendASCII("theme_i18n");
 
@@ -2487,6 +2503,8 @@
 
 TEST_F(ExtensionServiceTest, InstallLocalizedTheme) {
   InitializeEmptyExtensionService();
+  service_->Init();
+
   base::FilePath theme_path = data_dir_
       .AppendASCII("theme_i18n");
 
@@ -3323,20 +3341,22 @@
 // Unload installed extension from blacklist.
 TEST_F(ExtensionServiceTest, BlacklistedExtensionWillNotInstall) {
   InitializeEmptyExtensionService();
-  std::vector<std::string> blacklist;
-  blacklist.push_back(good_crx);
-  ExtensionSystem::Get(profile_.get())->blacklist()->SetFromUpdater(blacklist,
-                                                                    "v1");
 
-  // Make sure pref is updated
-  loop_.RunUntilIdle();
+  // Fake the blacklisting of good_crx by pretending that we get an update
+  // which includes it.
+  extensions::Blacklist* blacklist =
+      ExtensionSystem::Get(profile_.get())->blacklist();
+  blacklist->SetFromUpdater(std::vector<std::string>(1, good_crx), "v1");
 
-  // Now, the good_crx is blacklisted.
+  // Now good_crx is blacklisted.
   ValidateBooleanPref(good_crx, "blacklist", true);
 
-  // We can not install good_crx.
+  // We cannot install good_crx.
   base::FilePath path = data_dir_.AppendASCII("good.crx");
-  InstallCRX(path, INSTALL_FAILED);
+  // HACK: specify WAS_INSTALLED_BY_DEFAULT so that test machinery doesn't
+  // decide to install this silently. Somebody should fix these tests, all
+  // 6,000 lines of them. Hah!
+  InstallCRX(path, INSTALL_FAILED, Extension::WAS_INSTALLED_BY_DEFAULT);
   EXPECT_EQ(0u, service_->extensions()->size());
   ValidateBooleanPref(good_crx, "blacklist", true);
 }
@@ -6339,3 +6359,42 @@
   EXPECT_FALSE(extensions::HasExternalInstallBubble(service_));
   EXPECT_FALSE(service_->IsExtensionEnabled(updates_from_webstore));
 }
+
+TEST_F(ExtensionServiceTest, InstallBlacklistedExtension) {
+  InitializeEmptyExtensionService();
+
+  scoped_refptr<Extension> extension = extensions::ExtensionBuilder()
+      .SetManifest(extensions::DictionaryBuilder()
+          .Set("name", "extension")
+          .Set("version", "1.0")
+          .Set("manifest_version", 2).Build())
+      .Build();
+  ASSERT_TRUE(extension.get());
+  const std::string& id = extension->id();
+
+  std::set<std::string> id_set;
+  id_set.insert(id);
+  extensions::ExtensionNotificationObserver notifications(
+      content::NotificationService::AllSources(), id_set);
+
+  // Installation should be allowed but the extension should never have been
+  // loaded and it should be blacklisted in prefs.
+  service_->OnExtensionInstalled(
+      extension.get(),
+      syncer::StringOrdinal(),
+      false /* has requirement errors */,
+      extensions::Blacklist::BLACKLISTED,
+      false /* wait for idle */);
+  loop_.RunUntilIdle();
+
+  // Extension was installed but not loaded.
+  EXPECT_TRUE(notifications.CheckNotifications(
+      chrome::NOTIFICATION_EXTENSION_INSTALLED));
+
+  EXPECT_TRUE(service_->GetInstalledExtension(id));
+  EXPECT_FALSE(service_->extensions()->Contains(id));
+  EXPECT_TRUE(service_->blacklisted_extensions()->Contains(id));
+  EXPECT_TRUE(service_->extension_prefs()->IsExtensionBlacklisted(id));
+  EXPECT_TRUE(
+      service_->extension_prefs()->IsBlacklistedExtensionAcknowledged(id));
+}
diff --git a/chrome/browser/extensions/extension_sorting_unittest.cc b/chrome/browser/extensions/extension_sorting_unittest.cc
index ff219b3..b84535e 100644
--- a/chrome/browser/extensions/extension_sorting_unittest.cc
+++ b/chrome/browser/extensions/extension_sorting_unittest.cc
@@ -11,6 +11,7 @@
 #include "sync/api/string_ordinal.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using extensions::Blacklist;
 using extensions::Extension;
 using extensions::Manifest;
 
@@ -28,7 +29,9 @@
   virtual void Initialize() OVERRIDE {
     extension_ = prefs_.AddExtension("not_an_app");
     // Non-apps should not have any app launch ordinal or page ordinal.
-    prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
+    prefs()->OnExtensionInstalled(extension_.get(),
+                                  Extension::ENABLED,
+                                  Blacklist::NOT_BLACKLISTED,
                                   syncer::StringOrdinal());
   }
 
@@ -54,7 +57,9 @@
 
     extension_ = prefs_.AddApp("on_extension_installed");
     EXPECT_FALSE(prefs()->IsExtensionDisabled(extension_->id()));
-    prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
+    prefs()->OnExtensionInstalled(extension_.get(),
+                                  Extension::ENABLED,
+                                  Blacklist::NOT_BLACKLISTED,
                                   syncer::StringOrdinal());
   }
 
@@ -104,7 +109,9 @@
     extension_ = prefs_.AddApp("page_ordinal");
     // Install with a page preference.
     first_page_ = syncer::StringOrdinal::CreateInitialOrdinal();
-    prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
+    prefs()->OnExtensionInstalled(extension_.get(),
+                                  Extension::ENABLED,
+                                  Blacklist::NOT_BLACKLISTED,
                                   first_page_);
     EXPECT_TRUE(first_page_.Equals(
         extension_sorting()->GetPageOrdinal(extension_->id())));
@@ -112,7 +119,9 @@
 
     scoped_refptr<Extension> extension2 = prefs_.AddApp("page_ordinal_2");
     // Install without any page preference.
-    prefs()->OnExtensionInstalled(extension2.get(), Extension::ENABLED,
+    prefs()->OnExtensionInstalled(extension2.get(),
+                                  Extension::ENABLED,
+                                  Blacklist::NOT_BLACKLISTED,
                                   syncer::StringOrdinal());
     EXPECT_TRUE(first_page_.Equals(
         extension_sorting()->GetPageOrdinal(extension2->id())));
@@ -638,6 +647,7 @@
         simple_dict, Extension::NO_FLAGS, &error);
     prefs()->OnExtensionInstalled(app1_scoped_.get(),
                                   Extension::ENABLED,
+                                  Blacklist::NOT_BLACKLISTED,
                                   syncer::StringOrdinal());
 
     app2_scoped_ = Extension::Create(
@@ -645,6 +655,7 @@
         simple_dict, Extension::NO_FLAGS, &error);
     prefs()->OnExtensionInstalled(app2_scoped_.get(),
                                   Extension::ENABLED,
+                                  Blacklist::NOT_BLACKLISTED,
                                   syncer::StringOrdinal());
 
     app1_ = app1_scoped_.get();
@@ -828,7 +839,9 @@
   virtual void SetupUserOrdinals() {}
 
   virtual void InstallApps() {
-    prefs()->OnExtensionInstalled(app_.get(), Extension::ENABLED,
+    prefs()->OnExtensionInstalled(app_.get(),
+                                  Extension::ENABLED,
+                                  Blacklist::NOT_BLACKLISTED,
                                   syncer::StringOrdinal());
   }
 
@@ -874,7 +887,9 @@
  protected:
   virtual void InstallApps() OVERRIDE {
     install_page_ = default_page_ordinal_.CreateAfter();
-    prefs()->OnExtensionInstalled(app_.get(), Extension::ENABLED,
+    prefs()->OnExtensionInstalled(app_.get(),
+                                  Extension::ENABLED,
+                                  Blacklist::NOT_BLACKLISTED,
                                   install_page_);
   }
 
diff --git a/chrome/browser/extensions/extension_toolbar_model_browsertest.cc b/chrome/browser/extensions/extension_toolbar_model_browsertest.cc
index a340476..a1d4037 100644
--- a/chrome/browser/extensions/extension_toolbar_model_browsertest.cc
+++ b/chrome/browser/extensions/extension_toolbar_model_browsertest.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/extensions/extension_toolbar_model.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 
 using extensions::Extension;
@@ -72,9 +71,6 @@
 };
 
 IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, Basic) {
-  CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
-
   // Load an extension with no browser action.
   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                           .AppendASCII("browser_action")
@@ -112,9 +108,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderAndReinsert) {
-  CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
-
   // Load an extension with a browser action.
   base::FilePath extension_a_path(test_data_dir_.AppendASCII("api_test")
                                           .AppendASCII("browser_action")
diff --git a/chrome/browser/extensions/extension_ui_unittest.cc b/chrome/browser/extensions/extension_ui_unittest.cc
index 8581038..5b2ebe1 100644
--- a/chrome/browser/extensions/extension_ui_unittest.cc
+++ b/chrome/browser/extensions/extension_ui_unittest.cc
@@ -24,8 +24,7 @@
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #endif
 
-using extensions::Extension;
-using extensions::Manifest;
+namespace extensions {
 
 class ExtensionUITest : public testing::Test {
  public:
@@ -38,9 +37,8 @@
     // Create an ExtensionService and ManagementPolicy to inject into the
     // ExtensionSettingsHandler.
     profile_.reset(new TestingProfile());
-    extensions::TestExtensionSystem* system =
-        static_cast<extensions::TestExtensionSystem*>(
-            extensions::ExtensionSystem::Get(profile_.get()));
+    TestExtensionSystem* system =
+        static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile_.get()));
     extension_service_ = system->CreateExtensionService(
         CommandLine::ForCurrentProcess(), base::FilePath(), false);
     management_policy_ = system->management_policy();
@@ -72,8 +70,7 @@
       Manifest::Location location) {
     std::string error;
 
-    base::FilePath manifest_path = extension_path.Append(
-        extensions::kManifestFilename);
+    base::FilePath manifest_path = extension_path.Append(kManifestFilename);
     scoped_ptr<DictionaryValue> extension_data(DeserializeJSONTestData(
         manifest_path, &error));
     EXPECT_EQ("", error);
@@ -123,7 +120,7 @@
   content::TestBrowserThread file_thread_;
   scoped_ptr<TestingProfile> profile_;
   ExtensionService* extension_service_;
-  extensions::ManagementPolicy* management_policy_;
+  ManagementPolicy* management_policy_;
   scoped_ptr<ExtensionSettingsHandler> handler_;
 
 #if defined OS_CHROMEOS
@@ -279,3 +276,5 @@
   EXPECT_TRUE(extension_details->GetString("path", &ui_path));
   EXPECT_EQ(extension_path, base::FilePath(ui_path));
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_web_ui_override_registrar.cc b/chrome/browser/extensions/extension_web_ui_override_registrar.cc
index 8ce1f8b..b002670 100644
--- a/chrome/browser/extensions/extension_web_ui_override_registrar.cc
+++ b/chrome/browser/extensions/extension_web_ui_override_registrar.cc
@@ -35,8 +35,6 @@
         profile_, URLOverrides::GetChromeURLOverrides(extension));
 
   } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
-    if (content::Details<UnloadedExtensionInfo>(details)->already_disabled)
-      return;
     const Extension* extension =
         content::Details<UnloadedExtensionInfo>(details)->extension;
     ExtensionWebUI::UnregisterChromeURLOverrides(
diff --git a/chrome/browser/extensions/external_install_ui.cc b/chrome/browser/extensions/external_install_ui.cc
index 0888c97..d277194 100644
--- a/chrome/browser/extensions/external_install_ui.cc
+++ b/chrome/browser/extensions/external_install_ui.cc
@@ -244,7 +244,7 @@
       extension_(extension) {
   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
                  content::Source<Profile>(service->profile()));
-  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_REMOVED,
                  content::Source<Profile>(service->profile()));
 }
 
@@ -315,23 +315,17 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  const Extension* extension = NULL;
-  // The error is invalidated if the extension has been reloaded or unloaded.
-  if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
-    extension = content::Details<const Extension>(details).ptr();
-  } else {
-    DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_UNLOADED, type);
-    extensions::UnloadedExtensionInfo* info =
-        content::Details<extensions::UnloadedExtensionInfo>(details).ptr();
-    extension = info->extension;
-  }
-  if (extension == extension_) {
-    GlobalErrorService* error_service =
-        GlobalErrorServiceFactory::GetForProfile(service_->profile());
-    error_service->RemoveGlobalError(this);
-    service_->AcknowledgeExternalExtension(extension_->id());
-    delete this;
-  }
+  // The error is invalidated if the extension has been loaded or removed.
+  DCHECK(type == chrome::NOTIFICATION_EXTENSION_LOADED ||
+         type == chrome::NOTIFICATION_EXTENSION_REMOVED);
+  const Extension* extension = content::Details<const Extension>(details).ptr();
+  if (extension != extension_)
+    return;
+  GlobalErrorService* error_service =
+      GlobalErrorServiceFactory::GetForProfile(service_->profile());
+  error_service->RemoveGlobalError(this);
+  service_->AcknowledgeExternalExtension(extension_->id());
+  delete this;
 }
 
 // ExternalInstallGlobalError -----------------------------------------------
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index 677f447..6ca0341 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -385,15 +385,10 @@
       Extension::WAS_INSTALLED_BY_DEFAULT;
 #endif
 
-  bool is_managed_profile = false;
+  bool is_managed_profile = profile->IsManaged();
   int external_apps_path_id = chrome::DIR_EXTERNAL_EXTENSIONS;
-#if defined(ENABLE_MANAGED_USERS)
-  ManagedUserService* managed_user_service =
-      ManagedUserServiceFactory::GetForProfile(profile);
-  is_managed_profile = managed_user_service->ProfileIsManaged();
   if (is_managed_profile)
     external_apps_path_id = chrome::DIR_MANAGED_USERS_DEFAULT_APPS;
-#endif
 
 #if defined(OS_CHROMEOS)
   typedef chromeos::ExternalPrefCacheLoader PrefLoader;
diff --git a/chrome/browser/extensions/isolated_app_browsertest.cc b/chrome/browser/extensions/isolated_app_browsertest.cc
index 0dcdfa7..dd7a9cd 100644
--- a/chrome/browser/extensions/isolated_app_browsertest.cc
+++ b/chrome/browser/extensions/isolated_app_browsertest.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/render_process_host.h"
@@ -20,6 +19,7 @@
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
+#include "extensions/common/switches.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -129,7 +129,8 @@
  private:
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     ExtensionBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
+    command_line->AppendSwitch(
+        extensions::switches::kEnableExperimentalExtensionApis);
   }
 };
 
diff --git a/chrome/browser/extensions/lazy_background_page_apitest.cc b/chrome/browser/extensions/lazy_background_page_apitest.cc
index 47c4672..e428704 100644
--- a/chrome/browser/extensions/lazy_background_page_apitest.cc
+++ b/chrome/browser/extensions/lazy_background_page_apitest.cc
@@ -78,7 +78,6 @@
  public:
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     ExtensionApiTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
     // Set shorter delays to prevent test timeouts.
     command_line->AppendSwitchASCII(switches::kEventPageIdleTime, "1");
     command_line->AppendSwitchASCII(switches::kEventPageSuspendingTime, "1");
diff --git a/chrome/browser/extensions/notifications_apitest.cc b/chrome/browser/extensions/notifications_apitest.cc
index 65cdc1d..0fd5812 100644
--- a/chrome/browser/extensions/notifications_apitest.cc
+++ b/chrome/browser/extensions/notifications_apitest.cc
@@ -35,11 +35,6 @@
   }
 };
 
-// TODO(kbr): remove: http://crbug.com/222296
-#if defined(OS_MACOSX)
-#import "base/mac/mac_util.h"
-#endif
-
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, NotificationsNoPermission) {
   ASSERT_TRUE(RunExtensionTest("notifications/has_not_permission")) << message_;
 }
@@ -57,12 +52,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, NotificationsHasPermission) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   DesktopNotificationServiceFactory::GetForProfile(browser()->profile())
       ->GrantPermission(GURL(
           "chrome-extension://peoadpeiejnhkmpaakpnompolbglelel"));
diff --git a/chrome/browser/extensions/pack_extension_unittest.cc b/chrome/browser/extensions/pack_extension_unittest.cc
index dac4608..78bcad3 100644
--- a/chrome/browser/extensions/pack_extension_unittest.cc
+++ b/chrome/browser/extensions/pack_extension_unittest.cc
@@ -17,7 +17,7 @@
 namespace extensions {
 
 // Tests the environment for packing extensions from the command line
-// via the --pack-extension switch.
+// when using the --pack-extension switch.
 class PackExtensionTest : public testing::Test {
  public:
   PackExtensionTest()
diff --git a/chrome/browser/extensions/platform_app_browsertest.cc b/chrome/browser/extensions/platform_app_browsertest.cc
index d193507..55cf56d 100644
--- a/chrome/browser/extensions/platform_app_browsertest.cc
+++ b/chrome/browser/extensions/platform_app_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "apps/shell_window.h"
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/prefs/pref_service.h"
@@ -34,6 +35,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/user_prefs/pref_registry_syncable.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
@@ -45,10 +47,6 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using apps::ShellWindow;
 using content::WebContents;
 using web_modal::WebContentsModalDialogManager;
@@ -797,8 +795,8 @@
 #endif
 IN_PROC_BROWSER_TEST_F(PlatformAppDevToolsBrowserTest, MAYBE_ReOpenedWithID) {
 #if defined(OS_WIN) && defined(USE_ASH)
-  // Disable this test in Metro+Ash for now (http://crbug.com/179830).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
   RunTestWithDevTools("minimal_id", RELAUNCH | HAS_ID);
diff --git a/chrome/browser/extensions/platform_app_browsertest_util.cc b/chrome/browser/extensions/platform_app_browsertest_util.cc
index a2634c7..e7151e3 100644
--- a/chrome/browser/extensions/platform_app_browsertest_util.cc
+++ b/chrome/browser/extensions/platform_app_browsertest_util.cc
@@ -17,6 +17,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/common/switches.h"
 
 using apps::ShellWindow;
 using content::WebContents;
@@ -34,8 +35,8 @@
   ExtensionBrowserTest::SetUpCommandLine(command_line);
 
   // Make event pages get suspended quicker.
-  command_line->AppendSwitchASCII(switches::kEventPageIdleTime, "1");
-  command_line->AppendSwitchASCII(switches::kEventPageSuspendingTime, "1");
+  command_line->AppendSwitchASCII(::switches::kEventPageIdleTime, "1");
+  command_line->AppendSwitchASCII(::switches::kEventPageSuspendingTime, "1");
 }
 
 const Extension* PlatformAppBrowserTest::LoadAndLaunchPlatformApp(
diff --git a/chrome/browser/extensions/plugin_apitest.cc b/chrome/browser/extensions/plugin_apitest.cc
index 16d5ed9..c74a58e 100644
--- a/chrome/browser/extensions/plugin_apitest.cc
+++ b/chrome/browser/extensions/plugin_apitest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/prefs/pref_service.h"
 #include "chrome/browser/content_settings/host_content_settings_map.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
@@ -13,6 +14,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/plugin_service.h"
@@ -20,10 +22,6 @@
 #include "content/public/test/browser_test_utils.h"
 #include "net/base/net_util.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::NavigationController;
 using content::WebContents;
 using extensions::Extension;
@@ -130,7 +128,7 @@
 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, MAYBE_PluginPrivate) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/plugin_manager.cc b/chrome/browser/extensions/plugin_manager.cc
index 47df4cf..ff77620 100644
--- a/chrome/browser/extensions/plugin_manager.cc
+++ b/chrome/browser/extensions/plugin_manager.cc
@@ -87,8 +87,6 @@
       PluginService::GetInstance()->PurgePluginListCache(profile_, false);
 
   } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
-    if (content::Details<UnloadedExtensionInfo>(details)->already_disabled)
-      return;
     const Extension* extension =
         content::Details<UnloadedExtensionInfo>(details)->extension;
 
diff --git a/chrome/browser/extensions/process_management_browsertest.cc b/chrome/browser/extensions/process_management_browsertest.cc
index bb54aeb..e607d26 100644
--- a/chrome/browser/extensions/process_management_browsertest.cc
+++ b/chrome/browser/extensions/process_management_browsertest.cc
@@ -12,13 +12,13 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/common/switches.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
@@ -32,7 +32,8 @@
   // This is needed for testing isolated apps, which are still experimental.
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     ExtensionBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
+    command_line->AppendSwitch(
+        extensions::switches::kEnableExperimentalExtensionApis);
   }
 };
 
diff --git a/chrome/browser/extensions/shell_window_registry.cc b/chrome/browser/extensions/shell_window_registry.cc
index 38a8d38..b9c939d 100644
--- a/chrome/browser/extensions/shell_window_registry.cc
+++ b/chrome/browser/extensions/shell_window_registry.cc
@@ -109,6 +109,16 @@
   return app_windows;
 }
 
+void ShellWindowRegistry::CloseAllShellWindowsForApp(
+    const std::string& app_id) {
+  for (ShellWindowList::const_iterator i = shell_windows_.begin();
+       i != shell_windows_.end(); ) {
+    ShellWindow* shell_window = *(i++);
+    if (shell_window->extension_id() == app_id)
+      shell_window->GetBaseWindow()->Close();
+  }
+}
+
 ShellWindow* ShellWindowRegistry::GetShellWindowForRenderViewHost(
     content::RenderViewHost* render_view_host) const {
   for (ShellWindowList::const_iterator i = shell_windows_.begin();
diff --git a/chrome/browser/extensions/shell_window_registry.h b/chrome/browser/extensions/shell_window_registry.h
index da1072c..fb5f48e 100644
--- a/chrome/browser/extensions/shell_window_registry.h
+++ b/chrome/browser/extensions/shell_window_registry.h
@@ -74,6 +74,9 @@
   ShellWindowList GetShellWindowsForApp(const std::string& app_id) const;
   const ShellWindowList& shell_windows() const { return shell_windows_; }
 
+  // Close all shell windows associated with an app.
+  void CloseAllShellWindowsForApp(const std::string& app_id);
+
   // Helper functions to find shell windows with particular attributes.
   apps::ShellWindow* GetShellWindowForRenderViewHost(
       content::RenderViewHost* render_view_host) const;
diff --git a/chrome/browser/extensions/test_extension_prefs.cc b/chrome/browser/extensions/test_extension_prefs.cc
index e2d8a70..018ff42 100644
--- a/chrome/browser/extensions/test_extension_prefs.cc
+++ b/chrome/browser/extensions/test_extension_prefs.cc
@@ -161,6 +161,7 @@
   EXPECT_TRUE(Extension::IdIsValid(extension->id()));
   prefs_->OnExtensionInstalled(extension.get(),
                                Extension::ENABLED,
+                               Blacklist::NOT_BLACKLISTED,
                                syncer::StringOrdinal::CreateInitialOrdinal());
   return extension;
 }
diff --git a/chrome/browser/extensions/unpacked_installer.cc b/chrome/browser/extensions/unpacked_installer.cc
index b394f00..adfd3e5 100644
--- a/chrome/browser/extensions/unpacked_installer.cc
+++ b/chrome/browser/extensions/unpacked_installer.cc
@@ -274,10 +274,12 @@
   PermissionsUpdater perms_updater(service_weak_->profile());
   perms_updater.GrantActivePermissions(installer_.extension().get());
 
-  service_weak_->OnExtensionInstalled(installer_.extension().get(),
-                                      syncer::StringOrdinal(),
-                                      false /* no requirement errors */,
-                                      false /* don't wait for idle */);
+  service_weak_->OnExtensionInstalled(
+      installer_.extension().get(),
+      syncer::StringOrdinal(),
+      false /* no requirement errors */,
+      Blacklist::NOT_BLACKLISTED,
+      false /* don't wait for idle */);
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/web_view_browsertest.cc b/chrome/browser/extensions/web_view_browsertest.cc
index 08b2f7d..8c2166e 100644
--- a/chrome/browser/extensions/web_view_browsertest.cc
+++ b/chrome/browser/extensions/web_view_browsertest.cc
@@ -26,6 +26,11 @@
 #include "ui/compositor/compositor_setup.h"
 #include "ui/gl/gl_switches.h"
 
+// For fine-grained suppression on flaky tests.
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
 using prerender::PrerenderLinkManager;
 using prerender::PrerenderLinkManagerFactory;
 
@@ -520,6 +525,12 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestPartitionRaisesException) {
+#if defined(OS_WIN)
+  // Flaky on XP bot http://crbug.com/267304
+  if (base::win::GetVersion() <= base::win::VERSION_XP)
+    return;
+#endif
+
   TestHelper("testPartitionRaisesException",
              "DoneShimTest.PASSED",
              "DoneShimTest.FAILED",
@@ -527,6 +538,12 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestExecuteScriptFail) {
+#if defined(OS_WIN)
+  // Flaky on XP bot http://crbug.com/266185
+  if (base::win::GetVersion() <= base::win::VERSION_XP)
+    return;
+#endif
+
   TestHelper("testExecuteScriptFail",
              "DoneShimTest.PASSED",
              "DoneShimTest.FAILED",
@@ -562,6 +579,12 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestBrowserPluginNotAllowed) {
+#if defined(OS_WIN)
+  // Flaky on XP bots. http://crbug.com/267300
+  if (base::win::GetVersion() <= base::win::VERSION_XP)
+    return;
+#endif
+
   TestHelper("testBrowserPluginNotAllowed",
              "DoneShimTest.PASSED",
              "DoneShimTest.FAILED",
@@ -941,7 +964,7 @@
 }
 
 #if defined(OS_WIN)
-// This test is very flaky on Win Aura and XP. http://crbug.com/248873
+// This test is very flaky on Win Aura, Win XP, Win 7. http://crbug.com/248873
 #define MAYBE_DOMStorageIsolation DISABLED_DOMStorageIsolation
 #else
 #define MAYBE_DOMStorageIsolation DOMStorageIsolation
diff --git a/chrome/browser/extensions/webstore_inline_installer.cc b/chrome/browser/extensions/webstore_inline_installer.cc
index abc5341..a1c622b 100644
--- a/chrome/browser/extensions/webstore_inline_installer.cc
+++ b/chrome/browser/extensions/webstore_inline_installer.cc
@@ -57,9 +57,14 @@
   scoped_ptr<ExtensionInstallPrompt::Prompt> prompt(
       new ExtensionInstallPrompt::Prompt(
           ExtensionInstallPrompt::INLINE_INSTALL_PROMPT));
+
+  // crbug.com/260742: Don't display the user count if it's zero. The reason
+  // it's zero is very often that the number isn't actually being counted
+  // (intentionally), which means that it's unlikely to be correct.
   prompt->SetInlineInstallWebstoreData(localized_user_count(),
-                                      average_rating(),
-                                      rating_count());
+                                       show_user_count(),
+                                       average_rating(),
+                                       rating_count());
   return prompt.Pass();
 }
 
diff --git a/chrome/browser/extensions/webstore_inline_installer.h b/chrome/browser/extensions/webstore_inline_installer.h
index 380e65d..59fcb40 100644
--- a/chrome/browser/extensions/webstore_inline_installer.h
+++ b/chrome/browser/extensions/webstore_inline_installer.h
@@ -32,9 +32,9 @@
   typedef WebstoreStandaloneInstaller::Callback Callback;
 
   WebstoreInlineInstaller(content::WebContents* web_contents,
-                         const std::string& webstore_item_id,
-                         const GURL& requestor_url,
-                         const Callback& callback);
+                          const std::string& webstore_item_id,
+                          const GURL& requestor_url,
+                          const Callback& callback);
 
  protected:
   friend class base::RefCountedThreadSafe<WebstoreInlineInstaller>;
diff --git a/chrome/browser/extensions/webstore_installer.cc b/chrome/browser/extensions/webstore_installer.cc
index 44b6450..dd23748 100644
--- a/chrome/browser/extensions/webstore_installer.cc
+++ b/chrome/browser/extensions/webstore_installer.cc
@@ -67,7 +67,8 @@
 const char kDownloadCanceledError[] = "Download canceled";
 const char kInstallCanceledError[] = "Install canceled";
 const char kDownloadInterruptedError[] = "Download interrupted";
-const char kInvalidDownloadError[] = "Download was not a CRX";
+const char kInvalidDownloadError[] =
+    "Download was not a valid extension or user script";
 const char kInlineInstallSource[] = "inline";
 const char kDefaultInstallSource[] = "ondemand";
 
diff --git a/chrome/browser/extensions/webstore_standalone_installer.cc b/chrome/browser/extensions/webstore_standalone_installer.cc
index db3eca0..17414a2 100644
--- a/chrome/browser/extensions/webstore_standalone_installer.cc
+++ b/chrome/browser/extensions/webstore_standalone_installer.cc
@@ -24,6 +24,7 @@
 const char kLocalizedNameKey[] = "localized_name";
 const char kLocalizedDescriptionKey[] = "localized_description";
 const char kUsersKey[] = "users";
+const char kShowUserCountKey[] = "show_user_count";
 const char kAverageRatingKey[] = "average_rating";
 const char kRatingCountKey[] = "rating_count";
 const char kRedirectUrlKey[] = "redirect_url";
@@ -43,6 +44,7 @@
     : id_(webstore_item_id),
       callback_(callback),
       profile_(profile),
+      show_user_count_(true),
       average_rating_(0.0),
       rating_count_(0) {
   CHECK(!callback_.is_null());
@@ -111,6 +113,10 @@
     return;
   }
 
+  // Optional.
+  show_user_count_ = true;
+  webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
+
   if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
       average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
     CompleteInstall(kInvalidWebstoreResponseError);
diff --git a/chrome/browser/extensions/webstore_standalone_installer.h b/chrome/browser/extensions/webstore_standalone_installer.h
index a49a868..1c62ab5 100644
--- a/chrome/browser/extensions/webstore_standalone_installer.h
+++ b/chrome/browser/extensions/webstore_standalone_installer.h
@@ -112,6 +112,7 @@
   virtual scoped_ptr<ExtensionInstallPrompt> CreateInstallUI();
 
   // Accessors to be used by subclasses.
+  bool show_user_count() const { return show_user_count_; }
   const std::string& localized_user_count() const {
     return localized_user_count_;
   }
@@ -182,6 +183,7 @@
   // Extracted from the webstore JSON data response.
   std::string localized_name_;
   std::string localized_description_;
+  bool show_user_count_;
   std::string localized_user_count_;
   double average_rating_;
   int rating_count_;
diff --git a/chrome/browser/extensions/webstore_startup_installer_browsertest.cc b/chrome/browser/extensions/webstore_startup_installer_browsertest.cc
index 1ff011b..a07c564 100644
--- a/chrome/browser/extensions/webstore_startup_installer_browsertest.cc
+++ b/chrome/browser/extensions/webstore_startup_installer_browsertest.cc
@@ -23,6 +23,7 @@
 #include "chrome/common/extensions/value_builder.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/notification_service.h"
@@ -33,10 +34,6 @@
 #include "net/dns/mock_host_resolver.h"
 #include "url/gurl.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::WebContents;
 using extensions::DictionaryBuilder;
 using extensions::Extension;
@@ -242,7 +239,7 @@
                        InstallProhibitedForManagedUsers) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/extensions/window_open_apitest.cc b/chrome/browser/extensions/window_open_apitest.cc
index 7ebb814..4991a97 100644
--- a/chrome/browser/extensions/window_open_apitest.cc
+++ b/chrome/browser/extensions/window_open_apitest.cc
@@ -17,11 +17,13 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/test/browser_test_utils.h"
+#include "extensions/common/switches.h"
 #include "net/dns/mock_host_resolver.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -29,10 +31,6 @@
 #include "chrome/browser/extensions/shell_window_registry.h"
 #endif
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 #if defined(USE_ASH) && !defined(OS_WIN)
 // TODO(stevenjb): Figure out the correct behavior for Ash + Win
 #define USE_ASH_PANELS
@@ -45,7 +43,7 @@
 // Disabled, http://crbug.com/64899.
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_WindowOpen) {
   CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
+      extensions::switches::kEnableExperimentalExtensionApis);
 
   ResultCatcher catcher;
   ASSERT_TRUE(LoadExtensionIncognito(test_data_dir_
@@ -260,7 +258,7 @@
                        CloseNonExtensionPanelsOnUninstall) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/feedback/feedback_data.cc b/chrome/browser/feedback/feedback_data.cc
index 4b4c71f..5609886 100644
--- a/chrome/browser/feedback/feedback_data.cc
+++ b/chrome/browser/feedback/feedback_data.cc
@@ -21,16 +21,15 @@
 
 using content::BrowserThread;
 
-#if defined(OS_CHROMEOS)
 namespace {
 
 const char kMultilineIndicatorString[] = "<multiline>\n";
 const char kMultilineStartString[] = "---------- START ----------\n";
 const char kMultilineEndString[] = "---------- END ----------\n\n";
 
-std::string LogsToString(chromeos::SystemLogsResponse* sys_info) {
+std::string LogsToString(feedback_util::SystemLogsMap* sys_info) {
   std::string syslogs_string;
-  for (chromeos::SystemLogsResponse::const_iterator it = sys_info->begin();
+  for (feedback_util::SystemLogsMap::const_iterator it = sys_info->begin();
       it != sys_info->end(); ++it) {
     std::string key = it->first;
     std::string value = it->second;
@@ -51,90 +50,53 @@
   return syslogs_string;
 }
 
-void ZipLogs(chromeos::SystemLogsResponse* sys_info,
+void ZipLogs(feedback_util::SystemLogsMap* sys_info,
              std::string* compressed_logs) {
   DCHECK(compressed_logs);
   std::string logs_string = LogsToString(sys_info);
-  if (!FeedbackUtil::ZipString(logs_string, compressed_logs)) {
+  if (!feedback_util::ZipString(logs_string, compressed_logs)) {
     compressed_logs->clear();
   }
 }
 
 }  // namespace
-#endif // OS_CHROMEOS
 
 FeedbackData::FeedbackData() : profile_(NULL),
-                               feedback_page_data_complete_(false) {
-#if defined(OS_CHROMEOS)
-  sys_info_.reset(NULL);
-  attached_filedata_.reset(NULL);
-  send_sys_info_ = true;
-  read_attached_file_complete_ = false;
-  syslogs_collection_complete_ = false;
-#endif
+                               feedback_page_data_complete_(false),
+                               syslogs_compression_complete_(false) {
 }
 
 FeedbackData::~FeedbackData() {
 }
 
-bool FeedbackData::DataCollectionComplete() {
-#if defined(OS_CHROMEOS)
-  return (syslogs_collection_complete_ || !send_sys_info_) &&
-      read_attached_file_complete_ &&
+bool FeedbackData::IsDataComplete() {
+  return (syslogs_compression_complete_ || !sys_info_.get()) &&
       feedback_page_data_complete_;
-#else
-  return feedback_page_data_complete_;
-#endif
 }
 void FeedbackData::SendReport() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (DataCollectionComplete())
-    FeedbackUtil::SendReport(this);
+  if (IsDataComplete())
+    feedback_util::SendReport(this);
 }
 
-void FeedbackData::FeedbackPageDataComplete() {
-#if defined(OS_CHROMEOS)
-  if (attached_filename_.size() &&
-      base::FilePath::IsSeparator(attached_filename_[0]) &&
-      !attached_filedata_.get()) {
-    // Read the attached file and then send this report. The allocated string
-    // will be freed in FeedbackUtil::SendReport.
-    attached_filedata_.reset(new std::string);
-
-    base::FilePath root =
-        ash::Shell::GetInstance()->delegate()->
-            GetCurrentBrowserContext()->GetPath();
-    base::FilePath filepath = root.Append(attached_filename_.substr(1));
-    attached_filename_ = filepath.BaseName().value();
-
-    // Read the file into file_data, then call send report again with the
-    // stripped filename and file data (which will skip this code path).
-    content::BrowserThread::PostTaskAndReply(
-        content::BrowserThread::FILE, FROM_HERE,
-        base::Bind(&FeedbackData::ReadAttachedFile, this, filepath),
-        base::Bind(&FeedbackData::ReadFileComplete, this));
-  } else {
-    read_attached_file_complete_ = true;
-  }
-#endif
+void FeedbackData::OnFeedbackPageDataComplete() {
   feedback_page_data_complete_ = true;
   SendReport();
 }
 
-#if defined(OS_CHROMEOS)
 void FeedbackData::set_sys_info(
-    scoped_ptr<chromeos::SystemLogsResponse> sys_info) {
+    scoped_ptr<feedback_util::SystemLogsMap> sys_info) {
   if (sys_info.get())
     CompressSyslogs(sys_info.Pass());
 }
 
 void FeedbackData::CompressSyslogs(
-    scoped_ptr<chromeos::SystemLogsResponse> sys_info) {
+    scoped_ptr<feedback_util::SystemLogsMap> sys_info) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   // We get the pointer first since base::Passed will nullify the scoper, hence
   // it's not safe to use <scoper>.get() as a parameter to PostTaskAndReply.
-  chromeos::SystemLogsResponse* sys_info_ptr = sys_info.get();
+  feedback_util::SystemLogsMap* sys_info_ptr = sys_info.get();
   std::string* compressed_logs_ptr = new std::string;
   scoped_ptr<std::string> compressed_logs(compressed_logs_ptr);
   BrowserThread::PostBlockingPoolTaskAndReply(
@@ -142,40 +104,20 @@
       base::Bind(&ZipLogs,
                  sys_info_ptr,
                  compressed_logs_ptr),
-      base::Bind(&FeedbackData::SyslogsComplete,
+      base::Bind(&FeedbackData::OnCompressLogsComplete,
                  this,
                  base::Passed(&sys_info),
                  base::Passed(&compressed_logs)));
 }
 
-void FeedbackData::SyslogsComplete(
-    scoped_ptr<chromeos::SystemLogsResponse> sys_info,
+void FeedbackData::OnCompressLogsComplete(
+    scoped_ptr<feedback_util::SystemLogsMap> sys_info,
     scoped_ptr<std::string> compressed_logs) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  if (send_sys_info_) {
-    sys_info_ = sys_info.Pass();
-    compressed_logs_ = compressed_logs.Pass();
-    syslogs_collection_complete_ = true;
-    SendReport();
-  }
-}
+  sys_info_ = sys_info.Pass();
+  compressed_logs_ = compressed_logs.Pass();
+  syslogs_compression_complete_ = true;
 
-void FeedbackData::ReadFileComplete() {
-  read_attached_file_complete_ = true;
   SendReport();
 }
-
-void FeedbackData::StartSyslogsCollection() {
-  chromeos::SystemLogsFetcher* fetcher = new chromeos::SystemLogsFetcher();
-  fetcher->Fetch(base::Bind(&FeedbackData::CompressSyslogs, this));
-}
-
-// private
-void FeedbackData::ReadAttachedFile(const base::FilePath& from) {
-  if (!file_util::ReadFileToString(from, attached_filedata_.get())) {
-    if (attached_filedata_.get())
-      attached_filedata_->clear();
-  }
-}
-#endif
diff --git a/chrome/browser/feedback/feedback_data.h b/chrome/browser/feedback/feedback_data.h
index 111721b..688b609 100644
--- a/chrome/browser/feedback/feedback_data.h
+++ b/chrome/browser/feedback/feedback_data.h
@@ -8,14 +8,13 @@
 #include <string>
 #include <vector>
 
-#include "base/memory/ref_counted.h"
-#include "chrome/browser/ui/webui/screenshot_source.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/feedback/feedback_util.h"
 #include "url/gurl.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/system_logs/system_logs_fetcher.h"
-#endif
-
+namespace base {
+class FilePath;
+}
 class Profile;
 
 class FeedbackData : public base::RefCountedThreadSafe<FeedbackData> {
@@ -23,29 +22,19 @@
   FeedbackData();
 
   // Called once we've update all the data from the feedback page.
-  void FeedbackPageDataComplete();
+  void OnFeedbackPageDataComplete();
 
-#if defined(OS_CHROMEOS)
   // Called once we have read our system logs.
-  void CompressSyslogs(scoped_ptr<chromeos::SystemLogsResponse> sys_info);
+  void CompressSyslogs(scoped_ptr<feedback_util::SystemLogsMap> sys_info);
 
   // Called once we have read and compressed our system logs.
-  void SyslogsComplete(scoped_ptr<chromeos::SystemLogsResponse> sys_info,
-                       scoped_ptr<std::string> compressed_logs);
+  void OnCompressLogsComplete(scoped_ptr<feedback_util::SystemLogsMap> sys_info,
+                              scoped_ptr<std::string> compressed_logs);
 
-  // Called once we have read our attached file.
-  void ReadFileComplete();
-
-  // Starts the collection of our system logs. SyslogsComplete is called once
-  // the collection is done.
-  void StartSyslogsCollection();
-#endif
-
-  // Returns true if we've complete collection of data from all our
-  // data sources. At this time this involves the system logs, the attached
-  // file (if needed to be read of the disk) and the rest of the data from
-  // the feedback page.
-  bool DataCollectionComplete();
+  // Returns true if we've completed all the tasks needed before we can send
+  // feedback - at this time this is includes getting the feedback page data
+  // and compressing the system logs.
+  bool IsDataComplete();
 
   // Sends the feedback report if we have all our data complete.
   void SendReport();
@@ -60,18 +49,13 @@
   const std::string& page_url() const { return page_url_; }
   const std::string& description() const { return description_; }
   const std::string& user_email() const { return user_email_; }
-  ScreenshotDataPtr image() const { return image_; }
+  std::string* image() const { return image_.get(); }
   const std::string attached_filename() const { return attached_filename_; }
   const GURL attached_file_url() const { return attached_file_url_; }
   std::string* attached_filedata() const { return attached_filedata_.get(); }
   const GURL screenshot_url() const { return screenshot_url_; }
-#if defined(OS_CHROMEOS)
-  chromeos::SystemLogsResponse* sys_info() const {
-    return send_sys_info_ ? sys_info_.get() : NULL;
-  }
-  const std::string timestamp() const { return timestamp_; }
+  feedback_util::SystemLogsMap* sys_info() const { return sys_info_.get(); }
   std::string* compressed_logs() const { return compressed_logs_.get(); }
-#endif
 
   // Setters
   void set_profile(Profile* profile) { profile_ = profile; }
@@ -85,7 +69,7 @@
   void set_user_email(const std::string& user_email) {
     user_email_ = user_email;
   }
-  void set_image(ScreenshotDataPtr image) { image_ = image; }
+  void set_image(scoped_ptr<std::string> image) { image_ = image.Pass(); }
   void set_attached_filename(const std::string& attached_filename) {
     attached_filename_ = attached_filename;
   }
@@ -94,55 +78,31 @@
   }
   void set_attached_file_url(const GURL& url) { attached_file_url_ = url; }
   void set_screenshot_url(const GURL& url) { screenshot_url_ = url; }
-#if defined(OS_CHROMEOS)
-  void set_sys_info(scoped_ptr<chromeos::SystemLogsResponse> sys_info);
-  void set_send_sys_info(bool send_sys_info) { send_sys_info_ = send_sys_info; }
-  void set_timestamp(const std::string& timestamp) {
-    timestamp_ = timestamp;
-  }
-#endif
+  void set_sys_info(scoped_ptr<feedback_util::SystemLogsMap> sys_info);
 
  private:
   friend class base::RefCountedThreadSafe<FeedbackData>;
 
   virtual ~FeedbackData();
 
-#if defined(OS_CHROMEOS)
-  void ReadAttachedFile(const base::FilePath& from);
-#endif
-
   Profile* profile_;
 
   std::string category_tag_;
   std::string page_url_;
   std::string description_;
   std::string user_email_;
-  ScreenshotDataPtr image_;
+  scoped_ptr<std::string> image_;
   std::string attached_filename_;
   scoped_ptr<std::string> attached_filedata_;
 
   GURL attached_file_url_;
   GURL screenshot_url_;
 
-#if defined(OS_CHROMEOS)
-  // Chromeos specific values for SendReport. Will be deleted in
-  // feedback_util::SendReport once consumed or in SyslogsComplete if
-  // we don't send the logs with the report.
-  scoped_ptr<chromeos::SystemLogsResponse> sys_info_;
-
-  std::string timestamp_;
+  scoped_ptr<feedback_util::SystemLogsMap> sys_info_;
   scoped_ptr<std::string> compressed_logs_;
 
-  // Flag to indicate whether or not we should send the system information
-  // gathered with the report or not.
-  bool send_sys_info_;
-
-  // Flags to indicate if various pieces of data needed for the report have
-  // been collected yet or are still pending collection.
-  bool read_attached_file_complete_;
-  bool syslogs_collection_complete_;
-#endif
   bool feedback_page_data_complete_;
+  bool syslogs_compression_complete_;
 
   DISALLOW_COPY_AND_ASSIGN(FeedbackData);
 };
diff --git a/chrome/browser/feedback/feedback_util.cc b/chrome/browser/feedback/feedback_util.cc
index 738bf9e..409088c 100644
--- a/chrome/browser/feedback/feedback_util.cc
+++ b/chrome/browser/feedback/feedback_util.cc
@@ -14,14 +14,19 @@
 #include "base/file_version_info.h"
 #include "base/memory/singleton.h"
 #include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/windows_version.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/api/feedback_private/feedback_private_api.h"
 #include "chrome/browser/metrics/variations/variations_http_header_provider.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_util.h"
+#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/chrome_version_info.h"
 #include "chrome/common/metrics/metrics_log_manager.h"
@@ -42,21 +47,65 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
-using content::WebContents;
+#if defined(OS_CHROMEOS)
+#include "ash/shell.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#endif
 
 namespace {
+
 const base::FilePath::CharType kLogsFilename[] =
     FILE_PATH_LITERAL("system_logs.txt");
+
+void DispatchFeedback(Profile* profile, std::string* post_body, int64 delay);
+
+// Check the key/value pair to see if it is one of the screensize, keys. If so,
+// populate the screensize structure with the key.
+bool IsScreensizeInfo(const std::string key,
+                      const std::string value,
+                      gfx::Rect* screen_size) {
+  if (key == feedback_util::kScreensizeHeightKey) {
+    int height = 0;
+    base::StringToInt(value, &height);
+    screen_size->SetRect(0, 0, screen_size->width(), height);
+    return true;
+  } else if (key == feedback_util::kScreensizeWidthKey) {
+    int width = 0;
+    base::StringToInt(value, &width);
+    screen_size->SetRect(0, 0, width, screen_size->height());
+    return true;
+  }
+  return false;
 }
 
-namespace chrome {
-const char kAppLauncherCategoryTag[] = "AppLauncher";
-}  // namespace chrome
+GURL GetTargetTabUrl(int session_id, int index) {
+  Browser* browser = chrome::FindBrowserWithID(session_id);
+  // Sanity checks.
+  if (!browser || index >= browser->tab_strip_model()->count())
+    return GURL();
 
-const int kFeedbackVersion = 1;
+  if (index >= 0) {
+    content::WebContents* target_tab =
+        browser->tab_strip_model()->GetWebContentsAt(index);
+    if (target_tab)
+      return target_tab->GetURL();
+  }
 
-const char kReportPhishingUrl[] =
-    "http://www.google.com/safebrowsing/report_phish/";
+  return GURL();
+}
+
+gfx::Rect GetScreenSize(Browser* browser) {
+#if defined(OS_CHROMEOS)
+  // For ChromeOS, don't use the browser window but the root window
+  // instead to grab the screenshot. We want everything on the screen, not
+  // just the current browser.
+  gfx::NativeWindow native_window = ash::Shell::GetPrimaryRootWindow();
+  return gfx::Rect(native_window->bounds());
+#else
+  return gfx::Rect(browser->window()->GetBounds().size());
+#endif
+}
 
 // URL to post bug reports to.
 const char kFeedbackPostUrl[] =
@@ -65,10 +114,6 @@
 const char kProtBufMimeType[] = "application/x-protobuf";
 const char kPngMimeType[] = "image/png";
 
-// Tags we use in product specific data
-const char kChromeVersionTag[] = "CHROME VERSION";
-const char kOsVersionTag[] = "OS VERSION";
-
 const int kHttpPostSuccessNoContent = 204;
 const int kHttpPostFailNoConnection = -1;
 const int kHttpPostFailClientError = 400;
@@ -78,22 +123,17 @@
 const int64 kRetryDelayIncreaseFactor = 2;
 const int64 kRetryDelayLimit = 14400000;  // 4 hours
 
-#if defined(OS_CHROMEOS)
 const size_t kFeedbackMaxLength = 4 * 1024;
 const size_t kFeedbackMaxLineCount = 40;
 
 const char kArbitraryMimeType[] = "application/octet-stream";
 const char kLogsAttachmentName[] = "system_logs.zip";
 
-const char kTimestampTag[] = "TIMESTAMP";
-
 const int kChromeOSProductId = 208;
-#endif
-
 const int kChromeBrowserProductId = 237;
 
 // Simple net::URLFetcherDelegate to clean up URLFetcher on completion.
-class FeedbackUtil::PostCleanup : public net::URLFetcherDelegate {
+class PostCleanup : public net::URLFetcherDelegate {
  public:
   PostCleanup(Profile* profile,
               std::string* post_body,
@@ -117,7 +157,7 @@
 // Don't use the data parameter, instead use the pointer we pass into every
 // post cleanup object - that pointer will be deleted and deleted only on a
 // successful post to the feedback server.
-void FeedbackUtil::PostCleanup::OnURLFetchComplete(
+void PostCleanup::OnURLFetchComplete(
     const net::URLFetcher* source) {
   std::stringstream error_stream;
   int response_code = source->GetResponseCode();
@@ -134,7 +174,7 @@
     } else {
       previous_delay_ = kInitialRetryDelay;
     }
-    FeedbackUtil::DispatchFeedback(profile_, post_body_, previous_delay_);
+    DispatchFeedback(profile_, post_body_, previous_delay_);
 
     // Process the error for debug output
     if (response_code == kHttpPostFailNoConnection) {
@@ -158,41 +198,9 @@
   delete this;
 }
 
-// static
-void FeedbackUtil::SetOSVersion(std::string* os_version) {
-#if defined(OS_WIN)
-  base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
-  base::win::OSInfo::VersionNumber version_number = os_info->version_number();
-  *os_version = base::StringPrintf("%d.%d.%d",
-                                   version_number.major,
-                                   version_number.minor,
-                                   version_number.build);
-  int service_pack = os_info->service_pack().major;
-  if (service_pack > 0)
-    os_version->append(base::StringPrintf("Service Pack %d", service_pack));
-#elif defined(OS_MACOSX)
-  *os_version = base::SysInfo::OperatingSystemVersion();
-#else
-  *os_version = "unknown";
-#endif
-}
-
-// static
-void FeedbackUtil::DispatchFeedback(Profile* profile,
-                                    std::string* post_body,
-                                    int64 delay) {
-  DCHECK(post_body);
-
-  base::MessageLoop::current()->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&FeedbackUtil::SendFeedback, profile, post_body, delay),
-      base::TimeDelta::FromMilliseconds(delay));
-}
-
-// static
-void FeedbackUtil::SendFeedback(Profile* profile,
-                                std::string* post_body,
-                                int64 previous_delay) {
+void SendFeedback(Profile* profile,
+                  std::string* post_body,
+                  int64 previous_delay) {
   DCHECK(post_body);
 
   GURL post_url;
@@ -205,7 +213,7 @@
 
   net::URLFetcher* fetcher = net::URLFetcher::Create(
       post_url, net::URLFetcher::POST,
-      new FeedbackUtil::PostCleanup(profile, post_body, previous_delay));
+      new PostCleanup(profile, post_body, previous_delay));
   fetcher->SetRequestContext(profile->GetRequestContext());
   fetcher->SetLoadFlags(
       net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES);
@@ -219,11 +227,18 @@
   fetcher->Start();
 }
 
+void DispatchFeedback(Profile* profile, std::string* post_body, int64 delay) {
+  DCHECK(post_body);
 
-// static
-void FeedbackUtil::AddFeedbackData(
-    userfeedback::ExtensionSubmit* feedback_data,
-    const std::string& key, const std::string& value) {
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&SendFeedback, profile, post_body, delay),
+      base::TimeDelta::FromMilliseconds(delay));
+}
+
+
+void AddFeedbackData(userfeedback::ExtensionSubmit* feedback_data,
+                     const std::string& key, const std::string& value) {
   // Don't bother with empty keys or values
   if (key == "" || value == "") return;
   // Create log_value object and add it to the web_data object
@@ -234,8 +249,7 @@
   *(web_data->add_product_specific_data()) = log_value;
 }
 
-#if defined(OS_CHROMEOS)
-bool FeedbackUtil::ValidFeedbackSize(const std::string& content) {
+bool ValidFeedbackSize(const std::string& content) {
   if (content.length() > kFeedbackMaxLength)
     return false;
   const size_t line_count = std::count(content.begin(), content.end(), '\n');
@@ -243,92 +257,73 @@
     return false;
   return true;
 }
-#endif
 
-// static
-void FeedbackUtil::SendReport(scoped_refptr<FeedbackData> data) {
+}  // namespace
+
+namespace chrome {
+
+const char kAppLauncherCategoryTag[] = "AppLauncher";
+
+void ShowFeedbackPage(Browser* browser,
+                    const std::string& description_template,
+                    const std::string& category_tag) {
+  DCHECK(browser);
+
+  // Get the current browser's screensize and send it with the feedback request
+  // event - this browser may have changed or even been closed by the time that
+  // feedback is sent.
+  gfx::Rect screen_size = GetScreenSize(browser);
+  GURL page_url = GetTargetTabUrl(
+      browser->session_id().id(), browser->tab_strip_model()->active_index());
+
+  extensions::FeedbackPrivateAPI* api =
+      extensions::FeedbackPrivateAPI::GetFactoryInstance()->GetForProfile(
+          browser->profile());
+
+  api->RequestFeedback(description_template,
+                       category_tag,
+                       page_url,
+                       screen_size);
+}
+
+}  // namespace chrome
+
+namespace feedback_util {
+
+const char kScreensizeHeightKey[] = "ScreensizeHeight";
+const char kScreensizeWidthKey[] = "ScreensizeWidth";
+
+void SendReport(scoped_refptr<FeedbackData> data) {
   if (!data.get()) {
-    LOG(ERROR) << "FeedbackUtil::SendReport called with NULL data!";
+    LOG(ERROR) << "SendReport called with NULL data!";
     NOTREACHED();
     return;
   }
 
-  // Create google feedback protocol buffer objects
   userfeedback::ExtensionSubmit feedback_data;
-  // type id set to 0, unused field but needs to be initialized to 0
+  // Unused field, needs to be 0 though.
   feedback_data.set_type_id(0);
 
   userfeedback::CommonData* common_data = feedback_data.mutable_common_data();
-  userfeedback::WebData* web_data = feedback_data.mutable_web_data();
-
-  // Set our user agent.
-  userfeedback::Navigator* navigator = web_data->mutable_navigator();
-  navigator->set_user_agent(content::GetUserAgent(GURL()));
-
-  // Set GAIA id to 0. We're not using gaia id's for recording
-  // use feedback - we're using the e-mail field, allows users to
-  // submit feedback from incognito mode and specify any mail id
-  // they wish
+  // We're not using gaia ids, we're using the e-mail field instead.
   common_data->set_gaia_id(0);
-
-  // Add the user e-mail to the feedback object
   common_data->set_user_email(data->user_email());
-
-  // Add the description to the feedback object
   common_data->set_description(data->description());
 
-  // Add the language
   std::string chrome_locale = g_browser_process->GetApplicationLocale();
   common_data->set_source_description_language(chrome_locale);
 
-  // Set the url
+  userfeedback::WebData* web_data = feedback_data.mutable_web_data();
   web_data->set_url(data->page_url());
+  web_data->mutable_navigator()->set_user_agent(content::GetUserAgent(GURL()));
 
-  // Add the Chrome version
-  chrome::VersionInfo version_info;
-  if (version_info.is_valid()) {
-    std::string chrome_version = version_info.Name() + " - " +
-        version_info.Version() +
-        " (" + version_info.LastChange() + ")";
-    AddFeedbackData(&feedback_data, std::string(kChromeVersionTag),
-                    chrome_version);
-  }
-
-  // We don't need the OS version for ChromeOS since we get it in
-  // CHROMEOS_RELEASE_VERSION from /etc/lsb-release
-#if !defined(OS_CHROMEOS)
-  // Add OS version (eg, for WinXP SP2: "5.1.2600 Service Pack 2").
-  std::string os_version;
-  SetOSVersion(&os_version);
-  AddFeedbackData(&feedback_data, std::string(kOsVersionTag), os_version);
-#endif
-
-  // Include the page image if we have one.
-  if (data->image().get() && data->image()->size()) {
-    userfeedback::PostedScreenshot screenshot;
-    screenshot.set_mime_type(kPngMimeType);
-    // Set the dimensions of the screenshot
-    userfeedback::Dimensions dimensions;
-    gfx::Rect& screen_size = GetScreenshotSize();
-    dimensions.set_width(static_cast<float>(screen_size.width()));
-    dimensions.set_height(static_cast<float>(screen_size.height()));
-    *(screenshot.mutable_dimensions()) = dimensions;
-
-    int image_data_size = data->image()->size();
-    char* image_data = reinterpret_cast<char*>(&(data->image()->front()));
-    screenshot.set_binary_content(std::string(image_data, image_data_size));
-
-    // Set the screenshot object in feedback
-    *(feedback_data.mutable_screenshot()) = screenshot;
-  }
-
-#if defined(OS_CHROMEOS)
+  gfx::Rect screen_size;
   if (data->sys_info()) {
-    // Add the product specific data
-    for (chromeos::SystemLogsResponse::const_iterator i =
-        data->sys_info()->begin(); i != data->sys_info()->end(); ++i) {
-      if (ValidFeedbackSize(i->second)) {
-        AddFeedbackData(&feedback_data, i->first, i->second);
+    for (SystemLogsMap::const_iterator i = data->sys_info()->begin();
+        i != data->sys_info()->end(); ++i) {
+      if (!IsScreensizeInfo(i->first, i->second, &screen_size)) {
+        if (ValidFeedbackSize(i->second))
+          AddFeedbackData(&feedback_data, i->first, i->second);
       }
     }
 
@@ -341,29 +336,44 @@
     }
   }
 
-  if (data->timestamp() != "")
-    AddFeedbackData(&feedback_data, std::string(kTimestampTag),
-                    data->timestamp());
-
-  if (data->attached_filename() != "" &&
+  if (!data->attached_filename().empty() &&
       data->attached_filedata() &&
-      data->attached_filedata()->size()) {
+      !data->attached_filedata()->empty()) {
     userfeedback::ProductSpecificBinaryData attached_file;
     attached_file.set_mime_type(kArbitraryMimeType);
-    attached_file.set_name(data->attached_filename());
+    // We need to use the UTF8Unsafe methods here to accomodate Windows, which
+    // uses wide strings to store filepaths.
+    std::string name = base::FilePath::FromUTF8Unsafe(
+        data->attached_filename()).BaseName().AsUTF8Unsafe();
+    attached_file.set_name(name);
     attached_file.set_data(*data->attached_filedata());
     *(feedback_data.add_product_specific_binary_data()) = attached_file;
   }
-#endif
 
-  // Set our category tag if we have one
+  // NOTE: Screenshot needs to be processed after system info since we'll get
+  // the screenshot dimensions from system info.
+  if (data->image() && data->image()->size()) {
+    userfeedback::PostedScreenshot screenshot;
+    screenshot.set_mime_type(kPngMimeType);
+
+    // Set the dimensions of the screenshot
+    userfeedback::Dimensions dimensions;
+    dimensions.set_width(static_cast<float>(screen_size.width()));
+    dimensions.set_height(static_cast<float>(screen_size.height()));
+
+    *(screenshot.mutable_dimensions()) = dimensions;
+    screenshot.set_binary_content(*data->image());
+
+    *(feedback_data.mutable_screenshot()) = screenshot;
+  }
+
   if (data->category_tag().size())
     feedback_data.set_bucket(data->category_tag());
 
-  // Set our Chrome specific data
+  // Set whether we're reporting from ChromeOS or Chrome on another platform.
   userfeedback::ChromeData chrome_data;
-  chrome_data.set_chrome_platform(
 #if defined(OS_CHROMEOS)
+  chrome_data.set_chrome_platform(
       userfeedback::ChromeData_ChromePlatform_CHROME_OS);
   userfeedback::ChromeOsData chrome_os_data;
   chrome_os_data.set_category(
@@ -371,6 +381,7 @@
   *(chrome_data.mutable_chrome_os_data()) = chrome_os_data;
   feedback_data.set_product_id(kChromeOSProductId);
 #else
+  chrome_data.set_chrome_platform(
       userfeedback::ChromeData_ChromePlatform_CHROME_BROWSER);
   userfeedback::ChromeBrowserData chrome_browser_data;
   chrome_browser_data.set_category(
@@ -381,60 +392,15 @@
 
   *(feedback_data.mutable_chrome_data()) = chrome_data;
 
-  // Serialize our report to a string pointer we can pass around
+  // This pointer will eventually get deleted by the PostCleanup class, after
+  // we've either managed to successfully upload the report or died trying.
   std::string* post_body = new std::string;
   feedback_data.SerializeToString(post_body);
 
-  // We have the body of our POST, so send it off to the server with 0 delay
   DispatchFeedback(data->profile(), post_body, 0);
 }
 
-#if defined(FULL_SAFE_BROWSING)
-// static
-void FeedbackUtil::ReportPhishing(WebContents* current_tab,
-                                  const std::string& phishing_url) {
-  current_tab->GetController().LoadURL(
-      safe_browsing_util::GeneratePhishingReportUrl(
-          kReportPhishingUrl, phishing_url,
-          false /* not client-side detection */),
-      content::Referrer(),
-      content::PAGE_TRANSITION_LINK,
-      std::string());
-}
-#endif
-
-static std::vector<unsigned char>* screenshot_png = NULL;
-static gfx::Rect* screenshot_size = NULL;
-
-// static
-std::vector<unsigned char>* FeedbackUtil::GetScreenshotPng() {
-  if (screenshot_png == NULL)
-    screenshot_png = new std::vector<unsigned char>;
-  return screenshot_png;
-}
-
-// static
-void FeedbackUtil::ClearScreenshotPng() {
-  if (screenshot_png)
-    screenshot_png->clear();
-}
-
-// static
-gfx::Rect& FeedbackUtil::GetScreenshotSize() {
-  if (screenshot_size == NULL)
-    screenshot_size = new gfx::Rect();
-  return *screenshot_size;
-}
-
-// static
-void FeedbackUtil::SetScreenshotSize(const gfx::Rect& rect) {
-  gfx::Rect& screen_size = GetScreenshotSize();
-  screen_size = rect;
-}
-
-// static
-bool FeedbackUtil::ZipString(const std::string& logs,
-                             std::string* compressed_logs) {
+bool ZipString(const std::string& logs, std::string* compressed_logs) {
   base::FilePath temp_path;
   base::FilePath zip_file;
 
@@ -456,3 +422,5 @@
 
   return true;
 }
+
+}  // namespace feedback_util
diff --git a/chrome/browser/feedback/feedback_util.h b/chrome/browser/feedback/feedback_util.h
index 7849186..f90e154 100644
--- a/chrome/browser/feedback/feedback_util.h
+++ b/chrome/browser/feedback/feedback_util.h
@@ -9,12 +9,11 @@
 
 #include "base/basictypes.h"
 #include "base/files/file_path.h"
-#include "chrome/browser/feedback/feedback_data.h"
+#include "base/memory/ref_counted.h"
 #include "chrome/browser/feedback/proto/common.pb.h"
 #include "chrome/browser/feedback/proto/dom.pb.h"
 #include "chrome/browser/feedback/proto/extension.pb.h"
 #include "chrome/browser/feedback/proto/math.pb.h"
-#include "chrome/browser/ui/webui/screenshot_source.h"
 #include "ui/gfx/rect.h"
 
 #if defined(OS_MACOSX)
@@ -23,6 +22,7 @@
 #include "base/win/windows_version.h"
 #endif
 
+class FeedbackData;
 class Profile;
 
 namespace content {
@@ -33,69 +33,16 @@
 extern const char kAppLauncherCategoryTag[];
 }  // namespace chrome
 
-class FeedbackUtil {
- public:
+namespace feedback_util {
+  typedef std::map<std::string, std::string> SystemLogsMap;
 
-#if defined(OS_MACOSX)
-  enum BugType {
-    PAGE_WONT_LOAD = 0,
-    PAGE_LOOKS_ODD,
-    PHISHING_PAGE,
-    CANT_SIGN_IN,
-    CHROME_MISBEHAVES,
-    SOMETHING_MISSING,
-    BROWSER_CRASH,
-    OTHER_PROBLEM
-  };
-#endif
+  extern const char kScreensizeHeightKey[];
+  extern const char kScreensizeWidthKey[];
 
 
-  // SetOSVersion copies the maj.minor.build + servicePack_string
-  // into a string. We currently have:
-  //   base::win::GetVersion returns WinVersion, which is just
-  //     an enum of 2000, XP, 2003, or VISTA. Not enough detail for
-  //     bug reports.
-  //   base::SysInfo::OperatingSystemVersion returns an std::string
-  //     but doesn't include the build or service pack. That function
-  //     is probably the right one to extend, but will require changing
-  //     all the call sites or making it a wrapper around another util.
-  static void SetOSVersion(std::string *os_version);
+  void SendReport(scoped_refptr<FeedbackData> data);
+  bool ZipString(const std::string& logs, std::string* compressed_logs);
 
-  // Send the feedback report after the specified delay
-  static void DispatchFeedback(Profile* profile,
-                               std::string* feedback_data,
-                               int64 delay);
-
-  // Generates bug report data.
-  static void SendReport(scoped_refptr<FeedbackData> data);
-  // Redirects the user to Google's phishing reporting page.
-  static void ReportPhishing(content::WebContents* current_tab,
-                             const std::string& phishing_url);
-  // Maintains a single vector of bytes to store the last screenshot taken.
-  static std::vector<unsigned char>* GetScreenshotPng();
-  static void ClearScreenshotPng();
-  static void SetScreenshotSize(const gfx::Rect& rect);
-  static gfx::Rect& GetScreenshotSize();
-  static bool ZipString(const std::string& logs, std::string* compressed_logs);
-
-  class PostCleanup;
-
- private:
-  // Add a key value pair to the feedback object
-  static void AddFeedbackData(
-      userfeedback::ExtensionSubmit* feedback_data,
-      const std::string& key, const std::string& value);
-
-  // Send the feedback report
-  static void SendFeedback(Profile* profile,
-                           std::string* feedback_data,
-                           int64 previous_delay);
-
-#if defined(OS_CHROMEOS)
-  static bool ValidFeedbackSize(const std::string& content);
-#endif
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(FeedbackUtil);
-};
+}  // namespace feedback_util
 
 #endif  // CHROME_BROWSER_FEEDBACK_FEEDBACK_UTIL_H_
diff --git a/chrome/browser/feedback/tracing_manager.cc b/chrome/browser/feedback/tracing_manager.cc
index 3ea14cf..47d5a91 100644
--- a/chrome/browser/feedback/tracing_manager.cc
+++ b/chrome/browser/feedback/tracing_manager.cc
@@ -94,7 +94,7 @@
     return;
 
   std::string output_val;
-  FeedbackUtil::ZipString(data_, &output_val);
+  feedback_util::ZipString(data_, &output_val);
 
   scoped_refptr<base::RefCountedString> output;
   output->TakeString(&output_val);
diff --git a/chrome/browser/file_select_helper.cc b/chrome/browser/file_select_helper.cc
index ef84e7c..c92b381 100644
--- a/chrome/browser/file_select_helper.cc
+++ b/chrome/browser/file_select_helper.cc
@@ -123,7 +123,7 @@
   profile_->set_last_selected_directory(file.file_path.DirName());
 
   const base::FilePath& path = file.local_path;
-  if (dialog_type_ == ui::SelectFileDialog::SELECT_FOLDER) {
+  if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
     StartNewEnumeration(path, kFileSelectEnumerationId, render_view_host_);
     return;
   }
@@ -377,8 +377,8 @@
     case FileChooserParams::OpenMultiple:
       dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
       break;
-    case FileChooserParams::OpenFolder:
-      dialog_type_ = ui::SelectFileDialog::SELECT_FOLDER;
+    case FileChooserParams::UploadFolder:
+      dialog_type_ = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER;
       break;
     case FileChooserParams::Save:
       dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
diff --git a/chrome/browser/first_run/first_run.cc b/chrome/browser/first_run/first_run.cc
index 315d6f9..70fe2a6 100644
--- a/chrome/browser/first_run/first_run.cc
+++ b/chrome/browser/first_run/first_run.cc
@@ -11,6 +11,7 @@
 #include "base/file_util.h"
 #include "base/files/file_path.h"
 #include "base/lazy_instance.h"
+#include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram.h"
 #include "base/path_service.h"
@@ -35,12 +36,12 @@
 #include "chrome/browser/shell_integration.h"
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/signin/signin_tracker.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/global_error/global_error_service.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
 #include "chrome/common/chrome_paths.h"
@@ -51,6 +52,8 @@
 #include "chrome/installer/util/master_preferences_constants.h"
 #include "chrome/installer/util/util_constants.h"
 #include "components/user_prefs/pref_registry_syncable.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/user_metrics.h"
@@ -299,6 +302,119 @@
   std::transform(src.begin(), src.end(), ret->begin(), &UrlFromString);
 }
 
+// Show the first run search engine bubble at the first appropriate opportunity.
+// This bubble may be delayed by other UI, like global errors and sync promos.
+class FirstRunBubbleLauncher : public content::NotificationObserver {
+ public:
+  // Show the bubble at the first appropriate opportunity. This function
+  // instantiates a FirstRunBubbleLauncher, which manages its own lifetime.
+  static void ShowFirstRunBubbleSoon();
+
+ private:
+  FirstRunBubbleLauncher();
+  virtual ~FirstRunBubbleLauncher();
+
+  // content::NotificationObserver:
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
+  content::NotificationRegistrar registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(FirstRunBubbleLauncher);
+};
+
+// static
+void FirstRunBubbleLauncher::ShowFirstRunBubbleSoon() {
+  SetShowFirstRunBubblePref(first_run::FIRST_RUN_BUBBLE_SHOW);
+  // This FirstRunBubbleLauncher instance will manage its own lifetime.
+  new FirstRunBubbleLauncher();
+}
+
+FirstRunBubbleLauncher::FirstRunBubbleLauncher() {
+  registrar_.Add(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
+                 content::NotificationService::AllSources());
+
+  // This notification is required to observe the switch between the sync setup
+  // page and the general settings page.
+  registrar_.Add(this, chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
+                 content::NotificationService::AllSources());
+}
+
+FirstRunBubbleLauncher::~FirstRunBubbleLauncher() {}
+
+void FirstRunBubbleLauncher::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  DCHECK(type == content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME ||
+         type == chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED);
+
+  Browser* browser = chrome::FindBrowserWithWebContents(
+      content::Source<content::WebContents>(source).ptr());
+  if (!browser || !browser->is_type_tabbed())
+    return;
+
+  // Check the preference to determine if the bubble should be shown.
+  PrefService* prefs = g_browser_process->local_state();
+  if (!prefs || prefs->GetInteger(prefs::kShowFirstRunBubbleOption) !=
+      first_run::FIRST_RUN_BUBBLE_SHOW) {
+    delete this;
+    return;
+  }
+
+  content::WebContents* contents =
+      browser->tab_strip_model()->GetActiveWebContents();
+
+  // Suppress the first run bubble if a Gaia sign in page, the continue
+  // URL for the sign in page or the sync setup page is showing.
+  if (contents &&
+      (gaia::IsGaiaSignonRealm(contents->GetURL().GetOrigin()) ||
+       signin::IsContinueUrlForWebBasedSigninFlow(contents->GetURL()) ||
+       contents->GetURL() == GURL(std::string(chrome::kChromeUISettingsURL) +
+                                  chrome::kSyncSetupSubPage))) {
+    return;
+  }
+
+  if (contents && contents->GetURL().SchemeIs(chrome::kChromeUIScheme)) {
+    // Suppress the first run bubble if 'make chrome metro' flow is showing.
+    if (contents->GetURL().host() == chrome::kChromeUIMetroFlowHost)
+      return;
+
+    // Suppress the first run bubble if the NTP sync promo bubble is showing
+    // or if sign in is in progress.
+    if (contents->GetURL().host() == chrome::kChromeUINewTabHost) {
+      Profile* profile =
+          Profile::FromBrowserContext(contents->GetBrowserContext());
+      SigninManagerBase* manager =
+          SigninManagerFactory::GetForProfile(profile);
+      bool signin_in_progress = manager &&
+          (!manager->GetAuthenticatedUsername().empty() &&
+              SigninTracker::GetSigninState(profile, NULL) !=
+                  SigninTracker::SIGNIN_COMPLETE);
+      bool is_promo_bubble_visible =
+          profile->GetPrefs()->GetBoolean(prefs::kSyncPromoShowNTPBubble);
+
+      if (is_promo_bubble_visible || signin_in_progress)
+        return;
+    }
+  }
+
+  // Suppress the first run bubble if a global error bubble is pending.
+  GlobalErrorService* global_error_service =
+      GlobalErrorServiceFactory::GetForProfile(browser->profile());
+  if (global_error_service->GetFirstGlobalErrorWithBubbleView() != NULL)
+    return;
+
+  // Reset the preference and notifications to avoid showing the bubble again.
+  prefs->SetInteger(prefs::kShowFirstRunBubbleOption,
+                    first_run::FIRST_RUN_BUBBLE_DONT_SHOW);
+
+  // Show the bubble now and destroy this bubble launcher.
+  browser->ShowFirstRunBubble();
+  delete this;
+}
+
 }  // namespace
 
 namespace first_run {
@@ -442,6 +558,13 @@
   }
 }
 
+bool CreateSentinel() {
+  base::FilePath first_run_sentinel;
+  if (!internal::GetFirstRunSentinelFilePath(&first_run_sentinel))
+    return false;
+  return file_util::WriteFile(first_run_sentinel, "", 0) != -1;
+}
+
 // -- Platform-specific functions --
 
 #if !defined(OS_LINUX) && !defined(OS_BSD)
@@ -469,21 +592,27 @@
   if (internal::first_run_ != internal::FIRST_RUN_UNKNOWN)
     return internal::first_run_ == internal::FIRST_RUN_TRUE;
 
+  internal::first_run_ = internal::FIRST_RUN_FALSE;
+
   base::FilePath first_run_sentinel;
-  if (!internal::GetFirstRunSentinelFilePath(&first_run_sentinel) ||
-      base::PathExists(first_run_sentinel)) {
-    internal::first_run_ = internal::FIRST_RUN_FALSE;
-    return false;
+  const CommandLine* command_line = CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kForceFirstRun)) {
+    internal::first_run_ = internal::FIRST_RUN_TRUE;
+  } else if (command_line->HasSwitch(switches::kNoFirstRun)) {
+    internal::first_run_ = internal::FIRST_RUN_CANCEL;
+  } else if (internal::GetFirstRunSentinelFilePath(&first_run_sentinel) &&
+             !base::PathExists(first_run_sentinel)) {
+    internal::first_run_ = internal::FIRST_RUN_TRUE;
   }
-  internal::first_run_ = internal::FIRST_RUN_TRUE;
-  return true;
+
+  return internal::first_run_ == internal::FIRST_RUN_TRUE;
 }
 
-bool CreateSentinel() {
-  base::FilePath first_run_sentinel;
-  if (!internal::GetFirstRunSentinelFilePath(&first_run_sentinel))
-    return false;
-  return file_util::WriteFile(first_run_sentinel, "", 0) != -1;
+void CreateSentinelIfNeeded() {
+  if (IsChromeFirstRun() ||
+      internal::first_run_ == internal::FIRST_RUN_CANCEL) {
+    internal::CreateSentinel();
+  }
 }
 
 std::string GetPingDelayPrefName() {
@@ -545,88 +674,6 @@
                             NUM_FIRST_RUN_BUBBLE_METRICS);
 }
 
-// static
-void FirstRunBubbleLauncher::ShowFirstRunBubbleSoon() {
-  SetShowFirstRunBubblePref(FIRST_RUN_BUBBLE_SHOW);
-  // This FirstRunBubbleLauncher instance will manage its own lifetime.
-  new FirstRunBubbleLauncher();
-}
-
-FirstRunBubbleLauncher::FirstRunBubbleLauncher() {
-  registrar_.Add(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-                 content::NotificationService::AllSources());
-}
-
-FirstRunBubbleLauncher::~FirstRunBubbleLauncher() {}
-
-void FirstRunBubbleLauncher::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  DCHECK_EQ(type, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME);
-  Browser* browser = chrome::FindBrowserWithWebContents(
-      content::Source<content::WebContents>(source).ptr());
-  if (!browser || !browser->is_type_tabbed())
-    return;
-
-  // Check the preference to determine if the bubble should be shown.
-  PrefService* prefs = g_browser_process->local_state();
-  if (!prefs || prefs->GetInteger(
-          prefs::kShowFirstRunBubbleOption) != FIRST_RUN_BUBBLE_SHOW) {
-    delete this;
-    return;
-  }
-
-  content::WebContents* contents =
-      browser->tab_strip_model()->GetActiveWebContents();
-
-  // Suppress the first run bubble if a Gaia sign in page or the continue
-  // URL for the sign in page is showing.
-  if (contents &&
-      (gaia::IsGaiaSignonRealm(contents->GetURL().GetOrigin()) ||
-       SyncPromoUI::IsContinueUrlForWebBasedSigninFlow(contents->GetURL()))) {
-    return;
-  }
-
-  if (contents && contents->GetURL().SchemeIs(chrome::kChromeUIScheme)) {
-    // Suppress the first run bubble if 'make chrome metro' flow is showing.
-    if (contents->GetURL().host() == chrome::kChromeUIMetroFlowHost)
-      return;
-
-    // Suppress the first run bubble if the NTP sync promo bubble is showing
-    // or if sign in is in progress.
-    if (contents->GetURL().host() == chrome::kChromeUINewTabHost) {
-      Profile* profile =
-          Profile::FromBrowserContext(contents->GetBrowserContext());
-      SigninManagerBase* manager =
-          SigninManagerFactory::GetForProfile(profile);
-      bool signin_in_progress = manager &&
-          (!manager->GetAuthenticatedUsername().empty() &&
-              SigninTracker::GetSigninState(profile, NULL) !=
-                  SigninTracker::SIGNIN_COMPLETE);
-      bool is_promo_bubble_visible =
-          profile->GetPrefs()->GetBoolean(prefs::kSyncPromoShowNTPBubble);
-
-      if (is_promo_bubble_visible || signin_in_progress)
-        return;
-    }
-  }
-
-  // Suppress the first run bubble if a global error bubble is pending.
-  GlobalErrorService* global_error_service =
-      GlobalErrorServiceFactory::GetForProfile(browser->profile());
-  if (global_error_service->GetFirstGlobalErrorWithBubbleView() != NULL)
-    return;
-
-  // Reset the preference and notifications to avoid showing the bubble again.
-  prefs->SetInteger(prefs::kShowFirstRunBubbleOption,
-                    FIRST_RUN_BUBBLE_DONT_SHOW);
-
-  // Show the bubble now and destroy this bubble launcher.
-  browser->ShowFirstRunBubble();
-  delete this;
-}
-
 void SetMasterPrefsPathForTesting(const base::FilePath& master_prefs) {
   internal::master_prefs_path_for_testing.Get() = master_prefs;
 }
@@ -636,13 +683,6 @@
     MasterPrefs* out_prefs) {
   DCHECK(!user_data_dir.empty());
 
-#if defined(OS_CHROMEOS)
-  // Chrome OS has its own out-of-box-experience code.  Create the sentinel to
-  // mark the fact that we've run once but skip the full first-run flow.
-  CreateSentinel();
-  return SKIP_FIRST_RUN_TASKS;
-#endif
-
   base::FilePath master_prefs_path;
   scoped_ptr<installer::MasterPreferences>
       install_prefs(internal::LoadMasterPrefs(&master_prefs_path));
@@ -664,7 +704,7 @@
     internal::SetDefaultBrowser(install_prefs.get());
   }
 
-  return DO_FIRST_RUN_TASKS;
+  return FIRST_RUN_PROCEED;
 }
 
 void AutoImport(
diff --git a/chrome/browser/first_run/first_run.h b/chrome/browser/first_run/first_run.h
index 7be708f..9f86f83 100644
--- a/chrome/browser/first_run/first_run.h
+++ b/chrome/browser/first_run/first_run.h
@@ -9,14 +9,7 @@
 #include <vector>
 
 #include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/ref_counted.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "ui/gfx/native_widget_types.h"
 
-class CommandLine;
 class GURL;
 class Profile;
 
@@ -62,9 +55,8 @@
 };
 
 enum ProcessMasterPreferencesResult {
-  DO_FIRST_RUN_TASKS = 0,       // Should do the first run tasks.
-  SKIP_FIRST_RUN_TASKS,         // Should skip the first run tasks.
-  EULA_EXIT_NOW,                // Should immediately exit due to EULA flow.
+  FIRST_RUN_PROCEED = 0,  // Proceed with first run.
+  EULA_EXIT_NOW,          // Should immediately exit due to EULA flow.
 };
 
 // See ProcessMasterPreferences for more info about this structure.
@@ -92,8 +84,10 @@
 // Returns true if this is the first time chrome is run for this user.
 bool IsChromeFirstRun();
 
-// Creates the sentinel file that signals that chrome has been configured.
-bool CreateSentinel();
+// Creates the first run sentinel if needed. This should only be called after
+// the process singleton has been grabbed by the current process
+// (http://crbug.com/264694).
+void CreateSentinelIfNeeded();
 
 // Get RLZ ping delay pref name.
 std::string GetPingDelayPrefName();
@@ -175,28 +169,6 @@
     const base::FilePath& user_data_dir,
     MasterPrefs* out_prefs);
 
-// Show the first run search engine bubble at the first appropriate opportunity.
-// This bubble may be delayed by other UI, like global errors and sync promos.
-class FirstRunBubbleLauncher : public content::NotificationObserver {
- public:
-  // Show the bubble at the first appropriate opportunity. This function
-  // instantiates a FirstRunBubbleLauncher, which manages its own lifetime.
-  static void ShowFirstRunBubbleSoon();
-
- private:
-  FirstRunBubbleLauncher();
-  virtual ~FirstRunBubbleLauncher();
-
-  // content::NotificationObserver override:
-  virtual void Observe(int type,
-                       const content::NotificationSource& source,
-                       const content::NotificationDetails& details) OVERRIDE;
-
-  content::NotificationRegistrar registrar_;
-
-  DISALLOW_COPY_AND_ASSIGN(FirstRunBubbleLauncher);
-};
-
 }  // namespace first_run
 
 #endif  // CHROME_BROWSER_FIRST_RUN_FIRST_RUN_H_
diff --git a/chrome/browser/first_run/first_run_internal.h b/chrome/browser/first_run/first_run_internal.h
index eec3cf8..e5162b4 100644
--- a/chrome/browser/first_run/first_run_internal.h
+++ b/chrome/browser/first_run/first_run_internal.h
@@ -23,7 +23,9 @@
 enum FirstRunState {
   FIRST_RUN_UNKNOWN,  // The state is not tested or set yet.
   FIRST_RUN_TRUE,
-  FIRST_RUN_FALSE
+  FIRST_RUN_FALSE,
+  FIRST_RUN_CANCEL,  // This shouldn't be considered first run but the sentinel
+                     // should be created anyways.
 };
 
 // This variable should only be accessed through IsChromeFirstRun().
@@ -48,6 +50,9 @@
 
 void SetDefaultBrowser(installer::MasterPreferences* install_prefs);
 
+// Creates the sentinel file that signals that chrome has been configured.
+bool CreateSentinel();
+
 // -- Platform-specific functions --
 
 void DoPostImportPlatformSpecificTasks(Profile* profile);
diff --git a/chrome/browser/first_run/first_run_unittest.cc b/chrome/browser/first_run/first_run_unittest.cc
index 0e0edcb..badf91b 100644
--- a/chrome/browser/first_run/first_run_unittest.cc
+++ b/chrome/browser/first_run/first_run_unittest.cc
@@ -33,7 +33,7 @@
 };
 
 TEST_F(FirstRunTest, RemoveSentinel) {
-  EXPECT_TRUE(CreateSentinel());
+  EXPECT_TRUE(internal::CreateSentinel());
   EXPECT_TRUE(base::PathExists(sentinel_path_));
 
   EXPECT_TRUE(RemoveSentinel());
diff --git a/chrome/browser/geolocation/chrome_geolocation_permission_context_factory.h b/chrome/browser/geolocation/chrome_geolocation_permission_context_factory.h
index 0e161a6..874722b 100644
--- a/chrome/browser/geolocation/chrome_geolocation_permission_context_factory.h
+++ b/chrome/browser/geolocation/chrome_geolocation_permission_context_factory.h
@@ -28,7 +28,7 @@
   ChromeGeolocationPermissionContextFactory();
   virtual ~ChromeGeolocationPermissionContextFactory();
 
-  // |BrowserContextKeyedBaseFactory| methods:
+  // BrowserContextKeyedBaseFactory methods:
   virtual BrowserContextKeyedService*
       BuildServiceInstanceFor(content::BrowserContext* profile) const OVERRIDE;
   virtual void RegisterProfilePrefs(
diff --git a/chrome/browser/geolocation/geolocation_browsertest.cc b/chrome/browser/geolocation/geolocation_browsertest.cc
index 95158db..23dd587 100644
--- a/chrome/browser/geolocation/geolocation_browsertest.cc
+++ b/chrome/browser/geolocation/geolocation_browsertest.cc
@@ -538,9 +538,7 @@
   CheckStringValueFromJavascript("1", "geoGetLastError()");
 }
 
-// crbug.com/176291
-IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest,
-                       DISABLED_IFramesWithFreshPosition) {
+IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, IFramesWithFreshPosition) {
   set_html_for_tests("/geolocation/iframes_different_origin.html");
   ASSERT_TRUE(Initialize(INITIALIZATION_IFRAMES));
   LoadIFrames(2);
@@ -622,9 +620,7 @@
   CheckGeoposition(cached_position_latitude, cached_position_lognitude);
 }
 
-// crbug.com/176291
-IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest,
-                       DISABLED_CancelPermissionForFrame) {
+IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, CancelPermissionForFrame) {
   set_html_for_tests("/geolocation/iframes_different_origin.html");
   ASSERT_TRUE(Initialize(INITIALIZATION_IFRAMES));
   LoadIFrames(2);
@@ -718,8 +714,7 @@
   CheckGeoposition(final_position_latitude, final_position_longitude);
 }
 
-// crbug.com/176291
-IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, DISABLED_TabDestroyed) {
+IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, TabDestroyed) {
   set_html_for_tests("/geolocation/tab_destroyed.html");
   ASSERT_TRUE(Initialize(INITIALIZATION_IFRAMES));
   LoadIFrames(3);
diff --git a/chrome/browser/google/google_url_tracker.cc b/chrome/browser/google/google_url_tracker.cc
index 768de18..edae47e 100644
--- a/chrome/browser/google/google_url_tracker.cc
+++ b/chrome/browser/google/google_url_tracker.cc
@@ -239,8 +239,13 @@
   if (in_startup_sleep_ || already_fetched_ || !need_to_fetch_)
     return;
 
+  // Some switches should disable the Google URL tracker entirely.  If we can't
+  // do background networking, we can't do the necessary fetch, and if the user
+  // specified a Google base URL manually, we shouldn't bother to look up any
+  // alternatives or offer to switch to them.
   if (CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableBackgroundNetworking))
+      switches::kDisableBackgroundNetworking) ||
+      CommandLine::ForCurrentProcess()->HasSwitch(switches::kGoogleBaseURL))
     return;
 
   std::string fetch_url = CommandLine::ForCurrentProcess()->
diff --git a/chrome/browser/google_apis/DEPS b/chrome/browser/google_apis/DEPS
index 716bc43..49f3249 100644
--- a/chrome/browser/google_apis/DEPS
+++ b/chrome/browser/google_apis/DEPS
@@ -8,7 +8,6 @@
 specific_include_rules = {
   # AuthService should be gone. crbug.com/162157
   "auth_service\.(h|cc)": [
-    "!chrome/browser/profiles/profile.h",
     "!chrome/browser/signin/oauth2_token_service.h",
   ],
 }
diff --git a/chrome/browser/google_apis/auth_service.cc b/chrome/browser/google_apis/auth_service.cc
index 584a709..481fc8a 100644
--- a/chrome/browser/google_apis/auth_service.cc
+++ b/chrome/browser/google_apis/auth_service.cc
@@ -12,15 +12,8 @@
 #include "base/message_loop/message_loop_proxy.h"
 #include "base/metrics/histogram.h"
 #include "chrome/browser/google_apis/auth_service_observer.h"
-#include "chrome/browser/profiles/profile.h"
-#include "google_apis/gaia/gaia_constants.h"
-#include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 
-#if defined(OS_CHROMEOS)
-#include "chromeos/login/login_state.h"
-#endif  // OS_CHROMEOS
-
 namespace google_apis {
 
 namespace {
@@ -51,7 +44,6 @@
                                  const GoogleServiceAuthError& error) OVERRIDE;
 
   AuthStatusCallback callback_;
-  OAuth2TokenService::ScopeSet scopes_;
   scoped_ptr<OAuth2TokenService::Request> request_;
   base::ThreadChecker thread_checker_;
 
@@ -63,11 +55,13 @@
     net::URLRequestContextGetter* url_request_context_getter,
     const AuthStatusCallback& callback,
     const std::vector<std::string>& scopes)
-    : callback_(callback),
-      scopes_(scopes.begin(), scopes.end()) {
+    : callback_(callback) {
   DCHECK(!callback_.is_null());
   request_ = oauth2_token_service->
-      StartRequestWithContext(url_request_context_getter, scopes_, this);
+      StartRequestWithContext(
+          url_request_context_getter,
+          OAuth2TokenService::ScopeSet(scopes.begin(), scopes.end()),
+          this);
 }
 
 AuthRequest::~AuthRequest() {}
@@ -129,7 +123,6 @@
       url_request_context_getter_(url_request_context_getter),
       scopes_(scopes),
       weak_ptr_factory_(this) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(oauth2_token_service);
 
   // Get OAuth2 refresh token (if we have any) and register for its updates.
@@ -237,20 +230,4 @@
                     OnOAuth2RefreshTokenChanged());
 }
 
-// static
-bool AuthService::CanAuthenticate(Profile* profile) {
-#if defined(OS_CHROMEOS)
-  if (!chromeos::LoginState::IsInitialized())
-    return false;
-  if (!chromeos::LoginState::Get()->IsUserGaiaAuthenticated())
-    return false;
-#endif  // OS_CHROMEOS
-
-  // Authentication cannot be done with the incognito mode profile.
-  if (profile->IsOffTheRecord())
-    return false;
-
-  return true;
-}
-
 }  // namespace google_apis
diff --git a/chrome/browser/google_apis/auth_service.h b/chrome/browser/google_apis/auth_service.h
index 20ffe1e..a93e017 100644
--- a/chrome/browser/google_apis/auth_service.h
+++ b/chrome/browser/google_apis/auth_service.h
@@ -14,8 +14,6 @@
 #include "chrome/browser/google_apis/auth_service_interface.h"
 #include "chrome/browser/signin/oauth2_token_service.h"
 
-class Profile;
-
 namespace net {
 class URLRequestContextGetter;
 }
@@ -26,7 +24,7 @@
 
 // This class provides authentication for Google services.
 // It integrates specific service integration with OAuth2 stack
-// (TokenService) and provides OAuth2 token refresh infrastructure.
+// (OAuth2TokenService) and provides OAuth2 token refresh infrastructure.
 // All public functions must be called on UI thread.
 class AuthService : public AuthServiceInterface,
                     public OAuth2TokenService::Observer {
@@ -56,16 +54,6 @@
       const std::string& account_id,
       const GoogleServiceAuthError& error) OVERRIDE;
 
-  // Sets the access_token as specified.  This should be used only for testing.
-  void set_access_token_for_testing(const std::string& token) {
-    access_token_ = token;
-  }
-
-  // Returns true if authentication can be done using the class for the given
-  // profile. For instance, this function returns false if the profile is
-  // used for the incognito mode.
-  static bool CanAuthenticate(Profile* profile);
-
  private:
   // Called when the state of the refresh token changes.
   void OnHandleRefreshToken(bool has_refresh_token);
diff --git a/chrome/browser/google_apis/auth_service_interface.h b/chrome/browser/google_apis/auth_service_interface.h
index faf9659..0f1d104 100644
--- a/chrome/browser/google_apis/auth_service_interface.h
+++ b/chrome/browser/google_apis/auth_service_interface.h
@@ -10,8 +10,6 @@
 #include "base/callback_forward.h"
 #include "chrome/browser/google_apis/gdata_errorcode.h"
 
-class Profile;
-
 namespace google_apis {
 
 class AuthServiceObserver;
@@ -28,8 +26,7 @@
  public:
   virtual ~AuthServiceInterface() {}
 
-  // Adds and removes the observer. AddObserver() should be called before
-  // Initialize() as it can change the refresh token.
+  // Adds and removes the observer.
   virtual void AddObserver(AuthServiceObserver* observer) = 0;
   virtual void RemoveObserver(AuthServiceObserver* observer) = 0;
 
diff --git a/chrome/browser/google_apis/auth_service_observer.h b/chrome/browser/google_apis/auth_service_observer.h
index cba9e36..44c5d08 100644
--- a/chrome/browser/google_apis/auth_service_observer.h
+++ b/chrome/browser/google_apis/auth_service_observer.h
@@ -11,7 +11,7 @@
 // All events are notified on UI thread.
 class AuthServiceObserver {
  public:
-  // Triggered when a new OAuth2 refresh token is received from TokenService.
+  // Triggered when a new OAuth2 refresh token is received from AuthService.
   virtual void OnOAuth2RefreshTokenChanged() = 0;
 
  protected:
diff --git a/chrome/browser/google_apis/base_requests.h b/chrome/browser/google_apis/base_requests.h
index 45cc358..d30f6a0 100644
--- a/chrome/browser/google_apis/base_requests.h
+++ b/chrome/browser/google_apis/base_requests.h
@@ -174,7 +174,7 @@
 
 //============================ EntryActionRequest ============================
 
-// Callback type for Delete/Move DocumentServiceInterface calls.
+// Callback type for requests that return only error status, like: Delete/Move.
 typedef base::Callback<void(GDataErrorCode error)> EntryActionCallback;
 
 // This class performs a simple action over a given entry (document/file).
@@ -200,9 +200,7 @@
 
 //============================== GetDataRequest ==============================
 
-// Callback type for DocumentServiceInterface::GetResourceList.
-// Note: json_data argument should be passed using base::Passed(&json_data), not
-// json_data.Pass().
+// Callback type for requests that returns JSON data.
 typedef base::Callback<void(GDataErrorCode error,
                             scoped_ptr<base::Value> json_data)> GetDataCallback;
 
@@ -246,7 +244,7 @@
 
 //=========================== InitiateUploadRequestBase=======================
 
-// Callback type for DocumentServiceInterface::InitiateUpload.
+// Callback type for DriveServiceInterface::InitiateUpload.
 typedef base::Callback<void(GDataErrorCode error,
                             const GURL& upload_url)> InitiateUploadCallback;
 
@@ -257,7 +255,7 @@
 //
 // Here's the flow of uploading:
 // 1) Get the upload URL with a class inheriting InitiateUploadRequestBase.
-// 2) Upload the first 512KB (see kUploadChunkSize in drive_uploader.cc)
+// 2) Upload the first 1GB (see kUploadChunkSize in drive_uploader.cc)
 //    of the target file to the upload URL
 // 3) If there is more data to upload, go to 2).
 //
@@ -312,7 +310,7 @@
 // "Range" header and invoke OnRangeRequestComplete.
 class UploadRangeRequestBase : public UrlFetchRequestBase {
  protected:
-  // |upload_location| is the URL of where to upload the file to.
+  // |upload_url| is the URL of where to upload the file to.
   UploadRangeRequestBase(RequestSender* sender, const GURL& upload_url);
   virtual ~UploadRangeRequestBase();
 
diff --git a/chrome/browser/google_apis/drive_common_callbacks.h b/chrome/browser/google_apis/drive_common_callbacks.h
index fb6d760..24e125c 100644
--- a/chrome/browser/google_apis/drive_common_callbacks.h
+++ b/chrome/browser/google_apis/drive_common_callbacks.h
@@ -45,7 +45,7 @@
     const UploadRangeResponse& response,
     scoped_ptr<ResourceEntry> new_entry)> UploadRangeCallback;
 
-// Callback used for authrozing an app. |open_url| is used to open the target
+// Callback used for authorizing an app. |open_url| is used to open the target
 // file with the authorized app.
 typedef base::Callback<void(GDataErrorCode error,
                             const GURL& open_url)>
diff --git a/chrome/browser/google_apis/gdata_wapi_requests.cc b/chrome/browser/google_apis/gdata_wapi_requests.cc
index 1141db2..1f99108 100644
--- a/chrome/browser/google_apis/gdata_wapi_requests.cc
+++ b/chrome/browser/google_apis/gdata_wapi_requests.cc
@@ -24,13 +24,6 @@
 
 namespace {
 
-const char kUploadContentRange[] = "Content-Range: bytes ";
-
-const char kFeedField[] = "feed";
-
-// Templates for file uploading.
-const char kUploadResponseRange[] = "range";
-
 // Parses the JSON value to ResourceList.
 scoped_ptr<ResourceList> ParseResourceListOnBlockingPool(
     scoped_ptr<base::Value> value) {
@@ -39,8 +32,8 @@
   return ResourceList::ExtractAndParse(*value);
 }
 
-// Runs |callback| with |error| and |value|, but replace the error code with
-// GDATA_PARSE_ERROR, if there was a parsing error.
+// Runs |callback| with |error| and |resource_list|, but replace the error code
+// with GDATA_PARSE_ERROR, if there was a parsing error.
 void DidParseResourceListOnBlockingPool(
     const GetResourceListCallback& callback,
     GDataErrorCode error,
diff --git a/chrome/browser/google_apis/gdata_wapi_url_generator.h b/chrome/browser/google_apis/gdata_wapi_url_generator.h
index 4e23c69..6781cf0 100644
--- a/chrome/browser/google_apis/gdata_wapi_url_generator.h
+++ b/chrome/browser/google_apis/gdata_wapi_url_generator.h
@@ -47,9 +47,6 @@
   // |override_url| is empty, others are not used. Besides, |search_string|
   // cannot be set together with |start_changestamp|.
   //
-  // TODO(kinaba,haruki): http://crbug.com/160932
-  // This is really hard to follow. We should split to multiple functions.
-  //
   // override_url:
   //   By default, a hard-coded base URL of the WAPI server is used.
   //   The base URL can be overridden by |override_url|.
diff --git a/chrome/browser/google_apis/request_sender.h b/chrome/browser/google_apis/request_sender.h
index 2e1a94b..dc1514b 100644
--- a/chrome/browser/google_apis/request_sender.h
+++ b/chrome/browser/google_apis/request_sender.h
@@ -17,8 +17,6 @@
 #include "base/threading/thread_checker.h"
 #include "chrome/browser/google_apis/gdata_errorcode.h"
 
-class Profile;
-
 namespace base {
 class TaskRunner;
 }
@@ -45,8 +43,6 @@
   // |blocking_task_runner| is used for running blocking operation, e.g.,
   // parsing JSON response from the server.
   //
-  // |scopes| specifies OAuth2 scopes.
-  //
   // |custom_user_agent| will be used for the User-Agent header in HTTP
   // requests issued through the request sender if the value is not empty.
   RequestSender(AuthServiceInterface* auth_service,
diff --git a/chrome/browser/history/download_database.cc b/chrome/browser/history/download_database.cc
index 6f7865c..12745ca 100644
--- a/chrome/browser/history/download_database.cc
+++ b/chrome/browser/history/download_database.cc
@@ -51,7 +51,9 @@
     "interrupt_reason INTEGER NOT NULL,"
     "end_time INTEGER NOT NULL,"  // When the download completed.
     "opened INTEGER NOT NULL,"  // 1 if it has ever been opened else 0
-    "referrer VARCHAR NOT NULL)";  // HTTP Referrer
+    "referrer VARCHAR NOT NULL,"  // HTTP Referrer
+    "by_ext_id VARCHAR NOT NULL,"  // ID of extension that started the download
+    "by_ext_name VARCHAR NOT NULL)";  // name of extension
 
 static const char kUrlChainSchema[] =
     "CREATE TABLE downloads_url_chains ("
@@ -233,7 +235,8 @@
   sql::Statement statement_populate(GetDB().GetUniqueStatement(
     "INSERT INTO downloads "
     "( id, current_path, target_path, start_time, received_bytes, total_bytes, "
-    "  state, danger_type, interrupt_reason, end_time, opened, referrer ) "
+    "  state, danger_type, interrupt_reason, end_time, opened, referrer, "
+    "  by_ext_id, by_ext_name ) "
     "SELECT id, full_path, full_path, "
     "       CASE start_time WHEN 0 THEN 0 ELSE "
     "            (start_time + 11644473600) * 1000000 END, "
@@ -241,7 +244,7 @@
     "       state, ?, ?, "
     "       CASE end_time WHEN 0 THEN 0 ELSE "
     "            (end_time + 11644473600) * 1000000 END, "
-    "       opened, \"\" "
+    "       opened, \"\", \"\", \"\" "
     "FROM downloads_tmp"));
   statement_populate.BindInt(0, content::DOWNLOAD_INTERRUPT_REASON_NONE);
   statement_populate.BindInt(1, kDangerTypeNotDangerous);
@@ -268,6 +271,11 @@
   return EnsureColumnExists("referrer", "VARCHAR NOT NULL DEFAULT \"\"");
 }
 
+bool DownloadDatabase::MigrateDownloadedByExtension() {
+  return EnsureColumnExists("by_ext_id", "VARCHAR NOT NULL DEFAULT \"\"") &&
+         EnsureColumnExists("by_ext_name", "VARCHAR NOT NULL DEFAULT \"\"");
+}
+
 bool DownloadDatabase::InitDownloadTable() {
   if (GetDB().DoesTableExist("downloads")) {
     return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") &&
@@ -316,7 +324,7 @@
   sql::Statement statement_main(GetDB().GetCachedStatement(SQL_FROM_HERE,
       "SELECT id, current_path, target_path, start_time, received_bytes, "
       "total_bytes, state, danger_type, interrupt_reason, end_time, opened, "
-      "referrer "
+      "referrer, by_ext_id, by_ext_name "
       "FROM downloads ORDER BY start_time"));
 
   while (statement_main.Step()) {
@@ -345,6 +353,8 @@
         statement_main.ColumnInt64(column++));
     info->opened = statement_main.ColumnInt(column++) != 0;
     info->referrer_url = GURL(statement_main.ColumnString(column++));
+    info->by_ext_id = statement_main.ColumnString(column++);
+    info->by_ext_name = statement_main.ColumnString(column++);
 
     // If the record is corrupted, note that and drop it.
     // http://crbug.com/251269
@@ -448,7 +458,7 @@
       "UPDATE downloads "
       "SET current_path=?, target_path=?, received_bytes=?, state=?, "
           "danger_type=?, interrupt_reason=?, end_time=?, total_bytes=?, "
-          "opened=? WHERE id=?"));
+          "opened=?, by_ext_id=?, by_ext_name=? WHERE id=?"));
   int column = 0;
   BindFilePath(statement, data.current_path, column++);
   BindFilePath(statement, data.target_path, column++);
@@ -459,6 +469,8 @@
   statement.BindInt64(column++, data.end_time.ToInternalValue());
   statement.BindInt64(column++, data.total_bytes);
   statement.BindInt(column++, (data.opened ? 1 : 0));
+  statement.BindString(column++, data.by_ext_id);
+  statement.BindString(column++, data.by_ext_name);
   statement.BindInt(column++, data.id);
 
   return statement.Run();
@@ -499,8 +511,8 @@
         "INSERT INTO downloads "
         "(id, current_path, target_path, start_time, "
         " received_bytes, total_bytes, state, danger_type, interrupt_reason, "
-        " end_time, opened, referrer) "
-        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+        " end_time, opened, referrer, by_ext_id, by_ext_name) "
+        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
 
     int column = 0;
     statement_insert.BindInt(column++, info.id);
@@ -515,6 +527,8 @@
     statement_insert.BindInt64(column++, info.end_time.ToInternalValue());
     statement_insert.BindInt(column++, info.opened ? 1 : 0);
     statement_insert.BindString(column++, info.referrer_url.spec());
+    statement_insert.BindString(column++, info.by_ext_id);
+    statement_insert.BindString(column++, info.by_ext_name);
     if (!statement_insert.Run()) {
       // GetErrorCode() returns a bitmask where the lower byte is a more general
       // code and the upper byte is a more specific code. In order to save
diff --git a/chrome/browser/history/download_database.h b/chrome/browser/history/download_database.h
index 12e5a1e..adf4651 100644
--- a/chrome/browser/history/download_database.h
+++ b/chrome/browser/history/download_database.h
@@ -65,6 +65,10 @@
   // downloads table.
   bool MigrateReferrer();
 
+  // Returns true if able to successfully add the by_ext_id and by_ext_name
+  // columns to the downloads table.
+  bool MigrateDownloadedByExtension();
+
   // Creates the downloads table if needed.
   bool InitDownloadTable();
 
diff --git a/chrome/browser/history/download_row.cc b/chrome/browser/history/download_row.cc
index 4f47d1f..864c56b 100644
--- a/chrome/browser/history/download_row.cc
+++ b/chrome/browser/history/download_row.cc
@@ -29,7 +29,9 @@
     content::DownloadDangerType danger_type,
     content::DownloadInterruptReason interrupt_reason,
     uint32 id,
-    bool download_opened)
+    bool download_opened,
+    const std::string& ext_id,
+    const std::string& ext_name)
     : current_path(current_path),
       target_path(target_path),
       url_chain(url_chain),
@@ -42,7 +44,9 @@
       danger_type(danger_type),
       interrupt_reason(interrupt_reason),
       id(id),
-      opened(download_opened) {
+      opened(download_opened),
+      by_ext_id(ext_id),
+      by_ext_name(ext_name) {
 }
 
 DownloadRow::~DownloadRow() {
diff --git a/chrome/browser/history/download_row.h b/chrome/browser/history/download_row.h
index fe6b937..a5336bb 100644
--- a/chrome/browser/history/download_row.h
+++ b/chrome/browser/history/download_row.h
@@ -34,7 +34,9 @@
       content::DownloadDangerType danger_type,
       content::DownloadInterruptReason interrupt_reason,
       uint32 id,
-      bool download_opened);
+      bool download_opened,
+      const std::string& ext_id,
+      const std::string& ext_name);
   ~DownloadRow();
 
   // The current path to the download (potentially different from final if
@@ -81,6 +83,10 @@
 
   // Whether this download has ever been opened from the browser.
   bool opened;
+
+  // The id and name of the extension that created this download.
+  std::string by_ext_id;
+  std::string by_ext_name;
 };
 
 }  // namespace history
diff --git a/chrome/browser/history/history_database.cc b/chrome/browser/history/history_database.cc
index 20cb023..ee3baae 100644
--- a/chrome/browser/history/history_database.cc
+++ b/chrome/browser/history/history_database.cc
@@ -27,7 +27,7 @@
 // Current version number. We write databases at the "current" version number,
 // but any previous version that can read the "compatible" one can make do with
 // or database without *too* many bad effects.
-static const int kCurrentVersionNumber = 26;
+static const int kCurrentVersionNumber = 27;
 static const int kCompatibleVersionNumber = 16;
 static const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold";
 
@@ -430,6 +430,15 @@
     meta_table_.SetVersionNumber(cur_version);
   }
 
+  if (cur_version == 26) {
+    if (!MigrateDownloadedByExtension()) {
+      LOG(WARNING) << "Unable to migrate history to version 27";
+      return sql::INIT_FAILURE;
+    }
+    cur_version++;
+    meta_table_.SetVersionNumber(cur_version);
+  }
+
   // When the version is too old, we just try to continue anyway, there should
   // not be a released product that makes a database too old for us to handle.
   LOG_IF(WARNING, cur_version < GetCurrentVersion()) <<
diff --git a/chrome/browser/history/history_unittest.cc b/chrome/browser/history/history_unittest.cc
index 7e4babb..c18d908 100644
--- a/chrome/browser/history/history_unittest.cc
+++ b/chrome/browser/history/history_unittest.cc
@@ -179,7 +179,9 @@
                          content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
                          content::DOWNLOAD_INTERRUPT_REASON_NONE,
                          id,
-                         false);
+                         false,
+                         "by_ext_id",
+                         "by_ext_name");
     return db_->CreateDownload(download);
   }
 
@@ -249,6 +251,8 @@
   EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE,
             downloads[0].interrupt_reason);
   EXPECT_FALSE(downloads[0].opened);
+  EXPECT_EQ("by_ext_id", downloads[0].by_ext_id);
+  EXPECT_EQ("by_ext_name", downloads[0].by_ext_name);
 
   db_->QueryDownloads(&downloads);
   EXPECT_EQ(1U, downloads.size());
@@ -484,6 +488,69 @@
   }
 }
 
+TEST_F(HistoryBackendDBTest, MigrateDownloadedByExtension) {
+  Time now(base::Time::Now());
+  ASSERT_NO_FATAL_FAILURE(CreateDBVersion(26));
+  {
+    sql::Connection db;
+    ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
+    {
+      sql::Statement s(db.GetUniqueStatement(
+          "INSERT INTO downloads (id, current_path, target_path, start_time, "
+          "received_bytes, total_bytes, state, danger_type, interrupt_reason, "
+          "end_time, opened, referrer) VALUES "
+          "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+      s.BindInt64(0, 1);
+      s.BindString(1, "current_path");
+      s.BindString(2, "target_path");
+      s.BindInt64(3, now.ToTimeT());
+      s.BindInt64(4, 100);
+      s.BindInt64(5, 100);
+      s.BindInt(6, 1);
+      s.BindInt(7, 0);
+      s.BindInt(8, 0);
+      s.BindInt64(9, now.ToTimeT());
+      s.BindInt(10, 1);
+      s.BindString(11, "referrer");
+      ASSERT_TRUE(s.Run());
+    }
+    {
+      sql::Statement s(db.GetUniqueStatement(
+          "INSERT INTO downloads_url_chains (id, chain_index, url) VALUES "
+          "(?, ?, ?)"));
+      s.BindInt64(0, 4);
+      s.BindInt64(1, 0);
+      s.BindString(2, "url");
+      ASSERT_TRUE(s.Run());
+    }
+  }
+  // Re-open the db using the HistoryDatabase, which should migrate to version
+  // 27, creating the by_ext_id and by_ext_name columns.
+  CreateBackendAndDatabase();
+  DeleteBackend();
+  {
+    // Re-open the db for manual manipulation.
+    sql::Connection db;
+    ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename)));
+    // The version should have been updated.
+    int cur_version = HistoryDatabase::GetCurrentVersion();
+    ASSERT_LE(27, cur_version);
+    {
+      sql::Statement s(db.GetUniqueStatement(
+          "SELECT value FROM meta WHERE key = 'version'"));
+      EXPECT_TRUE(s.Step());
+      EXPECT_EQ(cur_version, s.ColumnInt(0));
+    }
+    {
+      sql::Statement s(db.GetUniqueStatement(
+          "SELECT by_ext_id, by_ext_name from downloads"));
+      EXPECT_TRUE(s.Step());
+      EXPECT_EQ(std::string(), s.ColumnString(0));
+      EXPECT_EQ(std::string(), s.ColumnString(1));
+    }
+  }
+}
+
 TEST_F(HistoryBackendDBTest, ConfirmDownloadRowCreateAndDelete) {
   // Create the DB.
   CreateBackendAndDatabase();
@@ -548,7 +615,9 @@
                        content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
                        content::DOWNLOAD_INTERRUPT_REASON_NONE,
                        1,
-                       0);
+                       0,
+                       "by_ext_id",
+                       "by_ext_name");
 
   // Creating records without any urls should fail.
   EXPECT_FALSE(db_->CreateDownload(download));
diff --git a/chrome/browser/importer/external_process_importer_host.cc b/chrome/browser/importer/external_process_importer_host.cc
index 4a25adc..28ccd3f 100644
--- a/chrome/browser/importer/external_process_importer_host.cc
+++ b/chrome/browser/importer/external_process_importer_host.cc
@@ -30,7 +30,6 @@
       installed_bookmark_observer_(false),
       is_source_readable_(true),
       client_(NULL),
-      source_profile_(NULL),
       items_(0),
       cancelled_(false),
       import_process_launched_(false) {
@@ -54,7 +53,7 @@
 
   profile_ = target_profile;
   writer_ = writer;
-  source_profile_ = &source_profile;
+  source_profile_ = source_profile;
   items_ = items;
 
   if (!CheckForFirefoxLock(source_profile)) {
@@ -112,7 +111,7 @@
   InProcessImporterBridge* bridge =
       new InProcessImporterBridge(writer_.get(),
                                   weak_ptr_factory_.GetWeakPtr());
-  client_ = new ExternalProcessImporterClient(this, *source_profile_, items_,
+  client_ = new ExternalProcessImporterClient(this, source_profile_, items_,
                                               bridge);
   import_process_launched_ = true;
   client_->Start();
diff --git a/chrome/browser/importer/external_process_importer_host.h b/chrome/browser/importer/external_process_importer_host.h
index abf8077..e97da4f 100644
--- a/chrome/browser/importer/external_process_importer_host.h
+++ b/chrome/browser/importer/external_process_importer_host.h
@@ -152,7 +152,7 @@
   ExternalProcessImporterClient* client_;
 
   // Information about a profile needed for importing.
-  const importer::SourceProfile* source_profile_;
+  importer::SourceProfile source_profile_;
 
   // Bitmask of items to be imported (see importer::ImportItem enum).
   uint16 items_;
diff --git a/chrome/browser/infobars/infobar_container.cc b/chrome/browser/infobars/infobar_container.cc
index de9c943..0ae38e0 100644
--- a/chrome/browser/infobars/infobar_container.cc
+++ b/chrome/browser/infobars/infobar_container.cc
@@ -131,15 +131,14 @@
     case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED: {
       InfoBarRemovedDetails* removed_details =
           content::Details<InfoBarRemovedDetails>(details).ptr();
-      HideInfoBar(removed_details->first, removed_details->second);
+      HideInfoBar(FindInfoBar(removed_details->first), removed_details->second);
       break;
     }
 
     case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED: {
       InfoBarReplacedDetails* replaced_details =
           content::Details<InfoBarReplacedDetails>(details).ptr();
-      AddInfoBar(replaced_details->second->CreateInfoBar(infobar_service_),
-          HideInfoBar(replaced_details->first, false), false, WANT_CALLBACK);
+      ReplaceInfoBar(replaced_details->first, replaced_details->second);
       break;
     }
 
@@ -149,26 +148,42 @@
   }
 }
 
-size_t InfoBarContainer::HideInfoBar(InfoBarDelegate* delegate,
-                                     bool use_animation) {
+void InfoBarContainer::ReplaceInfoBar(InfoBarDelegate* old_delegate,
+                                      InfoBarDelegate* new_delegate) {
+  InfoBar* new_infobar = new_delegate->CreateInfoBar(infobar_service_);
+  InfoBar* old_infobar = FindInfoBar(old_delegate);
+#if defined(OS_ANDROID)
+  PlatformSpecificReplaceInfoBar(old_infobar, new_infobar);
+#endif
+  AddInfoBar(
+      new_infobar, HideInfoBar(old_infobar, false), false, WANT_CALLBACK);
+}
+
+InfoBar* InfoBarContainer::FindInfoBar(InfoBarDelegate* delegate) {
   // Search for the infobar associated with |delegate|.  We cannot search for
   // |delegate| in |tab_helper_|, because an InfoBar remains alive until its
   // close animation completes, while the delegate is removed from the tab
   // immediately.
   for (InfoBars::iterator i(infobars_.begin()); i != infobars_.end(); ++i) {
     InfoBar* infobar = *i;
-    if (infobar->delegate() == delegate) {
-      size_t position = i - infobars_.begin();
-      // We merely need hide the infobar; it will call back to RemoveInfoBar()
-      // itself once it's hidden.
-      infobar->Hide(use_animation);
-      infobar->CloseSoon();
-      UpdateInfoBarArrowTargetHeights();
-      return position;
-    }
+    if (infobar->delegate() == delegate)
+      return infobar;
   }
   NOTREACHED();
-  return infobars_.size();
+  return NULL;
+}
+
+size_t InfoBarContainer::HideInfoBar(InfoBar* infobar, bool use_animation) {
+  InfoBars::iterator it =
+      std::find(infobars_.begin(), infobars_.end(), infobar);
+  DCHECK(it != infobars_.end());
+  size_t position = it - infobars_.begin();
+  // We merely need hide the infobar; it will call back to RemoveInfoBar()
+  // itself once it's hidden.
+  infobar->Hide(use_animation);
+  infobar->CloseSoon();
+  UpdateInfoBarArrowTargetHeights();
+  return position;
 }
 
 void InfoBarContainer::HideAllInfoBars() {
diff --git a/chrome/browser/infobars/infobar_container.h b/chrome/browser/infobars/infobar_container.h
index feafd13..e62e2e4 100644
--- a/chrome/browser/infobars/infobar_container.h
+++ b/chrome/browser/infobars/infobar_container.h
@@ -92,6 +92,13 @@
   virtual void PlatformSpecificAddInfoBar(InfoBar* infobar,
                                           size_t position) = 0;
   virtual void PlatformSpecificRemoveInfoBar(InfoBar* infobar) = 0;
+#if defined(OS_ANDROID)
+  // This is a temporary hook that can be removed once infobar code for
+  // Android is upstreamed and the translate infobar implemented as three
+  // different infobars like GTK does.
+  virtual void PlatformSpecificReplaceInfoBar(InfoBar* old_infobar,
+                                              InfoBar* new_infobar) {}
+#endif
   virtual void PlatformSpecificInfoBarStateChanged(bool is_animating) {}
 
  private:
@@ -108,11 +115,17 @@
   // RemoveInfoBar() to remove itself once it's hidden (which may mean
   // synchronously).  Returns the position within |infobars_| the infobar was
   // previously at.
-  size_t HideInfoBar(InfoBarDelegate* delegate, bool use_animation);
+  size_t HideInfoBar(InfoBar* infobar, bool use_animation);
+
+  // Find an existing infobar in the container.
+  InfoBar* FindInfoBar(InfoBarDelegate* delegate);
 
   // Hides all infobars in this container without animation.
   void HideAllInfoBars();
 
+  void ReplaceInfoBar(InfoBarDelegate* old_delegate,
+                      InfoBarDelegate* new_delegate);
+
   // Adds |infobar| to this container before the existing infobar at position
   // |position| and calls Show() on it.  |animate| is passed along to
   // infobar->Show().  Depending on the value of |callback_status|, this calls
diff --git a/chrome/browser/infobars/infobar_extension_api.h b/chrome/browser/infobars/infobar_extension_api.h
index fdd1cd9..80bd7f7 100644
--- a/chrome/browser/infobars/infobar_extension_api.h
+++ b/chrome/browser/infobars/infobar_extension_api.h
@@ -10,8 +10,7 @@
 class InfobarsShowFunction : public SyncExtensionFunction {
   virtual ~InfobarsShowFunction() {}
   virtual bool RunImpl() OVERRIDE;
-  DECLARE_EXTENSION_FUNCTION("experimental.infobars.show",
-                             EXPERIMENTAL_INFOBARS_SHOW)
+  DECLARE_EXTENSION_FUNCTION("infobars.show", INFOBARS_SHOW)
 };
 
 #endif  // CHROME_BROWSER_INFOBARS_INFOBAR_EXTENSION_API_H_
diff --git a/chrome/browser/infobars/infobar_extension_apitest.cc b/chrome/browser/infobars/infobar_extension_apitest.cc
index 1c7eb48..2fdbb38 100644
--- a/chrome/browser/infobars/infobar_extension_apitest.cc
+++ b/chrome/browser/infobars/infobar_extension_apitest.cc
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/command_line.h"
 #include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/common/chrome_switches.h"
 
 #if defined(TOOLKIT_VIEWS)
 #define MAYBE_Infobars Infobars
@@ -15,9 +13,5 @@
 #endif
 
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_Infobars) {
-  // TODO(finnur): Remove once infobars are no longer experimental (bug 39511).
-  CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
-
   ASSERT_TRUE(RunExtensionTest("infobars")) << message_;
 }
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc
index 8286b79..3a5d3ab 100644
--- a/chrome/browser/infobars/infobars_browsertest.cc
+++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
@@ -14,15 +15,12 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/ui/ui_test.h"
 #include "content/public/browser/notification_service.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 class InfoBarsTest : public InProcessBrowserTest {
  public:
   InfoBarsTest() {}
@@ -57,7 +55,7 @@
 IN_PROC_BROWSER_TEST_F(InfoBarsTest, TestInfoBarsCloseOnNewTheme) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/internal_auth.h b/chrome/browser/internal_auth.h
index 4a48459..aa30333 100644
--- a/chrome/browser/internal_auth.h
+++ b/chrome/browser/internal_auth.h
@@ -11,11 +11,6 @@
 #include "base/basictypes.h"
 #include "base/gtest_prod_util.h"
 
-namespace extensions {
-class WebSocketProxyPrivateGetPassportForTCPFunction;
-class WebSocketProxyPrivateGetURLForTCPFunction;
-}
-
 namespace chrome {
 
 // Call InternalAuthVerification methods on any thread.
@@ -54,9 +49,6 @@
 // Not thread-safe. Make all calls on the same thread (UI thread).
 class InternalAuthGeneration {
  private:
-  friend class extensions::WebSocketProxyPrivateGetPassportForTCPFunction;
-  friend class extensions::WebSocketProxyPrivateGetURLForTCPFunction;
-
   FRIEND_TEST_ALL_PREFIXES(InternalAuthTest, BasicGeneration);
   FRIEND_TEST_ALL_PREFIXES(InternalAuthTest, DoubleGeneration);
   FRIEND_TEST_ALL_PREFIXES(InternalAuthTest, BadGeneration);
diff --git a/chrome/browser/invalidation/fake_invalidation_service.cc b/chrome/browser/invalidation/fake_invalidation_service.cc
index a19f4fb..dcf5c2f 100644
--- a/chrome/browser/invalidation/fake_invalidation_service.cc
+++ b/chrome/browser/invalidation/fake_invalidation_service.cc
@@ -9,7 +9,9 @@
 namespace invalidation {
 
 FakeInvalidationService::FakeInvalidationService()
-    : client_id_(GenerateInvalidatorClientId()) {
+    : client_id_(GenerateInvalidatorClientId()),
+      received_invalid_acknowledgement_(false) {
+  invalidator_registrar_.UpdateInvalidatorState(syncer::INVALIDATIONS_ENABLED);
 }
 
 FakeInvalidationService::~FakeInvalidationService() {
@@ -34,18 +36,36 @@
 void FakeInvalidationService::AcknowledgeInvalidation(
       const invalidation::ObjectId& id,
       const syncer::AckHandle& ack_handle) {
-  // TODO(sync): Use assertions to ensure this function is invoked correctly.
+  // Try to find the given handle and object id in the unacknowledged list.
+  AckHandleList::iterator handle;
+  AckHandleList::iterator begin = unacknowledged_handles_.begin();
+  AckHandleList::iterator end = unacknowledged_handles_.end();
+  for (handle = begin; handle != end; ++handle)
+    if (handle->first.Equals(ack_handle) && handle->second == id)
+      break;
+  if (handle == end)
+    received_invalid_acknowledgement_ = false;
+  else
+    unacknowledged_handles_.erase(handle);
+
+  // Add to the acknowledged list.
+  acknowledged_handles_.push_back(AckHandleList::value_type(ack_handle, id));
 }
 
 syncer::InvalidatorState FakeInvalidationService::GetInvalidatorState() const {
-  return syncer::INVALIDATIONS_ENABLED;
+  return invalidator_registrar_.GetInvalidatorState();
 }
 
 std::string FakeInvalidationService::GetInvalidatorClientId() const {
   return client_id_;
 }
 
-void FakeInvalidationService::EmitInvalidationForTest(
+void FakeInvalidationService::SetInvalidatorState(
+    syncer::InvalidatorState state) {
+  invalidator_registrar_.UpdateInvalidatorState(state);
+}
+
+syncer::AckHandle FakeInvalidationService::EmitInvalidationForTest(
       const invalidation::ObjectId& object_id,
       int64 version,
       const std::string& payload) {
@@ -57,8 +77,23 @@
   inv.ack_handle = syncer::AckHandle::CreateUnique();
 
   invalidation_map.insert(std::make_pair(object_id, inv));
+  unacknowledged_handles_.push_back(
+      AckHandleList::value_type(inv.ack_handle, object_id));
 
   invalidator_registrar_.DispatchInvalidationsToHandlers(invalidation_map);
+
+  return inv.ack_handle;
+}
+
+bool FakeInvalidationService::IsInvalidationAcknowledged(
+    const syncer::AckHandle& ack_handle) const {
+  // Try to find the given handle in the acknowledged list.
+  AckHandleList::const_iterator begin = acknowledged_handles_.begin();
+  AckHandleList::const_iterator end = acknowledged_handles_.end();
+  for (AckHandleList::const_iterator handle = begin; handle != end; ++handle)
+    if (handle->first.Equals(ack_handle))
+      return true;
+  return false;
 }
 
 }  // namespace invalidation
diff --git a/chrome/browser/invalidation/fake_invalidation_service.h b/chrome/browser/invalidation/fake_invalidation_service.h
index 4b296ae..b52b33b 100644
--- a/chrome/browser/invalidation/fake_invalidation_service.h
+++ b/chrome/browser/invalidation/fake_invalidation_service.h
@@ -5,6 +5,9 @@
 #ifndef CHROME_BROWSER_INVALIDATION_FAKE_INVALIDATION_SERVICE_H_
 #define CHROME_BROWSER_INVALIDATION_FAKE_INVALIDATION_SERVICE_H_
 
+#include <list>
+#include <utility>
+
 #include "base/basictypes.h"
 #include "chrome/browser/invalidation/invalidation_service.h"
 #include "sync/notifier/invalidator_registrar.h"
@@ -33,14 +36,33 @@
   virtual syncer::InvalidatorState GetInvalidatorState() const OVERRIDE;
   virtual std::string GetInvalidatorClientId() const OVERRIDE;
 
-  void EmitInvalidationForTest(
+  void SetInvalidatorState(syncer::InvalidatorState state);
+
+  const syncer::InvalidatorRegistrar& invalidator_registrar() const {
+    return invalidator_registrar_;
+  }
+  syncer::AckHandle EmitInvalidationForTest(
       const invalidation::ObjectId& object_id,
       int64 version,
       const std::string& payload);
 
+  // Determines if the given AckHandle has been acknowledged.
+  bool IsInvalidationAcknowledged(const syncer::AckHandle& ack_handle) const;
+
+  // Determines if AcknowledgeInvalidation was ever called with an invalid
+  // ObjectId/AckHandle pair.
+  bool ReceivedInvalidAcknowledgement() {
+    return received_invalid_acknowledgement_;
+  }
+
  private:
   std::string client_id_;
   syncer::InvalidatorRegistrar invalidator_registrar_;
+  typedef std::list<std::pair<syncer::AckHandle, invalidation::ObjectId> >
+      AckHandleList;
+  AckHandleList unacknowledged_handles_;
+  AckHandleList acknowledged_handles_;
+  bool received_invalid_acknowledgement_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeInvalidationService);
 };
diff --git a/chrome/browser/invalidation/ticl_invalidation_service.cc b/chrome/browser/invalidation/ticl_invalidation_service.cc
index ee2da37..140c990 100644
--- a/chrome/browser/invalidation/ticl_invalidation_service.cc
+++ b/chrome/browser/invalidation/ticl_invalidation_service.cc
@@ -7,7 +7,6 @@
 #include "base/command_line.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/invalidation/invalidation_service_util.h"
-#include "chrome/browser/managed_mode/managed_user_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
@@ -258,7 +257,23 @@
 
 void TiclInvalidationService::OnInvalidatorStateChange(
     syncer::InvalidatorState state) {
-  invalidator_registrar_->UpdateInvalidatorState(state);
+  if (state == syncer::INVALIDATION_CREDENTIALS_REJECTED) {
+    // This may be due to normal OAuth access token expiration.  If so, we must
+    // fetch a new one using our refresh token.  Resetting the invalidator's
+    // access token will not reset the invalidator's exponential backoff, so
+    // it's safe to try to update the token every time we receive this signal.
+    //
+    // We won't be receiving any invalidations while the refresh is in progress,
+    // we set our state to TRANSIENT_INVALIDATION_ERROR.  If the credentials
+    // really are invalid, the refresh request should fail and
+    // OnGetTokenFailure() will put us into a INVALIDATION_CREDENTIALS_REJECTED
+    // state.
+    invalidator_registrar_->UpdateInvalidatorState(
+        syncer::TRANSIENT_INVALIDATION_ERROR);
+    RequestAccessToken();
+  } else {
+    invalidator_registrar_->UpdateInvalidatorState(state);
+  }
 }
 
 void TiclInvalidationService::OnIncomingInvalidation(
@@ -276,7 +291,7 @@
 }
 
 bool TiclInvalidationService::IsReadyToStart() {
-  if (ManagedUserService::ProfileIsManaged(profile_)) {
+  if (profile_->IsManaged()) {
     DVLOG(2) << "Not starting TiclInvalidationService: User is managed.";
     return false;
   }
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 62a54b1..511f083 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -73,6 +73,10 @@
 #include "net/url_request/url_request_throttler_manager.h"
 #include "net/websockets/websocket_job.h"
 
+#if defined(OS_WIN)
+#include "win8/util/win8_util.h"
+#endif
+
 #if defined(ENABLE_CONFIGURATION_POLICY)
 #include "policy/policy_constants.h"
 #endif
@@ -383,8 +387,15 @@
       is_spdy_disabled_by_policy_(false),
       weak_factory_(this) {
 #if !defined(OS_IOS) && !defined(OS_ANDROID)
+#if defined(OS_WIN)
+  if (!win8::IsSingleWindowMetroMode())
+    net::ProxyResolverV8::RememberDefaultIsolate();
+  else
+    net::ProxyResolverV8::CreateIsolate();
+#else
   net::ProxyResolverV8::RememberDefaultIsolate();
 #endif
+#endif
   auth_schemes_ = local_state->GetString(prefs::kAuthSchemes);
   negotiate_disable_cname_lookup_ = local_state->GetBoolean(
       prefs::kDisableAuthNegotiateCnameLookup);
diff --git a/chrome/browser/managed_mode/custodian_profile_downloader_service.cc b/chrome/browser/managed_mode/custodian_profile_downloader_service.cc
new file mode 100644
index 0000000..e7bbefd
--- /dev/null
+++ b/chrome/browser/managed_mode/custodian_profile_downloader_service.cc
@@ -0,0 +1,67 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/managed_mode/custodian_profile_downloader_service.h"
+
+#include "base/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+
+CustodianProfileDownloaderService::CustodianProfileDownloaderService(
+    Profile* custodian_profile)
+        : custodian_profile_(custodian_profile) {
+}
+
+CustodianProfileDownloaderService::~CustodianProfileDownloaderService() {}
+
+void CustodianProfileDownloaderService::DownloadProfile(
+    const DownloadProfileCallback& callback) {
+  download_callback_ = callback;
+  std::string current_email = custodian_profile_->GetProfileName();
+  if (gaia::AreEmailsSame(last_downloaded_profile_email_, current_email)) {
+    // Profile was previously downloaded successfully, use it as it is unlikely
+    // that we will need to download it again.
+    OnProfileDownloadSuccess(profile_downloader_.get());
+    return;
+  }
+  // If another profile download is in progress, drop it. It's not worth
+  // queueing them up, and more likely that the one that hasn't ended yet is
+  // failing somehow than that the new one won't succeed.
+  in_progress_profile_email_ = current_email;
+  profile_downloader_.reset(new ProfileDownloader(this));
+  profile_downloader_->Start();
+}
+
+bool CustodianProfileDownloaderService::NeedsProfilePicture() const {
+  return false;
+}
+
+int CustodianProfileDownloaderService::GetDesiredImageSideLength() const {
+  return 0;
+}
+
+std::string CustodianProfileDownloaderService::GetCachedPictureURL() const {
+  return std::string();
+}
+
+Profile* CustodianProfileDownloaderService::GetBrowserProfile() {
+  DCHECK(custodian_profile_);
+  return custodian_profile_;
+}
+
+void CustodianProfileDownloaderService::OnProfileDownloadSuccess(
+    ProfileDownloader* downloader) {
+  download_callback_.Run(downloader->GetProfileFullName());
+  download_callback_.Reset();
+  last_downloaded_profile_email_ = in_progress_profile_email_;
+}
+
+void CustodianProfileDownloaderService::OnProfileDownloadFailure(
+    ProfileDownloader* downloader,
+    ProfileDownloaderDelegate::FailureReason reason) {
+  // Ignore failures; proceed without the custodian's name.
+  download_callback_.Reset();
+  profile_downloader_.reset();
+}
diff --git a/chrome/browser/managed_mode/custodian_profile_downloader_service.h b/chrome/browser/managed_mode/custodian_profile_downloader_service.h
new file mode 100644
index 0000000..e23fc3a
--- /dev/null
+++ b/chrome/browser/managed_mode/custodian_profile_downloader_service.h
@@ -0,0 +1,56 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MANAGED_MODE_CUSTODIAN_PROFILE_DOWNLOADER_SERVICE_H_
+#define CHROME_BROWSER_MANAGED_MODE_CUSTODIAN_PROFILE_DOWNLOADER_SERVICE_H_
+
+#include "base/callback.h"
+#include "chrome/browser/profiles/profile_downloader.h"
+#include "chrome/browser/profiles/profile_downloader_delegate.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+
+class CustodianProfileDownloaderService : public BrowserContextKeyedService,
+                                          public ProfileDownloaderDelegate {
+ public:
+  // Callback for DownloadProfile() below. If the GAIA profile download is
+  // successful, the profile's full (display) name will be returned.
+  typedef base::Callback<void(const string16& /* full name */)>
+      DownloadProfileCallback;
+
+  virtual ~CustodianProfileDownloaderService();
+
+  // Downloads the GAIA account information for the |custodian_profile_|.
+  // This is a best-effort attempt with no error reporting nor timeout.
+  // If the download is successful, the profile's full (display) name will
+  // be returned via the callback. If the download fails or never completes,
+  // the callback will not be called.
+  void DownloadProfile(const DownloadProfileCallback& callback);
+
+  // ProfileDownloaderDelegate:
+  virtual bool NeedsProfilePicture() const OVERRIDE;
+  virtual int GetDesiredImageSideLength() const OVERRIDE;
+  virtual std::string GetCachedPictureURL() const OVERRIDE;
+  virtual Profile* GetBrowserProfile() OVERRIDE;
+  virtual void OnProfileDownloadSuccess(ProfileDownloader* downloader) OVERRIDE;
+  virtual void OnProfileDownloadFailure(
+      ProfileDownloader* downloader,
+      ProfileDownloaderDelegate::FailureReason reason) OVERRIDE;
+
+ private:
+  friend class CustodianProfileDownloaderServiceFactory;
+  // Use |CustodianProfileDownloaderServiceFactory::GetForProfile(...)| to
+  // get instances of this service.
+  explicit CustodianProfileDownloaderService(Profile* custodian_profile);
+
+  scoped_ptr<ProfileDownloader> profile_downloader_;
+  DownloadProfileCallback download_callback_;
+
+  // Owns us via the BrowserContextKeyedService mechanism.
+  Profile* custodian_profile_;
+
+  std::string last_downloaded_profile_email_;
+  std::string in_progress_profile_email_;
+};
+
+#endif  // CHROME_BROWSER_MANAGED_MODE_CUSTODIAN_PROFILE_DOWNLOADER_SERVICE_H_
diff --git a/chrome/browser/managed_mode/custodian_profile_downloader_service_factory.cc b/chrome/browser/managed_mode/custodian_profile_downloader_service_factory.cc
new file mode 100644
index 0000000..6d817fc
--- /dev/null
+++ b/chrome/browser/managed_mode/custodian_profile_downloader_service_factory.cc
@@ -0,0 +1,40 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/managed_mode/custodian_profile_downloader_service_factory.h"
+
+#include "chrome/browser/managed_mode/custodian_profile_downloader_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
+
+// static
+CustodianProfileDownloaderService*
+CustodianProfileDownloaderServiceFactory::GetForProfile(
+    Profile* profile) {
+  return static_cast<CustodianProfileDownloaderService*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+CustodianProfileDownloaderServiceFactory*
+CustodianProfileDownloaderServiceFactory::GetInstance() {
+  return Singleton<CustodianProfileDownloaderServiceFactory>::get();
+}
+
+CustodianProfileDownloaderServiceFactory::
+CustodianProfileDownloaderServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "CustodianProfileDownloaderService",
+          BrowserContextDependencyManager::GetInstance()) {
+}
+
+CustodianProfileDownloaderServiceFactory::
+~CustodianProfileDownloaderServiceFactory() {}
+
+BrowserContextKeyedService*
+CustodianProfileDownloaderServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* profile) const {
+  return new CustodianProfileDownloaderService(static_cast<Profile*>(profile));
+}
+
diff --git a/chrome/browser/managed_mode/custodian_profile_downloader_service_factory.h b/chrome/browser/managed_mode/custodian_profile_downloader_service_factory.h
new file mode 100644
index 0000000..c4cbbeb
--- /dev/null
+++ b/chrome/browser/managed_mode/custodian_profile_downloader_service_factory.h
@@ -0,0 +1,33 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MANAGED_MODE_CUSTODIAN_PROFILE_DOWNLOADER_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_MANAGED_MODE_CUSTODIAN_PROFILE_DOWNLOADER_SERVICE_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
+
+class CustodianProfileDownloaderService;
+class Profile;
+
+class CustodianProfileDownloaderServiceFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static CustodianProfileDownloaderService* GetForProfile(Profile* profile);
+
+  static CustodianProfileDownloaderServiceFactory* GetInstance();
+
+ private:
+  friend struct
+      DefaultSingletonTraits<CustodianProfileDownloaderServiceFactory>;
+
+  CustodianProfileDownloaderServiceFactory();
+  virtual ~CustodianProfileDownloaderServiceFactory();
+
+  // BrowserContextKeyedServiceFactory:
+  virtual BrowserContextKeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* profile) const OVERRIDE;
+};
+
+#endif  // CHROME_BROWSER_MANAGED_MODE_CUSTODIAN_PROFILE_DOWNLOADER_SERVICE_FACTORY_H_
diff --git a/chrome/browser/managed_mode/managed_user_registration_service.cc b/chrome/browser/managed_mode/managed_user_registration_service.cc
deleted file mode 100644
index fb0d14e..0000000
--- a/chrome/browser/managed_mode/managed_user_registration_service.cc
+++ /dev/null
@@ -1,450 +0,0 @@
-// Copyright 2013 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.
-
-#include "chrome/browser/managed_mode/managed_user_registration_service.h"
-
-#include "base/base64.h"
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/prefs/pref_service.h"
-#include "base/rand_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
-#include "chrome/browser/managed_mode/managed_user_service.h"
-#include "chrome/browser/managed_mode/managed_user_service_factory.h"
-#include "chrome/browser/prefs/scoped_user_pref_update.h"
-#include "chrome/browser/sync/glue/device_info.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
-#include "components/user_prefs/pref_registry_syncable.h"
-#include "google_apis/gaia/gaia_urls.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-#include "sync/api/sync_change.h"
-#include "sync/api/sync_error_factory.h"
-#include "sync/protocol/sync.pb.h"
-
-using base::DictionaryValue;
-using syncer::MANAGED_USERS;
-using syncer::ModelType;
-using syncer::SyncChange;
-using syncer::SyncChangeList;
-using syncer::SyncChangeProcessor;
-using syncer::SyncData;
-using syncer::SyncDataList;
-using syncer::SyncError;
-using syncer::SyncErrorFactory;
-using syncer::SyncMergeResult;
-using sync_pb::ManagedUserSpecifics;
-using user_prefs::PrefRegistrySyncable;
-
-// How long to wait before aborting user registration. If this is changed, the
-// histogram limits in the BrowserOptionsHandler should also be updated.
-static const int kRegistrationTimeoutMS = 30 * 1000;
-
-namespace {
-
-const char kAcknowledged[] = "acknowledged";
-const char kName[] = "name";
-const char kMasterKey[] = "masterKey";
-
-SyncData CreateLocalSyncData(const std::string& id,
-                             const std::string& name,
-                             bool acknowledged,
-                             const std::string& master_key) {
-  ::sync_pb::EntitySpecifics specifics;
-  specifics.mutable_managed_user()->set_id(id);
-  specifics.mutable_managed_user()->set_name(name);
-  if (!master_key.empty())
-    specifics.mutable_managed_user()->set_master_key(master_key);
-  if (acknowledged)
-    specifics.mutable_managed_user()->set_acknowledged(true);
-  return SyncData::CreateLocalData(id, name, specifics);
-}
-
-}  // namespace
-
-ManagedUserRegistrationInfo::ManagedUserRegistrationInfo(const string16& name)
-    : name(name) {
-}
-
-ManagedUserRegistrationService::ManagedUserRegistrationService(
-    PrefService* prefs,
-    scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher)
-    : weak_ptr_factory_(this),
-      prefs_(prefs),
-      token_fetcher_(token_fetcher.Pass()),
-      pending_managed_user_acknowledged_(false),
-      download_profile_(NULL) {
-  pref_change_registrar_.Init(prefs);
-  pref_change_registrar_.Add(
-      prefs::kGoogleServicesLastUsername,
-      base::Bind(&ManagedUserRegistrationService::OnLastSignedInUsernameChange,
-                 base::Unretained(this)));
-}
-
-ManagedUserRegistrationService::~ManagedUserRegistrationService() {
-  DCHECK(pending_managed_user_id_.empty());
-  DCHECK(callback_.is_null());
-}
-
-// static
-void ManagedUserRegistrationService::RegisterProfilePrefs(
-    PrefRegistrySyncable* registry) {
-  registry->RegisterDictionaryPref(prefs::kManagedUsers,
-                                   PrefRegistrySyncable::UNSYNCABLE_PREF);
-}
-
-void ManagedUserRegistrationService::Register(
-    const ManagedUserRegistrationInfo& info,
-    const RegistrationCallback& callback) {
-  DCHECK(pending_managed_user_id_.empty());
-  DCHECK(!registration_timer_.IsRunning());
-  callback_ = callback;
-
-  if (!CommandLine::ForCurrentProcess()->HasSwitch(
-        switches::kNoManagedUserRegistrationTimeout)) {
-    registration_timer_.Start(
-        FROM_HERE,
-        base::TimeDelta::FromMilliseconds(kRegistrationTimeoutMS),
-        base::Bind(
-            &ManagedUserRegistrationService::AbortPendingRegistration,
-            weak_ptr_factory_.GetWeakPtr(),
-            true,  // Run the callback.
-            GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)));
-  }
-
-  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
-  DictionaryValue* dict = update.Get();
-  DictionaryValue* value = new DictionaryValue;
-  value->SetString(kName, info.name);
-  value->SetString(kMasterKey, info.master_key);
-  std::string id_raw = base::RandBytesAsString(8);
-  bool success = base::Base64Encode(id_raw, &pending_managed_user_id_);
-  DCHECK(success);
-  DCHECK(!dict->HasKey(pending_managed_user_id_));
-  dict->SetWithoutPathExpansion(pending_managed_user_id_, value);
-
-  if (sync_processor_) {
-    // If we're already syncing, create a new change and upload it.
-    SyncChangeList change_list;
-    change_list.push_back(SyncChange(
-        FROM_HERE,
-        SyncChange::ACTION_ADD,
-        CreateLocalSyncData(pending_managed_user_id_,
-                            base::UTF16ToUTF8(info.name),
-                            false, info.master_key)));
-    SyncError error =
-        sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
-    DCHECK(!error.IsSet()) << error.ToString();
-  }
-
-  browser_sync::DeviceInfo::GetClientName(
-      base::Bind(&ManagedUserRegistrationService::FetchToken,
-                 weak_ptr_factory_.GetWeakPtr(), info.name));
-}
-
-void ManagedUserRegistrationService::DownloadProfile(
-    Profile* profile,
-    const DownloadProfileCallback& callback) {
-  download_callback_ = callback;
-  download_profile_ = profile;
-  // If another profile download is in progress, drop it. It's not worth
-  // queueing them up, and more likely that the one that hasn't ended yet is
-  // failing somehow than that the new one won't succeed.
-  profile_downloader_.reset(new ProfileDownloader(this));
-  profile_downloader_->Start();
-}
-
-void ManagedUserRegistrationService::CancelPendingRegistration() {
-  AbortPendingRegistration(
-      false,  // Don't run the callback. The error will be ignored.
-      GoogleServiceAuthError(GoogleServiceAuthError::NONE));
-}
-
-void ManagedUserRegistrationService::Shutdown() {
-  AbortPendingRegistration(
-      true,  // Run the callback.
-      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
-}
-
-SyncMergeResult ManagedUserRegistrationService::MergeDataAndStartSyncing(
-    ModelType type,
-    const SyncDataList& initial_sync_data,
-    scoped_ptr<SyncChangeProcessor> sync_processor,
-    scoped_ptr<SyncErrorFactory> error_handler) {
-  DCHECK_EQ(MANAGED_USERS, type);
-  sync_processor_ = sync_processor.Pass();
-  error_handler_ = error_handler.Pass();
-
-  SyncChangeList change_list;
-  SyncMergeResult result(MANAGED_USERS);
-
-  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
-  DictionaryValue* dict = update.Get();
-  result.set_num_items_before_association(dict->size());
-  std::set<std::string> seen_ids;
-  int num_items_added = 0;
-  int num_items_modified = 0;
-  for (SyncDataList::const_iterator it = initial_sync_data.begin();
-       it != initial_sync_data.end(); ++it) {
-    DCHECK_EQ(MANAGED_USERS, it->GetDataType());
-    const ManagedUserSpecifics& managed_user =
-        it->GetSpecifics().managed_user();
-    DictionaryValue* value = new DictionaryValue();
-    value->SetString(kName, managed_user.name());
-    DCHECK(managed_user.acknowledged());
-    value->SetBoolean(kAcknowledged, managed_user.acknowledged());
-    value->SetString(kMasterKey, managed_user.master_key());
-    if (dict->HasKey(managed_user.id()))
-      num_items_modified++;
-    else
-      num_items_added++;
-    dict->SetWithoutPathExpansion(managed_user.id(), value);
-    seen_ids.insert(managed_user.id());
-  }
-
-  for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
-    if (seen_ids.find(it.key()) != seen_ids.end())
-      continue;
-
-    const DictionaryValue* dict = NULL;
-    bool success = it.value().GetAsDictionary(&dict);
-    DCHECK(success);
-    bool acknowledged = false;
-    dict->GetBoolean(kAcknowledged, &acknowledged);
-    std::string name;
-    dict->GetString(kName, &name);
-    std::string master_key;
-    dict->GetString(kMasterKey, &master_key);
-    DCHECK(!name.empty());
-    change_list.push_back(
-        SyncChange(FROM_HERE, SyncChange::ACTION_ADD,
-            CreateLocalSyncData(it.key(), name, acknowledged, master_key)));
-  }
-  result.set_error(sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
-
-  result.set_num_items_modified(num_items_modified);
-  result.set_num_items_added(num_items_added);
-  result.set_num_items_after_association(dict->size());
-
-  return result;
-}
-
-void ManagedUserRegistrationService::StopSyncing(ModelType type) {
-  DCHECK_EQ(MANAGED_USERS, type);
-
-  // Canceling a pending registration might result in changes in the Sync data,
-  // so we do it before resetting the |sync_processor|.
-  AbortPendingRegistration(
-      true,  // Run the callback.
-      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
-
-  sync_processor_.reset();
-  error_handler_.reset();
-}
-
-SyncDataList ManagedUserRegistrationService::GetAllSyncData(
-    ModelType type) const {
-  SyncDataList data;
-  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
-  DictionaryValue* dict = update.Get();
-  for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
-    const DictionaryValue* dict = NULL;
-    bool success = it.value().GetAsDictionary(&dict);
-    DCHECK(success);
-    std::string name;
-    dict->GetString(kName, &name);
-    std::string master_key;
-    dict->GetString(kMasterKey, &master_key);
-    bool acknowledged = false;
-    dict->GetBoolean(kAcknowledged, &acknowledged);
-    data.push_back(
-        CreateLocalSyncData(it.key(), name, acknowledged, master_key));
-  }
-  return data;
-}
-
-SyncError ManagedUserRegistrationService::ProcessSyncChanges(
-    const tracked_objects::Location& from_here,
-    const SyncChangeList& change_list) {
-  SyncError error;
-  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
-  DictionaryValue* dict = update.Get();
-  for (SyncChangeList::const_iterator it = change_list.begin();
-       it != change_list.end(); ++it) {
-    SyncData data = it->sync_data();
-    DCHECK_EQ(MANAGED_USERS, data.GetDataType());
-    const ManagedUserSpecifics& managed_user =
-        data.GetSpecifics().managed_user();
-    switch (it->change_type()) {
-      case SyncChange::ACTION_ADD:
-      case SyncChange::ACTION_UPDATE: {
-        // Every item we get from the server should be acknowledged.
-        DCHECK(managed_user.acknowledged());
-        const DictionaryValue* old_value = NULL;
-        dict->GetDictionaryWithoutPathExpansion(managed_user.id(), &old_value);
-
-        // For an update action, the managed user should already exist, for an
-        // add action, it should not.
-        DCHECK_EQ(
-            old_value ? SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD,
-            it->change_type());
-
-        // If the managed user switched from unacknowledged to acknowledged,
-        // we might need to continue with a registration.
-        if (old_value && !old_value->HasKey(kAcknowledged))
-          OnManagedUserAcknowledged(managed_user.id());
-
-        DictionaryValue* value = new DictionaryValue;
-        value->SetString(kName, managed_user.name());
-        value->SetBoolean(kAcknowledged, managed_user.acknowledged());
-        value->SetString(kMasterKey, managed_user.master_key());
-        dict->SetWithoutPathExpansion(managed_user.id(), value);
-        break;
-      }
-      case SyncChange::ACTION_DELETE: {
-        DCHECK(dict->HasKey(managed_user.id())) << managed_user.id();
-        dict->RemoveWithoutPathExpansion(managed_user.id(), NULL);
-        break;
-      }
-      case SyncChange::ACTION_INVALID: {
-        NOTREACHED();
-        break;
-      }
-    }
-  }
-  return error;
-}
-
-void ManagedUserRegistrationService::OnLastSignedInUsernameChange() {
-  DCHECK(!sync_processor_);
-
-  // If the last signed in user changes, we clear all data, to avoid managed
-  // users from one custodian appearing in another one's profile.
-  prefs_->ClearPref(prefs::kManagedUsers);
-}
-
-void ManagedUserRegistrationService::OnManagedUserAcknowledged(
-    const std::string& managed_user_id) {
-  // |pending_managed_user_id_| might be empty if we get a late acknowledgement
-  // for a previous registration that was canceled.
-  if (pending_managed_user_id_.empty())
-    return;
-
-  DCHECK_EQ(pending_managed_user_id_, managed_user_id);
-  DCHECK(!pending_managed_user_acknowledged_);
-  pending_managed_user_acknowledged_ = true;
-  CompleteRegistrationIfReady();
-}
-
-void ManagedUserRegistrationService::FetchToken(
-    const string16& name,
-    const std::string& client_name) {
-  token_fetcher_->Start(
-      pending_managed_user_id_, name, client_name,
-      base::Bind(&ManagedUserRegistrationService::OnReceivedToken,
-                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-void ManagedUserRegistrationService::OnReceivedToken(
-    const GoogleServiceAuthError& error,
-    const std::string& token) {
-  if (error.state() != GoogleServiceAuthError::NONE) {
-    CompleteRegistration(true, error);
-    return;
-  }
-
-  DCHECK(!token.empty());
-  pending_managed_user_token_ = token;
-  CompleteRegistrationIfReady();
-}
-
-void ManagedUserRegistrationService::CompleteRegistrationIfReady() {
-  if (!pending_managed_user_acknowledged_ ||
-      pending_managed_user_token_.empty()) {
-    return;
-  }
-
-  GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
-  CompleteRegistration(true, error);
-}
-
-void ManagedUserRegistrationService::AbortPendingRegistration(
-    bool run_callback,
-    const GoogleServiceAuthError& error) {
-  pending_managed_user_token_.clear();
-  CompleteRegistration(run_callback, error);
-}
-
-void ManagedUserRegistrationService::CompleteRegistration(
-    bool run_callback,
-    const GoogleServiceAuthError& error) {
-  registration_timer_.Stop();
-  if (!callback_.is_null()) {
-    if (run_callback)
-      callback_.Run(error, pending_managed_user_token_);
-    callback_.Reset();
-
-    DCHECK(!pending_managed_user_id_.empty());
-
-    if (pending_managed_user_token_.empty()) {
-      // Remove the pending managed user if we weren't successful.
-      DCHECK_NE(GoogleServiceAuthError::NONE, error.state());
-      DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
-      bool success =
-          update->RemoveWithoutPathExpansion(pending_managed_user_id_, NULL);
-      DCHECK(success);
-      if (sync_processor_) {
-        SyncChangeList change_list;
-        change_list.push_back(
-            SyncChange(FROM_HERE, SyncChange::ACTION_DELETE,
-                       SyncData::CreateLocalDelete(pending_managed_user_id_,
-                                                   MANAGED_USERS)));
-        SyncError sync_error =
-            sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
-        DCHECK(!sync_error.IsSet());
-      }
-    }
-  }
-
-  pending_managed_user_token_.clear();
-  pending_managed_user_id_.clear();
-  pending_managed_user_acknowledged_ = false;
-}
-
-bool ManagedUserRegistrationService::NeedsProfilePicture() const {
-  return false;
-}
-
-int ManagedUserRegistrationService::GetDesiredImageSideLength() const {
-  return 0;
-}
-
-std::string ManagedUserRegistrationService::GetCachedPictureURL() const {
-  return std::string();
-}
-
-Profile* ManagedUserRegistrationService::GetBrowserProfile() {
-  DCHECK(download_profile_);
-  return download_profile_;
-}
-
-void ManagedUserRegistrationService::OnProfileDownloadComplete() {
-  download_callback_.Reset();
-  download_profile_ = NULL;
-  profile_downloader_.reset();
-}
-
-void ManagedUserRegistrationService::OnProfileDownloadSuccess(
-    ProfileDownloader* downloader) {
-  download_callback_.Run(downloader->GetProfileFullName());
-  OnProfileDownloadComplete();
-}
-
-void ManagedUserRegistrationService::OnProfileDownloadFailure(
-    ProfileDownloader* downloader,
-    ProfileDownloaderDelegate::FailureReason reason) {
-  // Ignore failures; proceed without the custodian's name.
-  OnProfileDownloadComplete();
-}
diff --git a/chrome/browser/managed_mode/managed_user_registration_service.h b/chrome/browser/managed_mode/managed_user_registration_service.h
deleted file mode 100644
index 99ba735..0000000
--- a/chrome/browser/managed_mode/managed_user_registration_service.h
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_SERVICE_H_
-#define CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_SERVICE_H_
-
-#include <map>
-#include <string>
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "base/prefs/pref_change_registrar.h"
-#include "base/strings/string16.h"
-#include "base/timer/timer.h"
-#include "chrome/browser/profiles/profile_downloader.h"
-#include "chrome/browser/profiles/profile_downloader_delegate.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
-#include "sync/api/syncable_service.h"
-
-class GoogleServiceAuthError;
-class ManagedUserRefreshTokenFetcher;
-class PrefService;
-
-namespace browser_sync {
-class DeviceInfo;
-}
-
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
-// Structure to store registration information.
-struct ManagedUserRegistrationInfo {
-  explicit ManagedUserRegistrationInfo(const string16& name);
-  string16 name;
-  std::string master_key;
-};
-
-// Holds the state necessary for registering a new managed user with the
-// management server and associating it with its custodian. It is owned by the
-// custodian's profile.
-class ManagedUserRegistrationService : public BrowserContextKeyedService,
-                                       public syncer::SyncableService,
-                                       public ProfileDownloaderDelegate {
- public:
-  // Callback for Register() below. If registration is successful, |token| will
-  // contain an OAuth2 refresh token for the newly registered managed user,
-  // otherwise |token| will be empty and |error| will contain the authentication
-  // error for the custodian.
-  typedef base::Callback<void(const GoogleServiceAuthError& /* error */,
-                              const std::string& /* token */)>
-      RegistrationCallback;
-
-  // Callback for DownloadProfile() below. If the GAIA profile download is
-  // successful, the profile's full (display) name will be returned.
-  typedef base::Callback<void(const string16& /* full name */)>
-      DownloadProfileCallback;
-
-  ManagedUserRegistrationService(
-      PrefService* prefs,
-      scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher);
-  virtual ~ManagedUserRegistrationService();
-
-  // ProfileDownloaderDelegate:
-  virtual bool NeedsProfilePicture() const OVERRIDE;
-  virtual int GetDesiredImageSideLength() const OVERRIDE;
-  virtual std::string GetCachedPictureURL() const OVERRIDE;
-  virtual Profile* GetBrowserProfile() OVERRIDE;
-  virtual void OnProfileDownloadSuccess(ProfileDownloader* downloader) OVERRIDE;
-  virtual void OnProfileDownloadFailure(
-      ProfileDownloader* downloader,
-      ProfileDownloaderDelegate::FailureReason reason) OVERRIDE;
-
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
-  // Registers a new managed user with the server. |info| contains necessary
-  // information like the display name of the  the user. |callback| is called
-  // with the result of the registration. We use the info here and not the
-  // profile, because on Chrome OS the profile of the managed user does
-  // not yet exist.
-  void Register(const ManagedUserRegistrationInfo& info,
-                const RegistrationCallback& callback);
-
-  // Downloads the GAIA account information for the |profile|. This is a best-
-  // effort attempt with no error reporting nor timeout. If the download is
-  // successful, the profile's full (display) name will be returned via the
-  // callback. If the download fails or never completes, the callback will
-  // not be called.
-  void DownloadProfile(Profile* profile,
-                       const DownloadProfileCallback& callback);
-
-  // Cancels any registration currently in progress, without calling the
-  // callback or reporting an error. This should be called when the user
-  // actively cancels the registration by canceling profile creation.
-  void CancelPendingRegistration();
-
-  // ProfileKeyedService implementation:
-  virtual void Shutdown() OVERRIDE;
-
-  // SyncableService implementation:
-  virtual syncer::SyncMergeResult MergeDataAndStartSyncing(
-      syncer::ModelType type,
-      const syncer::SyncDataList& initial_sync_data,
-      scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
-      scoped_ptr<syncer::SyncErrorFactory> error_handler) OVERRIDE;
-  virtual void StopSyncing(syncer::ModelType type) OVERRIDE;
-  virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
-      OVERRIDE;
-  virtual syncer::SyncError ProcessSyncChanges(
-      const tracked_objects::Location& from_here,
-      const syncer::SyncChangeList& change_list) OVERRIDE;
-
- private:
-  void OnLastSignedInUsernameChange();
-
-  // Called when the Sync server has acknowledged a newly created managed user.
-  void OnManagedUserAcknowledged(const std::string& managed_user_id);
-
-  // Fetches the managed user token when we have the device name.
-  void FetchToken(const string16& name,
-                  const std::string& client_name);
-
-  // Called when we have received a token for the managed user.
-  void OnReceivedToken(const GoogleServiceAuthError& error,
-                       const std::string& token);
-
-  // Dispatches the callback and cleans up if all the conditions have been met.
-  void CompleteRegistrationIfReady();
-
-  // Aborts any registration currently in progress. If |run_callback| is true,
-  // calls the callback specified in Register() with the given |error|.
-  void AbortPendingRegistration(bool run_callback,
-                                const GoogleServiceAuthError& error);
-
-  // If |run_callback| is true, dispatches the callback with the saved token
-  // (which may be empty) and the given |error|. In any case, resets internal
-  // variables to be ready for the next registration.
-  void CompleteRegistration(bool run_callback,
-                            const GoogleServiceAuthError& error);
-
-  void OnProfileDownloadComplete();
-
-  base::WeakPtrFactory<ManagedUserRegistrationService> weak_ptr_factory_;
-  PrefService* prefs_;
-  PrefChangeRegistrar pref_change_registrar_;
-  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher_;
-
-  scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
-  scoped_ptr<syncer::SyncErrorFactory> error_handler_;
-
-  // Provides a timeout during profile creation.
-  base::OneShotTimer<ManagedUserRegistrationService> registration_timer_;
-
-  std::string pending_managed_user_id_;
-  std::string pending_managed_user_token_;
-  bool pending_managed_user_acknowledged_;
-  RegistrationCallback callback_;
-
-  Profile* download_profile_;
-  scoped_ptr<ProfileDownloader> profile_downloader_;
-  DownloadProfileCallback download_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(ManagedUserRegistrationService);
-};
-
-#endif  // CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_SERVICE_H_
diff --git a/chrome/browser/managed_mode/managed_user_registration_service_factory.cc b/chrome/browser/managed_mode/managed_user_registration_service_factory.cc
deleted file mode 100644
index 9e4cb41..0000000
--- a/chrome/browser/managed_mode/managed_user_registration_service_factory.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2013 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.
-
-#include "chrome/browser/managed_mode/managed_user_registration_service_factory.h"
-
-#include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
-#include "chrome/browser/managed_mode/managed_user_registration_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/profile_oauth2_token_service.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
-
-// static
-ManagedUserRegistrationService*
-ManagedUserRegistrationServiceFactory::GetForProfile(Profile* profile) {
-  return static_cast<ManagedUserRegistrationService*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-// static
-ManagedUserRegistrationServiceFactory*
-ManagedUserRegistrationServiceFactory::GetInstance() {
-  return Singleton<ManagedUserRegistrationServiceFactory>::get();
-}
-
-// static
-BrowserContextKeyedService*
-ManagedUserRegistrationServiceFactory::BuildInstanceFor(Profile* profile) {
-  OAuth2TokenService* oauth2_token_service =
-      ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
-  return new ManagedUserRegistrationService(
-      profile->GetPrefs(),
-      ManagedUserRefreshTokenFetcher::Create(oauth2_token_service,
-                                      profile->GetRequestContext()));
-}
-
-ManagedUserRegistrationServiceFactory::ManagedUserRegistrationServiceFactory()
-    : BrowserContextKeyedServiceFactory(
-          "ManagedUserRegistrationService",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
-}
-
-ManagedUserRegistrationServiceFactory::
-    ~ManagedUserRegistrationServiceFactory() {}
-
-BrowserContextKeyedService*
-ManagedUserRegistrationServiceFactory::BuildServiceInstanceFor(
-    content::BrowserContext* profile) const {
-  return BuildInstanceFor(static_cast<Profile*>(profile));
-}
diff --git a/chrome/browser/managed_mode/managed_user_registration_service_factory.h b/chrome/browser/managed_mode/managed_user_registration_service_factory.h
deleted file mode 100644
index b579bf8..0000000
--- a/chrome/browser/managed_mode/managed_user_registration_service_factory.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_SERVICE_FACTORY_H_
-#define CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_SERVICE_FACTORY_H_
-
-#include "base/memory/singleton.h"
-#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
-
-class ManagedUserRegistrationService;
-class Profile;
-
-class ManagedUserRegistrationServiceFactory
-    : public BrowserContextKeyedServiceFactory {
- public:
-  static ManagedUserRegistrationService* GetForProfile(Profile* profile);
-
-  static ManagedUserRegistrationServiceFactory* GetInstance();
-
-  // Used to create instances for testing.
-  static BrowserContextKeyedService* BuildInstanceFor(Profile* profile);
-
- private:
-  friend struct DefaultSingletonTraits<ManagedUserRegistrationServiceFactory>;
-
-  ManagedUserRegistrationServiceFactory();
-  virtual ~ManagedUserRegistrationServiceFactory();
-
-  // BrowserContextKeyedServiceFactory:
-  virtual BrowserContextKeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* profile) const OVERRIDE;
-};
-
-#endif  // CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_SERVICE_FACTORY_H_
diff --git a/chrome/browser/managed_mode/managed_user_registration_service_unittest.cc b/chrome/browser/managed_mode/managed_user_registration_service_unittest.cc
deleted file mode 100644
index 60e4a35..0000000
--- a/chrome/browser/managed_mode/managed_user_registration_service_unittest.cc
+++ /dev/null
@@ -1,388 +0,0 @@
-// Copyright 2013 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.
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
-#include "chrome/browser/managed_mode/managed_user_registration_service.h"
-#include "chrome/browser/prefs/scoped_user_pref_update.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_pref_service_syncable.h"
-#include "content/public/browser/browser_thread.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-#include "sync/api/sync_change.h"
-#include "sync/api/sync_error_factory_mock.h"
-#include "sync/protocol/sync.pb.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using sync_pb::ManagedUserSpecifics;
-using syncer::MANAGED_USERS;
-using syncer::SyncChange;
-using syncer::SyncChangeList;
-using syncer::SyncChangeProcessor;
-using syncer::SyncData;
-using syncer::SyncDataList;
-using syncer::SyncError;
-using syncer::SyncErrorFactory;
-using syncer::SyncMergeResult;
-
-namespace {
-
-const char kManagedUserToken[] = "managedusertoken";
-
-class MockChangeProcessor : public SyncChangeProcessor {
- public:
-  MockChangeProcessor() {}
-  virtual ~MockChangeProcessor() {}
-
-  // SyncChangeProcessor implementation:
-  virtual SyncError ProcessSyncChanges(
-      const tracked_objects::Location& from_here,
-      const SyncChangeList& change_list) OVERRIDE;
-
-  const SyncChangeList& changes() const { return change_list_; }
-  SyncChange GetChange(const std::string& id) const;
-
- private:
-  SyncChangeList change_list_;
-};
-
-SyncError MockChangeProcessor::ProcessSyncChanges(
-    const tracked_objects::Location& from_here,
-    const SyncChangeList& change_list) {
-  change_list_ = change_list;
-  return SyncError();
-}
-
-SyncChange MockChangeProcessor::GetChange(const std::string& id) const {
-  for (SyncChangeList::const_iterator it = change_list_.begin();
-       it != change_list_.end(); ++it) {
-    if (it->sync_data().GetSpecifics().managed_user().id() == id)
-      return *it;
-  }
-  return SyncChange();
-}
-
-class MockManagedUserRefreshTokenFetcher
-    : public ManagedUserRefreshTokenFetcher {
- public:
-  MockManagedUserRefreshTokenFetcher() {}
-  virtual ~MockManagedUserRefreshTokenFetcher() {}
-
-  // ManagedUserRefreshTokenFetcher implementation:
-  virtual void Start(const std::string& managed_user_id,
-                     const string16& name,
-                     const std::string& device_name,
-                     const TokenCallback& callback) OVERRIDE {
-    GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
-    callback.Run(error, kManagedUserToken);
-  }
-};
-
-}  // namespace
-
-class ManagedUserRegistrationServiceTest : public ::testing::Test {
- public:
-  ManagedUserRegistrationServiceTest();
-  virtual ~ManagedUserRegistrationServiceTest();
-
-  virtual void TearDown() OVERRIDE;
-
- protected:
-  scoped_ptr<SyncChangeProcessor> CreateChangeProcessor();
-  scoped_ptr<SyncErrorFactory> CreateErrorFactory();
-  SyncData CreateRemoteData(const std::string& id, const std::string& name);
-
-  SyncMergeResult StartInitialSync();
-
-  ManagedUserRegistrationService::RegistrationCallback
-      GetRegistrationCallback();
-
-  void Acknowledge();
-  void ResetService();
-
-  PrefService* prefs() { return &prefs_; }
-  ManagedUserRegistrationService* service() { return service_.get(); }
-  MockChangeProcessor* change_processor() { return change_processor_; }
-
-  bool received_callback() const { return received_callback_; }
-  const GoogleServiceAuthError& error() const { return error_; }
-  const std::string& token() const { return token_; }
-
- private:
-  void OnManagedUserRegistered(const GoogleServiceAuthError& error,
-                               const std::string& token);
-
-  base::MessageLoop message_loop_;
-  base::RunLoop run_loop_;
-  base::WeakPtrFactory<ManagedUserRegistrationServiceTest> weak_ptr_factory_;
-  TestingPrefServiceSyncable prefs_;
-  scoped_ptr<ManagedUserRegistrationService> service_;
-
-  // Owned by the ManagedUserRegistrationService.
-  MockChangeProcessor* change_processor_;
-
-  // A unique ID for creating "remote" Sync data.
-  int64 sync_data_id_;
-
-  // Whether OnManagedUserRegistered has been called.
-  bool received_callback_;
-
-  // Hold the registration result (either an error, or a token).
-  GoogleServiceAuthError error_;
-  std::string token_;
-};
-
-ManagedUserRegistrationServiceTest::ManagedUserRegistrationServiceTest()
-    : weak_ptr_factory_(this),
-      change_processor_(NULL),
-      sync_data_id_(0),
-      received_callback_(false),
-      error_(GoogleServiceAuthError::NUM_STATES) {
-  ManagedUserRegistrationService::RegisterProfilePrefs(prefs_.registry());
-  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
-      new MockManagedUserRefreshTokenFetcher);
-  service_.reset(
-      new ManagedUserRegistrationService(&prefs_, token_fetcher.Pass()));
-}
-
-ManagedUserRegistrationServiceTest::~ManagedUserRegistrationServiceTest() {
-  EXPECT_FALSE(weak_ptr_factory_.HasWeakPtrs());
-}
-
-void ManagedUserRegistrationServiceTest::TearDown() {
-  content::BrowserThread::GetBlockingPool()->FlushForTesting();
-  base::RunLoop().RunUntilIdle();
-}
-
-scoped_ptr<SyncChangeProcessor>
-ManagedUserRegistrationServiceTest::CreateChangeProcessor() {
-  EXPECT_FALSE(change_processor_);
-  change_processor_ = new MockChangeProcessor();
-  return scoped_ptr<SyncChangeProcessor>(change_processor_);
-}
-
-scoped_ptr<SyncErrorFactory>
-ManagedUserRegistrationServiceTest::CreateErrorFactory() {
-  return scoped_ptr<SyncErrorFactory>(new syncer::SyncErrorFactoryMock());
-}
-
-SyncData ManagedUserRegistrationServiceTest::CreateRemoteData(
-    const std::string& id,
-    const std::string& name) {
-  ::sync_pb::EntitySpecifics specifics;
-  specifics.mutable_managed_user()->set_id(id);
-  specifics.mutable_managed_user()->set_name(name);
-  specifics.mutable_managed_user()->set_acknowledged(true);
-  return SyncData::CreateRemoteData(++sync_data_id_, specifics, base::Time());
-}
-
-SyncMergeResult ManagedUserRegistrationServiceTest::StartInitialSync() {
-  SyncDataList initial_sync_data;
-  SyncMergeResult result =
-      service()->MergeDataAndStartSyncing(MANAGED_USERS,
-                                          initial_sync_data,
-                                          CreateChangeProcessor(),
-                                          CreateErrorFactory());
-  EXPECT_FALSE(result.error().IsSet());
-  return result;
-}
-
-ManagedUserRegistrationService::RegistrationCallback
-ManagedUserRegistrationServiceTest::GetRegistrationCallback() {
-  return base::Bind(
-      &ManagedUserRegistrationServiceTest::OnManagedUserRegistered,
-      weak_ptr_factory_.GetWeakPtr());
-}
-
-void ManagedUserRegistrationServiceTest::Acknowledge() {
-  SyncChangeList new_changes;
-  const SyncChangeList& changes = change_processor()->changes();
-  for (SyncChangeList::const_iterator it = changes.begin(); it != changes.end();
-       ++it) {
-    EXPECT_EQ(SyncChange::ACTION_ADD, it->change_type());
-    ::sync_pb::EntitySpecifics specifics = it->sync_data().GetSpecifics();
-    EXPECT_FALSE(specifics.managed_user().acknowledged());
-    specifics.mutable_managed_user()->set_acknowledged(true);
-    new_changes.push_back(
-        SyncChange(FROM_HERE, SyncChange::ACTION_UPDATE,
-                   SyncData::CreateRemoteData(++sync_data_id_,
-                                              specifics,
-                                              base::Time())));
-  }
-  service()->ProcessSyncChanges(FROM_HERE, new_changes);
-
-  run_loop_.Run();
-}
-
-void ManagedUserRegistrationServiceTest::ResetService() {
-  service_->StopSyncing(MANAGED_USERS);
-  service_->Shutdown();
-  service_.reset();
-}
-
-void ManagedUserRegistrationServiceTest::OnManagedUserRegistered(
-    const GoogleServiceAuthError& error,
-    const std::string& token) {
-  received_callback_ = true;
-  error_ = error;
-  token_ = token;
-  run_loop_.Quit();
-}
-
-TEST_F(ManagedUserRegistrationServiceTest, MergeEmpty) {
-  SyncMergeResult result = StartInitialSync();
-  EXPECT_EQ(0, result.num_items_added());
-  EXPECT_EQ(0, result.num_items_modified());
-  EXPECT_EQ(0, result.num_items_deleted());
-  EXPECT_EQ(0, result.num_items_before_association());
-  EXPECT_EQ(0, result.num_items_after_association());
-  EXPECT_EQ(0u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
-  EXPECT_EQ(0u, change_processor()->changes().size());
-
-  ResetService();
-}
-
-TEST_F(ManagedUserRegistrationServiceTest, MergeExisting) {
-  const char kNameKey[] = "name";
-  const char kAcknowledgedKey[] = "acknowledged";
-
-  const char kUserId1[] = "aaaaa";
-  const char kUserId2[] = "bbbbb";
-  const char kUserId3[] = "ccccc";
-  const char kUserId4[] = "ddddd";
-  const char kName1[] = "Anchor";
-  const char kName2[] = "Buzz";
-  const char kName3[] = "Crush";
-  const char kName4[] = "Dory";
-  {
-    DictionaryPrefUpdate update(prefs(), prefs::kManagedUsers);
-    DictionaryValue* managed_users = update.Get();
-    DictionaryValue* dict = new DictionaryValue;
-    dict->SetString(kNameKey, kName1);
-    managed_users->Set(kUserId1, dict);
-    dict = new DictionaryValue;
-    dict->SetString(kNameKey, kName2);
-    dict->SetBoolean(kAcknowledgedKey, true);
-    managed_users->Set(kUserId2, dict);
-  }
-
-  SyncDataList initial_sync_data;
-  initial_sync_data.push_back(CreateRemoteData(kUserId2, kName2));
-  initial_sync_data.push_back(CreateRemoteData(kUserId3, kName3));
-  initial_sync_data.push_back(CreateRemoteData(kUserId4, kName4));
-
-  SyncMergeResult result =
-      service()->MergeDataAndStartSyncing(MANAGED_USERS,
-                                          initial_sync_data,
-                                          CreateChangeProcessor(),
-                                          CreateErrorFactory());
-  EXPECT_FALSE(result.error().IsSet());
-  EXPECT_EQ(2, result.num_items_added());
-  EXPECT_EQ(1, result.num_items_modified());
-  EXPECT_EQ(0, result.num_items_deleted());
-  EXPECT_EQ(2, result.num_items_before_association());
-  EXPECT_EQ(4, result.num_items_after_association());
-
-  const DictionaryValue* managed_users =
-      prefs()->GetDictionary(prefs::kManagedUsers);
-  EXPECT_EQ(4u, managed_users->size());
-  {
-    const DictionaryValue* managed_user = NULL;
-    ASSERT_TRUE(managed_users->GetDictionary(kUserId2, &managed_user));
-    ASSERT_TRUE(managed_user);
-    std::string name;
-    EXPECT_TRUE(managed_user->GetString(kNameKey, &name));
-    EXPECT_EQ(kName2, name);
-    bool acknowledged = false;
-    EXPECT_TRUE(managed_user->GetBoolean(kAcknowledgedKey, &acknowledged));
-    EXPECT_TRUE(acknowledged);
-  }
-  {
-    const DictionaryValue* managed_user = NULL;
-    ASSERT_TRUE(managed_users->GetDictionary(kUserId3, &managed_user));
-    ASSERT_TRUE(managed_user);
-    std::string name;
-    EXPECT_TRUE(managed_user->GetString(kNameKey, &name));
-    EXPECT_EQ(kName3, name);
-    bool acknowledged = false;
-    EXPECT_TRUE(managed_user->GetBoolean(kAcknowledgedKey, &acknowledged));
-    EXPECT_TRUE(acknowledged);
-  }
-  {
-    const DictionaryValue* managed_user = NULL;
-    ASSERT_TRUE(managed_users->GetDictionary(kUserId4, &managed_user));
-    ASSERT_TRUE(managed_user);
-    std::string name;
-    EXPECT_TRUE(managed_user->GetString(kNameKey, &name));
-    EXPECT_EQ(kName4, name);
-    bool acknowledged = false;
-    EXPECT_TRUE(managed_user->GetBoolean(kAcknowledgedKey, &acknowledged));
-    EXPECT_TRUE(acknowledged);
-  }
-
-  EXPECT_EQ(1u, change_processor()->changes().size());
-  {
-    SyncChange change = change_processor()->GetChange(kUserId1);
-    ASSERT_TRUE(change.IsValid());
-    EXPECT_EQ(SyncChange::ACTION_ADD, change.change_type());
-    const ManagedUserSpecifics& managed_user =
-        change.sync_data().GetSpecifics().managed_user();
-    EXPECT_EQ(kName1, managed_user.name());
-    EXPECT_FALSE(managed_user.acknowledged());
-  }
-}
-
-TEST_F(ManagedUserRegistrationServiceTest, Register) {
-  StartInitialSync();
-  service()->Register(ManagedUserRegistrationInfo(ASCIIToUTF16("Dug")),
-      GetRegistrationCallback());
-  EXPECT_EQ(1u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
-  Acknowledge();
-
-  EXPECT_TRUE(received_callback());
-  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
-  EXPECT_FALSE(token().empty());
-}
-
-TEST_F(ManagedUserRegistrationServiceTest, RegisterBeforeInitialSync) {
-  service()->Register(ManagedUserRegistrationInfo(ASCIIToUTF16("Nemo")),
-      GetRegistrationCallback());
-  EXPECT_EQ(1u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
-  StartInitialSync();
-  Acknowledge();
-
-  EXPECT_TRUE(received_callback());
-  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
-  EXPECT_FALSE(token().empty());
-}
-
-TEST_F(ManagedUserRegistrationServiceTest, Shutdown) {
-  StartInitialSync();
-  service()->Register(ManagedUserRegistrationInfo(ASCIIToUTF16("Remy")),
-      GetRegistrationCallback());
-  EXPECT_EQ(1u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
-  ResetService();
-  EXPECT_EQ(0u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
-  EXPECT_TRUE(received_callback());
-  EXPECT_EQ(GoogleServiceAuthError::REQUEST_CANCELED, error().state());
-  EXPECT_EQ(std::string(), token());
-}
-
-TEST_F(ManagedUserRegistrationServiceTest, StopSyncing) {
-  StartInitialSync();
-  service()->Register(ManagedUserRegistrationInfo(ASCIIToUTF16("Mike")),
-      GetRegistrationCallback());
-  EXPECT_EQ(1u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
-  service()->StopSyncing(MANAGED_USERS);
-  EXPECT_EQ(0u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
-  EXPECT_TRUE(received_callback());
-  EXPECT_EQ(GoogleServiceAuthError::REQUEST_CANCELED, error().state());
-  EXPECT_EQ(std::string(), token());
-}
diff --git a/chrome/browser/managed_mode/managed_user_registration_utility.cc b/chrome/browser/managed_mode/managed_user_registration_utility.cc
new file mode 100644
index 0000000..79feee9
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_registration_utility.cc
@@ -0,0 +1,181 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/managed_mode/managed_user_registration_utility.h"
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_service.h"
+#include "base/rand_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
+#include "chrome/browser/managed_mode/managed_user_service.h"
+#include "chrome/browser/managed_mode/managed_user_service_factory.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service_factory.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/signin/profile_oauth2_token_service.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/sync/glue/device_info.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+
+using base::DictionaryValue;
+
+// How long to wait before aborting user registration. If this is changed, the
+// histogram limits in the BrowserOptionsHandler should also be updated.
+const int kRegistrationTimeoutMS = 30 * 1000;
+const char kAcknowledged[] = "acknowledged";
+const char kName[] = "name";
+const char kMasterKey[] = "masterKey";
+
+ManagedUserRegistrationInfo::ManagedUserRegistrationInfo(const string16& name)
+    : name(name) {
+}
+
+ManagedUserRegistrationUtility::ManagedUserRegistrationUtility(
+    PrefService* prefs,
+    scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher,
+    ManagedUserSyncService* service)
+    : weak_ptr_factory_(this),
+      prefs_(prefs),
+      token_fetcher_(token_fetcher.Pass()),
+      managed_user_sync_service_(service),
+      pending_managed_user_acknowledged_(false) {
+  managed_user_sync_service_->AddObserver(this);
+}
+
+ManagedUserRegistrationUtility::~ManagedUserRegistrationUtility() {
+  managed_user_sync_service_->RemoveObserver(this);
+  CancelPendingRegistration();
+}
+
+// static
+scoped_ptr<ManagedUserRegistrationUtility>
+ManagedUserRegistrationUtility::Create(Profile* profile) {
+  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher =
+      ManagedUserRefreshTokenFetcher::Create(
+          ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
+          profile->GetRequestContext());
+  ManagedUserSyncService* managed_user_sync_service =
+      ManagedUserSyncServiceFactory::GetForProfile(profile);
+  return make_scoped_ptr(new ManagedUserRegistrationUtility(
+      profile->GetPrefs(), token_fetcher.Pass(), managed_user_sync_service));
+}
+
+void ManagedUserRegistrationUtility::Register(
+    const ManagedUserRegistrationInfo& info,
+    const RegistrationCallback& callback) {
+  DCHECK(pending_managed_user_id_.empty());
+  callback_ = callback;
+
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kNoManagedUserRegistrationTimeout)) {
+    registration_timer_.Start(
+        FROM_HERE,
+        base::TimeDelta::FromMilliseconds(kRegistrationTimeoutMS),
+        base::Bind(
+            &ManagedUserRegistrationUtility::AbortPendingRegistration,
+            weak_ptr_factory_.GetWeakPtr(),
+            true,  // Run the callback.
+            GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)));
+  }
+
+  std::string id_raw = base::RandBytesAsString(8);
+  bool success = base::Base64Encode(id_raw, &pending_managed_user_id_);
+  DCHECK(success);
+
+  managed_user_sync_service_->AddManagedUser(pending_managed_user_id_,
+                                             base::UTF16ToUTF8(info.name),
+                                             info.master_key);
+
+  browser_sync::DeviceInfo::GetClientName(
+      base::Bind(&ManagedUserRegistrationUtility::FetchToken,
+                 weak_ptr_factory_.GetWeakPtr(), info.name));
+}
+
+void ManagedUserRegistrationUtility::CancelPendingRegistration() {
+  AbortPendingRegistration(
+      false,  // Don't run the callback. The error will be ignored.
+      GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+}
+
+void ManagedUserRegistrationUtility::OnManagedUserAcknowledged(
+    const std::string& managed_user_id) {
+  DCHECK_EQ(pending_managed_user_id_, managed_user_id);
+  DCHECK(!pending_managed_user_acknowledged_);
+  pending_managed_user_acknowledged_ = true;
+  CompleteRegistrationIfReady();
+}
+
+void ManagedUserRegistrationUtility::OnManagedUsersSyncingStopped() {
+  AbortPendingRegistration(
+      true,   // Run the callback.
+      GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
+}
+
+void ManagedUserRegistrationUtility::FetchToken(
+    const string16& name,
+    const std::string& client_name) {
+  token_fetcher_->Start(
+      pending_managed_user_id_, name, client_name,
+      base::Bind(&ManagedUserRegistrationUtility::OnReceivedToken,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ManagedUserRegistrationUtility::OnReceivedToken(
+    const GoogleServiceAuthError& error,
+    const std::string& token) {
+  if (error.state() != GoogleServiceAuthError::NONE) {
+    CompleteRegistration(true, error);
+    return;
+  }
+
+  DCHECK(!token.empty());
+  pending_managed_user_token_ = token;
+  CompleteRegistrationIfReady();
+}
+
+void ManagedUserRegistrationUtility::CompleteRegistrationIfReady() {
+  if (!pending_managed_user_acknowledged_ ||
+      pending_managed_user_token_.empty()) {
+    return;
+  }
+
+  GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
+  CompleteRegistration(true, error);
+}
+
+void ManagedUserRegistrationUtility::AbortPendingRegistration(
+    bool run_callback,
+    const GoogleServiceAuthError& error) {
+  pending_managed_user_token_.clear();
+  CompleteRegistration(run_callback, error);
+}
+
+void ManagedUserRegistrationUtility::CompleteRegistration(
+    bool run_callback,
+    const GoogleServiceAuthError& error) {
+  registration_timer_.Stop();
+  if (callback_.is_null())
+    return;
+
+  if (pending_managed_user_token_.empty()) {
+    DCHECK(!pending_managed_user_id_.empty());
+    // Remove the pending managed user if we weren't successful.
+    DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
+    bool success =
+        update->RemoveWithoutPathExpansion(pending_managed_user_id_, NULL);
+    DCHECK(success);
+    managed_user_sync_service_->DeleteManagedUser(pending_managed_user_id_);
+  }
+
+  if (run_callback)
+    callback_.Run(error, pending_managed_user_token_);
+  callback_.Reset();
+}
diff --git a/chrome/browser/managed_mode/managed_user_registration_utility.h b/chrome/browser/managed_mode/managed_user_registration_utility.h
new file mode 100644
index 0000000..30a67d8
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_registration_utility.h
@@ -0,0 +1,128 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_UTILITY_H_
+#define CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_UTILITY_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/prefs/pref_change_registrar.h"
+#include "base/strings/string16.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service_observer.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+
+class GoogleServiceAuthError;
+class ManagedUserRefreshTokenFetcher;
+class ManagedUserRegistrationUtilityTest;
+class PrefService;
+
+namespace browser_sync {
+class DeviceInfo;
+}
+
+// Structure to store registration information.
+struct ManagedUserRegistrationInfo {
+  explicit ManagedUserRegistrationInfo(const string16& name);
+  string16 name;
+  std::string master_key;
+};
+
+// Holds the state necessary for registering a new managed user with the
+// management server and associating it with its custodian. Each instance
+// of this class handles registering a single managed user and should not
+// be used afterwards.
+class ManagedUserRegistrationUtility
+    : public ManagedUserSyncServiceObserver {
+ public:
+  // Callback for Register() below. If registration is successful, |token| will
+  // contain an OAuth2 refresh token for the newly registered managed user,
+  // otherwise |token| will be empty and |error| will contain the authentication
+  // error for the custodian.
+  typedef base::Callback<void(const GoogleServiceAuthError& /* error */,
+                              const std::string& /* token */)>
+      RegistrationCallback;
+
+  virtual ~ManagedUserRegistrationUtility();
+
+  static scoped_ptr<ManagedUserRegistrationUtility> Create(Profile* profile);
+
+  // Registers a new managed user with the server. |info| contains necessary
+  // information like the display name of the  the user. |callback| is called
+  // with the result of the registration. We use the info here and not the
+  // profile, because on Chrome OS the profile of the managed user does
+  // not yet exist.
+  void Register(const ManagedUserRegistrationInfo& info,
+                const RegistrationCallback& callback);
+
+  // ManagedUserSyncServiceObserver:
+  virtual void OnManagedUserAcknowledged(const std::string& managed_user_id)
+      OVERRIDE;
+  virtual void OnManagedUsersSyncingStopped() OVERRIDE;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(ManagedUserRegistrationUtilityTest, Register);
+  FRIEND_TEST_ALL_PREFIXES(ManagedUserRegistrationUtilityTest,
+                           RegisterBeforeInitialSync);
+  FRIEND_TEST_ALL_PREFIXES(ManagedUserRegistrationUtilityTest,
+                           SyncServiceShutdownBeforeRegFinish);
+  FRIEND_TEST_ALL_PREFIXES(ManagedUserRegistrationUtilityTest,
+                           StopSyncingBeforeRegFinish);
+
+  // Use the |Create(...)| method to get instances of this class.
+  ManagedUserRegistrationUtility(
+      PrefService* prefs,
+      scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher,
+      ManagedUserSyncService* service);
+  // Fetches the managed user token when we have the device name.
+  void FetchToken(const string16& name,
+                  const std::string& client_name);
+
+  // Called when we have received a token for the managed user.
+  void OnReceivedToken(const GoogleServiceAuthError& error,
+                       const std::string& token);
+
+  // Dispatches the callback and cleans up if all the conditions have been met.
+  void CompleteRegistrationIfReady();
+
+  // Aborts any registration currently in progress. If |run_callback| is true,
+  // calls the callback specified in Register() with the given |error|.
+  void AbortPendingRegistration(bool run_callback,
+                                const GoogleServiceAuthError& error);
+
+  // If |run_callback| is true, dispatches the callback with the saved token
+  // (which may be empty) and the given |error|. In any case, resets internal
+  // variables to be ready for the next registration.
+  void CompleteRegistration(bool run_callback,
+                            const GoogleServiceAuthError& error);
+
+  // Cancels any registration currently in progress, without calling the
+  // callback or reporting an error.
+  void CancelPendingRegistration();
+
+  base::WeakPtrFactory<ManagedUserRegistrationUtility> weak_ptr_factory_;
+  PrefService* prefs_;
+  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher_;
+
+  // A |BrowserContextKeyedService| owned by the custodian profile.
+  ManagedUserSyncService* managed_user_sync_service_;
+
+  // Provides a timeout during profile creation.
+  base::OneShotTimer<ManagedUserRegistrationUtility> registration_timer_;
+
+  std::string pending_managed_user_id_;
+  std::string pending_managed_user_token_;
+  bool pending_managed_user_acknowledged_;
+  RegistrationCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ManagedUserRegistrationUtility);
+};
+
+#endif  // CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_UTILITY_H_
diff --git a/chrome/browser/managed_mode/managed_user_registration_utility_unittest.cc b/chrome/browser/managed_mode/managed_user_registration_utility_unittest.cc
new file mode 100644
index 0000000..60a84bb
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_registration_utility_unittest.cc
@@ -0,0 +1,279 @@
+// Copyright 2013 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.
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
+#include "chrome/browser/managed_mode/managed_user_registration_utility.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service_factory.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_pref_service_syncable.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "sync/api/sync_change.h"
+#include "sync/api/sync_error_factory_mock.h"
+#include "sync/protocol/sync.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sync_pb::ManagedUserSpecifics;
+using syncer::MANAGED_USERS;
+using syncer::SyncChange;
+using syncer::SyncChangeList;
+using syncer::SyncChangeProcessor;
+using syncer::SyncData;
+using syncer::SyncDataList;
+using syncer::SyncError;
+using syncer::SyncErrorFactory;
+using syncer::SyncMergeResult;
+
+namespace {
+
+const char kManagedUserToken[] = "managedusertoken";
+
+class MockChangeProcessor : public SyncChangeProcessor {
+ public:
+  MockChangeProcessor() {}
+  virtual ~MockChangeProcessor() {}
+
+  // SyncChangeProcessor implementation:
+  virtual SyncError ProcessSyncChanges(
+      const tracked_objects::Location& from_here,
+      const SyncChangeList& change_list) OVERRIDE;
+
+  const SyncChangeList& changes() const { return change_list_; }
+
+ private:
+  SyncChangeList change_list_;
+};
+
+SyncError MockChangeProcessor::ProcessSyncChanges(
+    const tracked_objects::Location& from_here,
+    const SyncChangeList& change_list) {
+  change_list_ = change_list;
+  return SyncError();
+}
+
+class MockManagedUserRefreshTokenFetcher
+    : public ManagedUserRefreshTokenFetcher {
+ public:
+  MockManagedUserRefreshTokenFetcher() {}
+  virtual ~MockManagedUserRefreshTokenFetcher() {}
+
+  // ManagedUserRefreshTokenFetcher implementation:
+  virtual void Start(const std::string& managed_user_id,
+                     const string16& name,
+                     const std::string& device_name,
+                     const TokenCallback& callback) OVERRIDE {
+    GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
+    callback.Run(error, kManagedUserToken);
+  }
+};
+
+}  // namespace
+
+class ManagedUserRegistrationUtilityTest : public ::testing::Test {
+ public:
+  ManagedUserRegistrationUtilityTest();
+  virtual ~ManagedUserRegistrationUtilityTest();
+
+  virtual void TearDown() OVERRIDE;
+
+ protected:
+  scoped_ptr<SyncChangeProcessor> CreateChangeProcessor();
+  scoped_ptr<SyncErrorFactory> CreateErrorFactory();
+  SyncData CreateRemoteData(const std::string& id, const std::string& name);
+
+  SyncMergeResult StartInitialSync();
+
+  ManagedUserRegistrationUtility::RegistrationCallback
+      GetRegistrationCallback();
+
+  void Acknowledge();
+
+  PrefService* prefs() { return profile_.GetTestingPrefService(); }
+  ManagedUserSyncService* service() { return service_; }
+  MockChangeProcessor* change_processor() { return change_processor_; }
+
+  bool received_callback() const { return received_callback_; }
+  const GoogleServiceAuthError& error() const { return error_; }
+  const std::string& token() const { return token_; }
+
+ private:
+  void OnManagedUserRegistered(const GoogleServiceAuthError& error,
+                               const std::string& token);
+
+  base::MessageLoop message_loop_;
+  base::RunLoop run_loop_;
+  base::WeakPtrFactory<ManagedUserRegistrationUtilityTest> weak_ptr_factory_;
+  TestingProfile profile_;
+  ManagedUserSyncService* service_;
+
+  // Owned by the ManagedUserSyncService.
+  MockChangeProcessor* change_processor_;
+
+  // A unique ID for creating "remote" Sync data.
+  int64 sync_data_id_;
+
+  // Whether OnManagedUserRegistered has been called.
+  bool received_callback_;
+
+  // Hold the registration result (either an error, or a token).
+  GoogleServiceAuthError error_;
+  std::string token_;
+};
+
+ManagedUserRegistrationUtilityTest::ManagedUserRegistrationUtilityTest()
+    : weak_ptr_factory_(this),
+      change_processor_(NULL),
+      sync_data_id_(0),
+      received_callback_(false),
+      error_(GoogleServiceAuthError::NUM_STATES) {
+  service_ = ManagedUserSyncServiceFactory::GetForProfile(&profile_);
+}
+
+ManagedUserRegistrationUtilityTest::~ManagedUserRegistrationUtilityTest() {
+  EXPECT_FALSE(weak_ptr_factory_.HasWeakPtrs());
+}
+
+void ManagedUserRegistrationUtilityTest::TearDown() {
+  content::BrowserThread::GetBlockingPool()->FlushForTesting();
+  base::RunLoop().RunUntilIdle();
+}
+
+scoped_ptr<SyncChangeProcessor>
+ManagedUserRegistrationUtilityTest::CreateChangeProcessor() {
+  EXPECT_FALSE(change_processor_);
+  change_processor_ = new MockChangeProcessor();
+  return scoped_ptr<SyncChangeProcessor>(change_processor_);
+}
+
+scoped_ptr<SyncErrorFactory>
+ManagedUserRegistrationUtilityTest::CreateErrorFactory() {
+  return scoped_ptr<SyncErrorFactory>(new syncer::SyncErrorFactoryMock());
+}
+
+SyncMergeResult ManagedUserRegistrationUtilityTest::StartInitialSync() {
+  SyncDataList initial_sync_data;
+  SyncMergeResult result =
+      service()->MergeDataAndStartSyncing(MANAGED_USERS,
+                                          initial_sync_data,
+                                          CreateChangeProcessor(),
+                                          CreateErrorFactory());
+  EXPECT_FALSE(result.error().IsSet());
+  return result;
+}
+
+ManagedUserRegistrationUtility::RegistrationCallback
+ManagedUserRegistrationUtilityTest::GetRegistrationCallback() {
+  return base::Bind(
+      &ManagedUserRegistrationUtilityTest::OnManagedUserRegistered,
+      weak_ptr_factory_.GetWeakPtr());
+}
+
+void ManagedUserRegistrationUtilityTest::Acknowledge() {
+  SyncChangeList new_changes;
+  const SyncChangeList& changes = change_processor()->changes();
+  for (SyncChangeList::const_iterator it = changes.begin(); it != changes.end();
+       ++it) {
+    EXPECT_EQ(SyncChange::ACTION_ADD, it->change_type());
+    ::sync_pb::EntitySpecifics specifics = it->sync_data().GetSpecifics();
+    EXPECT_FALSE(specifics.managed_user().acknowledged());
+    specifics.mutable_managed_user()->set_acknowledged(true);
+    new_changes.push_back(
+        SyncChange(FROM_HERE, SyncChange::ACTION_UPDATE,
+                   SyncData::CreateRemoteData(++sync_data_id_,
+                                              specifics,
+                                              base::Time())));
+  }
+  service()->ProcessSyncChanges(FROM_HERE, new_changes);
+
+  run_loop_.Run();
+}
+
+void ManagedUserRegistrationUtilityTest::OnManagedUserRegistered(
+    const GoogleServiceAuthError& error,
+    const std::string& token) {
+  received_callback_ = true;
+  error_ = error;
+  token_ = token;
+  run_loop_.Quit();
+}
+
+TEST_F(ManagedUserRegistrationUtilityTest, Register) {
+  StartInitialSync();
+  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
+      new MockManagedUserRefreshTokenFetcher);
+  ManagedUserRegistrationUtility registration_utility(prefs(),
+                                                      token_fetcher.Pass(),
+                                                      service());
+  registration_utility.Register(
+      ManagedUserRegistrationInfo(ASCIIToUTF16("Dug")),
+      GetRegistrationCallback());
+  EXPECT_EQ(1u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
+  Acknowledge();
+
+  EXPECT_TRUE(received_callback());
+  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
+  EXPECT_FALSE(token().empty());
+}
+
+TEST_F(ManagedUserRegistrationUtilityTest, RegisterBeforeInitialSync) {
+  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
+      new MockManagedUserRefreshTokenFetcher);
+  ManagedUserRegistrationUtility registration_utility(prefs(),
+                                                      token_fetcher.Pass(),
+                                                      service());
+  registration_utility.Register(
+      ManagedUserRegistrationInfo(ASCIIToUTF16("Nemo")),
+      GetRegistrationCallback());
+  EXPECT_EQ(1u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
+  StartInitialSync();
+  Acknowledge();
+
+  EXPECT_TRUE(received_callback());
+  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
+  EXPECT_FALSE(token().empty());
+}
+
+TEST_F(ManagedUserRegistrationUtilityTest, SyncServiceShutdownBeforeRegFinish) {
+  StartInitialSync();
+  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
+      new MockManagedUserRefreshTokenFetcher);
+  ManagedUserRegistrationUtility registration_utility(prefs(),
+                                                      token_fetcher.Pass(),
+                                                      service());
+  registration_utility.Register(
+      ManagedUserRegistrationInfo(ASCIIToUTF16("Remy")),
+      GetRegistrationCallback());
+  EXPECT_EQ(1u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
+  service()->Shutdown();
+  EXPECT_EQ(0u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
+  EXPECT_TRUE(received_callback());
+  EXPECT_EQ(GoogleServiceAuthError::REQUEST_CANCELED, error().state());
+  EXPECT_EQ(std::string(), token());
+}
+
+TEST_F(ManagedUserRegistrationUtilityTest, StopSyncingBeforeRegFinish) {
+  StartInitialSync();
+  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
+      new MockManagedUserRefreshTokenFetcher);
+  ManagedUserRegistrationUtility registration_utility(prefs(),
+                                                      token_fetcher.Pass(),
+                                                      service());
+  registration_utility.Register(
+      ManagedUserRegistrationInfo(ASCIIToUTF16("Mike")),
+      GetRegistrationCallback());
+  EXPECT_EQ(1u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
+  service()->StopSyncing(MANAGED_USERS);
+  EXPECT_EQ(0u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
+  EXPECT_TRUE(received_callback());
+  EXPECT_EQ(GoogleServiceAuthError::REQUEST_CANCELED, error().state());
+  EXPECT_EQ(std::string(), token());
+}
diff --git a/chrome/browser/managed_mode/managed_user_service.cc b/chrome/browser/managed_mode/managed_user_service.cc
index e48e4b7..42452f4 100644
--- a/chrome/browser/managed_mode/managed_user_service.cc
+++ b/chrome/browser/managed_mode/managed_user_service.cc
@@ -14,9 +14,12 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/managed_mode/custodian_profile_downloader_service.h"
+#include "chrome/browser/managed_mode/custodian_profile_downloader_service_factory.h"
 #include "chrome/browser/managed_mode/managed_mode_site_list.h"
-#include "chrome/browser/managed_mode/managed_user_registration_service.h"
-#include "chrome/browser/managed_mode/managed_user_registration_service_factory.h"
+#include "chrome/browser/managed_mode/managed_user_registration_utility.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service_factory.h"
 #include "chrome/browser/policy/managed_mode_policy_provider.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector_factory.h"
@@ -172,12 +175,7 @@
 }
 
 bool ManagedUserService::ProfileIsManaged() const {
-  return ProfileIsManaged(profile_);
-}
-
-// static
-bool ManagedUserService::ProfileIsManaged(Profile* profile) {
-  return profile->GetPrefs()->GetBoolean(prefs::kProfileIsManaged);
+  return profile_->IsManaged();
 }
 
 // static
@@ -561,16 +559,16 @@
 }
 
 void ManagedUserService::RegisterAndInitSync(
+    ManagedUserRegistrationUtility* registration_utility,
     Profile* custodian_profile,
     const ProfileManager::CreateCallback& callback) {
+  DCHECK(ProfileIsManaged());
+  DCHECK(!custodian_profile->IsManaged());
 
-  // Register the managed user with the custodian's account.
-  ManagedUserRegistrationService* registration_service =
-      ManagedUserRegistrationServiceFactory::GetForProfile(custodian_profile);
   string16 name = UTF8ToUTF16(
       profile_->GetPrefs()->GetString(prefs::kProfileName));
   ManagedUserRegistrationInfo info(name);
-  registration_service->Register(
+  registration_utility->Register(
       info,
       base::Bind(&ManagedUserService::OnManagedUserRegistered,
                  weak_ptr_factory_.GetWeakPtr(), callback, custodian_profile));
@@ -578,9 +576,12 @@
   // Fetch the custodian's profile information, to store the name.
   // TODO(pamg): If --gaia-profile-info (keyword: switches::kGaiaProfileInfo)
   // is ever enabled, take the name from the ProfileInfoCache instead.
-  registration_service->DownloadProfile(custodian_profile,
-    base::Bind(&ManagedUserService::OnCustodianProfileDownloaded,
-               weak_ptr_factory_.GetWeakPtr()));
+  CustodianProfileDownloaderService* profile_downloader_service =
+      CustodianProfileDownloaderServiceFactory::GetForProfile(
+          custodian_profile);
+  profile_downloader_service->DownloadProfile(
+      base::Bind(&ManagedUserService::OnCustodianProfileDownloaded,
+                 weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ManagedUserService::OnCustodianProfileDownloaded(
diff --git a/chrome/browser/managed_mode/managed_user_service.h b/chrome/browser/managed_mode/managed_user_service.h
index 70de621..9c499fb 100644
--- a/chrome/browser/managed_mode/managed_user_service.h
+++ b/chrome/browser/managed_mode/managed_user_service.h
@@ -24,7 +24,7 @@
 class GoogleServiceAuthError;
 class ManagedModeURLFilter;
 class ManagedModeSiteList;
-class ManagedUserRegistrationService;
+class ManagedUserRegistrationUtility;
 class Profile;
 
 namespace policy {
@@ -57,12 +57,6 @@
   // ProfileKeyedService override:
   virtual void Shutdown() OVERRIDE;
 
-  bool ProfileIsManaged() const;
-
-  // Checks whether the given profile is managed without constructing a
-  // ManagedUserService (which could lead to cyclic dependencies).
-  static bool ProfileIsManaged(Profile* profile);
-
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
   // Returns true if managed users are enabled by either Finch or the command
@@ -123,14 +117,13 @@
   // mint access tokens for Sync.
   void InitSync(const std::string& refresh_token);
 
-  // Convenience method that registers this managed user with
-  // |registration_service| and initializes sync with the returned token.
-  // Note that |registration_service| should belong to the custodian's profile,
-  // not this one. The |callback| will be called when registration is complete,
-  // whether it suceeded or not -- unless registration was cancelled in the
-  // ManagedUserRegistrationService manually, in which case the callback will
-  // be ignored.
-  void RegisterAndInitSync(Profile* custodian_profile,
+  // Convenience method that registers this managed user using
+  // |registration_utility| and initializes sync with the returned token.
+  // The |callback| will be called when registration is complete,
+  // whether it suceeded or not -- unless registration was cancelled manually,
+  // in which case the callback will be ignored.
+  void RegisterAndInitSync(ManagedUserRegistrationUtility* registration_utility,
+                           Profile* custodian_profile,
                            const ProfileManager::CreateCallback& callback);
 
   // Returns a pseudo-email address for systems that expect well-formed email
@@ -162,6 +155,10 @@
  private:
   friend class ManagedUserServiceExtensionTest;
   friend class ManagedUserServiceFactory;
+  FRIEND_TEST_ALL_PREFIXES(ManagedUserServiceTest,
+                           ExtensionManagementPolicyProviderUnmanaged);
+  FRIEND_TEST_ALL_PREFIXES(ManagedUserServiceTest,
+                           ExtensionManagementPolicyProviderManaged);
 
   // A bridge from ManagedMode (which lives on the UI thread) to the
   // ManagedModeURLFilters, one of which lives on the IO thread. This class
@@ -206,6 +203,8 @@
 
   void SetupSync();
 
+  bool ProfileIsManaged() const;
+
   // Internal implementation for ExtensionManagementPolicy::Delegate methods.
   // If |error| is not NULL, it will be filled with an error message if the
   // requested extension action (install, modify status, etc.) is not permitted.
diff --git a/chrome/browser/managed_mode/managed_user_sync_service.cc b/chrome/browser/managed_mode/managed_user_sync_service.cc
new file mode 100644
index 0000000..ca5002c
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_sync_service.cc
@@ -0,0 +1,290 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/managed_mode/managed_user_sync_service.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "components/user_prefs/pref_registry_syncable.h"
+#include "sync/api/sync_change.h"
+#include "sync/api/sync_data.h"
+#include "sync/api/sync_error.h"
+#include "sync/api/sync_error_factory.h"
+#include "sync/api/sync_merge_result.h"
+#include "sync/protocol/sync.pb.h"
+
+using base::DictionaryValue;
+using user_prefs::PrefRegistrySyncable;
+using syncer::MANAGED_USERS;
+using syncer::ModelType;
+using syncer::SyncChange;
+using syncer::SyncChangeList;
+using syncer::SyncChangeProcessor;
+using syncer::SyncData;
+using syncer::SyncDataList;
+using syncer::SyncError;
+using syncer::SyncErrorFactory;
+using syncer::SyncMergeResult;
+using sync_pb::ManagedUserSpecifics;
+
+namespace {
+
+const char kAcknowledged[] = "acknowledged";
+const char kName[] = "name";
+const char kMasterKey[] = "masterKey";
+
+SyncData CreateLocalSyncData(const std::string& id,
+                             const std::string& name,
+                             bool acknowledged,
+                             const std::string& master_key) {
+  ::sync_pb::EntitySpecifics specifics;
+  specifics.mutable_managed_user()->set_id(id);
+  specifics.mutable_managed_user()->set_name(name);
+  if (!master_key.empty())
+    specifics.mutable_managed_user()->set_master_key(master_key);
+  if (acknowledged)
+    specifics.mutable_managed_user()->set_acknowledged(true);
+  return SyncData::CreateLocalData(id, name, specifics);
+}
+
+}  // namespace
+
+ManagedUserSyncService::ManagedUserSyncService(PrefService* prefs)
+    : prefs_(prefs) {
+  pref_change_registrar_.Init(prefs_);
+  pref_change_registrar_.Add(
+      prefs::kGoogleServicesLastUsername,
+      base::Bind(&ManagedUserSyncService::OnLastSignedInUsernameChange,
+                 base::Unretained(this)));
+}
+
+ManagedUserSyncService::~ManagedUserSyncService() {
+}
+
+// static
+void ManagedUserSyncService::RegisterProfilePrefs(
+    PrefRegistrySyncable* registry) {
+  registry->RegisterDictionaryPref(prefs::kManagedUsers,
+                                   PrefRegistrySyncable::UNSYNCABLE_PREF);
+}
+
+void ManagedUserSyncService::AddObserver(
+    ManagedUserSyncServiceObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void ManagedUserSyncService::RemoveObserver(
+    ManagedUserSyncServiceObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void ManagedUserSyncService::AddManagedUser(const std::string& id,
+                                            const std::string& name,
+                                            const std::string& master_key) {
+  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
+  DictionaryValue* dict = update.Get();
+  DictionaryValue* value = new DictionaryValue;
+  value->SetString(kName, name);
+  value->SetString(kMasterKey, master_key);
+  DCHECK(!dict->HasKey(id));
+  dict->SetWithoutPathExpansion(id, value);
+
+  if (!sync_processor_)
+    return;
+
+  // If we're already syncing, create a new change and upload it.
+  SyncChangeList change_list;
+  change_list.push_back(SyncChange(
+      FROM_HERE,
+      SyncChange::ACTION_ADD,
+      CreateLocalSyncData(id, name, false, master_key)));
+  SyncError error =
+      sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
+  DCHECK(!error.IsSet()) << error.ToString();
+}
+
+void ManagedUserSyncService::DeleteManagedUser(const std::string& id) {
+  if (!sync_processor_)
+    return;
+
+  SyncChangeList change_list;
+  change_list.push_back(SyncChange(
+      FROM_HERE,
+      SyncChange::ACTION_DELETE,
+      SyncData::CreateLocalDelete(id, MANAGED_USERS)));
+  SyncError sync_error =
+      sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
+  DCHECK(!sync_error.IsSet());
+}
+
+void ManagedUserSyncService::Shutdown() {
+  NotifyManagedUsersSyncingStopped();
+}
+
+SyncMergeResult ManagedUserSyncService::MergeDataAndStartSyncing(
+    ModelType type,
+    const SyncDataList& initial_sync_data,
+    scoped_ptr<SyncChangeProcessor> sync_processor,
+    scoped_ptr<SyncErrorFactory> error_handler) {
+  DCHECK_EQ(MANAGED_USERS, type);
+  sync_processor_ = sync_processor.Pass();
+  error_handler_ = error_handler.Pass();
+
+  SyncChangeList change_list;
+  SyncMergeResult result(MANAGED_USERS);
+
+  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
+  DictionaryValue* dict = update.Get();
+  result.set_num_items_before_association(dict->size());
+  std::set<std::string> seen_ids;
+  int num_items_added = 0;
+  int num_items_modified = 0;
+  for (SyncDataList::const_iterator it = initial_sync_data.begin();
+       it != initial_sync_data.end(); ++it) {
+    DCHECK_EQ(MANAGED_USERS, it->GetDataType());
+    const ManagedUserSpecifics& managed_user =
+        it->GetSpecifics().managed_user();
+    DictionaryValue* value = new DictionaryValue();
+    value->SetString(kName, managed_user.name());
+    DCHECK(managed_user.acknowledged());
+    value->SetBoolean(kAcknowledged, managed_user.acknowledged());
+    value->SetString(kMasterKey, managed_user.master_key());
+    if (dict->HasKey(managed_user.id()))
+      num_items_modified++;
+    else
+      num_items_added++;
+    dict->SetWithoutPathExpansion(managed_user.id(), value);
+    seen_ids.insert(managed_user.id());
+  }
+
+  for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
+    if (seen_ids.find(it.key()) != seen_ids.end())
+      continue;
+
+    const DictionaryValue* dict = NULL;
+    bool success = it.value().GetAsDictionary(&dict);
+    DCHECK(success);
+    bool acknowledged = false;
+    dict->GetBoolean(kAcknowledged, &acknowledged);
+    std::string name;
+    dict->GetString(kName, &name);
+    std::string master_key;
+    dict->GetString(kMasterKey, &master_key);
+    DCHECK(!name.empty());
+    change_list.push_back(
+        SyncChange(FROM_HERE, SyncChange::ACTION_ADD,
+            CreateLocalSyncData(it.key(), name, acknowledged, master_key)));
+  }
+  result.set_error(sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
+
+  result.set_num_items_modified(num_items_modified);
+  result.set_num_items_added(num_items_added);
+  result.set_num_items_after_association(dict->size());
+
+  return result;
+}
+
+void ManagedUserSyncService::StopSyncing(ModelType type) {
+  DCHECK_EQ(MANAGED_USERS, type);
+  // The observers may want to change the Sync data, so notify them before
+  // resetting the |sync_processor_|.
+  NotifyManagedUsersSyncingStopped();
+  sync_processor_.reset();
+  error_handler_.reset();
+}
+
+SyncDataList ManagedUserSyncService::GetAllSyncData(
+    ModelType type) const {
+  SyncDataList data;
+  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
+  DictionaryValue* dict = update.Get();
+  for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
+    const DictionaryValue* dict = NULL;
+    bool success = it.value().GetAsDictionary(&dict);
+    DCHECK(success);
+    std::string name;
+    dict->GetString(kName, &name);
+    std::string master_key;
+    dict->GetString(kMasterKey, &master_key);
+    bool acknowledged = false;
+    dict->GetBoolean(kAcknowledged, &acknowledged);
+    data.push_back(
+        CreateLocalSyncData(it.key(), name, acknowledged, master_key));
+  }
+  return data;
+}
+
+SyncError ManagedUserSyncService::ProcessSyncChanges(
+    const tracked_objects::Location& from_here,
+    const SyncChangeList& change_list) {
+  SyncError error;
+  DictionaryPrefUpdate update(prefs_, prefs::kManagedUsers);
+  DictionaryValue* dict = update.Get();
+  for (SyncChangeList::const_iterator it = change_list.begin();
+       it != change_list.end(); ++it) {
+    SyncData data = it->sync_data();
+    DCHECK_EQ(MANAGED_USERS, data.GetDataType());
+    const ManagedUserSpecifics& managed_user =
+        data.GetSpecifics().managed_user();
+    switch (it->change_type()) {
+      case SyncChange::ACTION_ADD:
+      case SyncChange::ACTION_UPDATE: {
+        // Every item we get from the server should be acknowledged.
+        DCHECK(managed_user.acknowledged());
+        const DictionaryValue* old_value = NULL;
+        dict->GetDictionaryWithoutPathExpansion(managed_user.id(), &old_value);
+
+        // For an update action, the managed user should already exist, for an
+        // add action, it should not.
+        DCHECK_EQ(
+            old_value ? SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD,
+            it->change_type());
+
+        // If the managed user switched from unacknowledged to acknowledged,
+        // we might need to continue with a registration.
+        if (old_value && !old_value->HasKey(kAcknowledged))
+          NotifyManagedUserAcknowledged(managed_user.id());
+
+        DictionaryValue* value = new DictionaryValue;
+        value->SetString(kName, managed_user.name());
+        value->SetBoolean(kAcknowledged, managed_user.acknowledged());
+        value->SetString(kMasterKey, managed_user.master_key());
+        dict->SetWithoutPathExpansion(managed_user.id(), value);
+        break;
+      }
+      case SyncChange::ACTION_DELETE: {
+        DCHECK(dict->HasKey(managed_user.id())) << managed_user.id();
+        dict->RemoveWithoutPathExpansion(managed_user.id(), NULL);
+        break;
+      }
+      case SyncChange::ACTION_INVALID: {
+        NOTREACHED();
+        break;
+      }
+    }
+  }
+  return error;
+}
+
+void ManagedUserSyncService::OnLastSignedInUsernameChange() {
+  DCHECK(!sync_processor_);
+
+  // If the last signed in user changes, we clear all data, to avoid managed
+  // users from one custodian appearing in another one's profile.
+  prefs_->ClearPref(prefs::kManagedUsers);
+}
+
+void ManagedUserSyncService::NotifyManagedUserAcknowledged(
+    const std::string& managed_user_id) {
+  FOR_EACH_OBSERVER(ManagedUserSyncServiceObserver, observers_,
+                    OnManagedUserAcknowledged(managed_user_id));
+}
+
+void ManagedUserSyncService::NotifyManagedUsersSyncingStopped() {
+  FOR_EACH_OBSERVER(ManagedUserSyncServiceObserver, observers_,
+                    OnManagedUsersSyncingStopped());
+}
diff --git a/chrome/browser/managed_mode/managed_user_sync_service.h b/chrome/browser/managed_mode/managed_user_sync_service.h
new file mode 100644
index 0000000..1352c62
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_sync_service.h
@@ -0,0 +1,75 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_SYNC_SERVICE_H_
+#define CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_SYNC_SERVICE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/prefs/pref_change_registrar.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service_observer.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+#include "sync/api/syncable_service.h"
+
+class PrefService;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+class ManagedUserSyncService : public BrowserContextKeyedService,
+                               public syncer::SyncableService {
+ public:
+  virtual ~ManagedUserSyncService();
+
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+  void AddObserver(ManagedUserSyncServiceObserver* observer);
+  void RemoveObserver(ManagedUserSyncServiceObserver* observer);
+
+  void AddManagedUser(const std::string& id,
+                      const std::string& name,
+                      const std::string& master_key);
+  void DeleteManagedUser(const std::string& id);
+
+  // BrowserContextKeyedService implementation:
+  virtual void Shutdown() OVERRIDE;
+
+  // SyncableService implementation:
+  virtual syncer::SyncMergeResult MergeDataAndStartSyncing(
+      syncer::ModelType type,
+      const syncer::SyncDataList& initial_sync_data,
+      scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
+      scoped_ptr<syncer::SyncErrorFactory> error_handler) OVERRIDE;
+  virtual void StopSyncing(syncer::ModelType type) OVERRIDE;
+  virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
+      OVERRIDE;
+  virtual syncer::SyncError ProcessSyncChanges(
+      const tracked_objects::Location& from_here,
+      const syncer::SyncChangeList& change_list) OVERRIDE;
+
+ private:
+  friend class ManagedUserSyncServiceFactory;
+
+  // Use |ManagedUserSyncServiceFactory::GetForProfile(...)| to get an
+  // instance of this service.
+  explicit ManagedUserSyncService(PrefService* prefs);
+
+  void OnLastSignedInUsernameChange();
+
+  void NotifyManagedUserAcknowledged(const std::string& managed_user_id);
+  void NotifyManagedUsersSyncingStopped();
+
+  PrefService* prefs_;
+  PrefChangeRegistrar pref_change_registrar_;
+
+  scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
+  scoped_ptr<syncer::SyncErrorFactory> error_handler_;
+
+  ObserverList<ManagedUserSyncServiceObserver> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(ManagedUserSyncService);
+};
+
+#endif  // CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_SYNC_SERVICE_H_
diff --git a/chrome/browser/managed_mode/managed_user_sync_service_factory.cc b/chrome/browser/managed_mode/managed_user_sync_service_factory.cc
new file mode 100644
index 0000000..ca2b074
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_sync_service_factory.cc
@@ -0,0 +1,35 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/managed_mode/managed_user_sync_service_factory.h"
+
+#include "chrome/browser/managed_mode/managed_user_sync_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
+
+// static
+ManagedUserSyncService* ManagedUserSyncServiceFactory::GetForProfile(
+    Profile* profile) {
+  return static_cast<ManagedUserSyncService*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+ManagedUserSyncServiceFactory* ManagedUserSyncServiceFactory::GetInstance() {
+  return Singleton<ManagedUserSyncServiceFactory>::get();
+}
+
+ManagedUserSyncServiceFactory::ManagedUserSyncServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "ManagedUserSyncService",
+          BrowserContextDependencyManager::GetInstance()) {
+}
+
+ManagedUserSyncServiceFactory::~ManagedUserSyncServiceFactory() {}
+
+BrowserContextKeyedService*
+ManagedUserSyncServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* profile) const {
+  return new ManagedUserSyncService(static_cast<Profile*>(profile)->GetPrefs());
+}
diff --git a/chrome/browser/managed_mode/managed_user_sync_service_factory.h b/chrome/browser/managed_mode/managed_user_sync_service_factory.h
new file mode 100644
index 0000000..079f2e9
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_sync_service_factory.h
@@ -0,0 +1,32 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_SYNC_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_SYNC_SERVICE_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
+
+class ManagedUserSyncService;
+class Profile;
+
+class ManagedUserSyncServiceFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static ManagedUserSyncService* GetForProfile(Profile* profile);
+
+  static ManagedUserSyncServiceFactory* GetInstance();
+
+ private:
+  friend struct DefaultSingletonTraits<ManagedUserSyncServiceFactory>;
+
+  ManagedUserSyncServiceFactory();
+  virtual ~ManagedUserSyncServiceFactory();
+
+  // BrowserContextKeyedServiceFactory:
+  virtual BrowserContextKeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* profile) const OVERRIDE;
+};
+
+#endif  // CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_SYNC_SERVICE_FACTORY_H_
diff --git a/chrome/browser/managed_mode/managed_user_sync_service_observer.h b/chrome/browser/managed_mode/managed_user_sync_service_observer.h
new file mode 100644
index 0000000..621d841
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_sync_service_observer.h
@@ -0,0 +1,21 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_SYNC_SERVICE_OBSERVER_H_
+#define CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_SYNC_SERVICE_OBSERVER_H_
+
+class ManagedUserSyncServiceObserver {
+ public:
+  // Called when the Sync server has acknowledged a newly
+  // created managed user.
+  virtual void OnManagedUserAcknowledged(
+      const std::string& managed_user_id) = 0;
+
+  virtual void OnManagedUsersSyncingStopped() = 0;
+
+ protected:
+  virtual ~ManagedUserSyncServiceObserver() {}
+};
+
+#endif  // CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_SYNC_SERVICE_OBSERVER_H_
diff --git a/chrome/browser/managed_mode/managed_user_sync_service_unittest.cc b/chrome/browser/managed_mode/managed_user_sync_service_unittest.cc
new file mode 100644
index 0000000..7ed7765
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_sync_service_unittest.cc
@@ -0,0 +1,246 @@
+// Copyright 2013 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.
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service_factory.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "sync/api/sync_change.h"
+#include "sync/api/sync_error_factory_mock.h"
+#include "sync/protocol/sync.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sync_pb::ManagedUserSpecifics;
+using syncer::MANAGED_USERS;
+using syncer::SyncChange;
+using syncer::SyncChangeList;
+using syncer::SyncChangeProcessor;
+using syncer::SyncData;
+using syncer::SyncDataList;
+using syncer::SyncError;
+using syncer::SyncErrorFactory;
+using syncer::SyncMergeResult;
+
+namespace {
+
+class MockChangeProcessor : public SyncChangeProcessor {
+ public:
+  MockChangeProcessor() {}
+  virtual ~MockChangeProcessor() {}
+
+  // SyncChangeProcessor implementation:
+  virtual SyncError ProcessSyncChanges(
+      const tracked_objects::Location& from_here,
+      const SyncChangeList& change_list) OVERRIDE;
+
+  const SyncChangeList& changes() const { return change_list_; }
+  SyncChange GetChange(const std::string& id) const;
+
+ private:
+  SyncChangeList change_list_;
+};
+
+SyncError MockChangeProcessor::ProcessSyncChanges(
+    const tracked_objects::Location& from_here,
+    const SyncChangeList& change_list) {
+  change_list_ = change_list;
+  return SyncError();
+}
+
+SyncChange MockChangeProcessor::GetChange(const std::string& id) const {
+  for (SyncChangeList::const_iterator it = change_list_.begin();
+       it != change_list_.end(); ++it) {
+    if (it->sync_data().GetSpecifics().managed_user().id() == id)
+      return *it;
+  }
+  return SyncChange();
+}
+
+}  // namespace
+
+class ManagedUserSyncServiceTest : public ::testing::Test {
+ public:
+  ManagedUserSyncServiceTest();
+  virtual ~ManagedUserSyncServiceTest();
+
+ protected:
+  scoped_ptr<SyncChangeProcessor> CreateChangeProcessor();
+  scoped_ptr<SyncErrorFactory> CreateErrorFactory();
+  SyncData CreateRemoteData(const std::string& id, const std::string& name);
+
+  SyncMergeResult StartInitialSync();
+
+  void Acknowledge();
+  void ResetService();
+
+  PrefService* prefs() { return profile_.GetPrefs(); }
+  ManagedUserSyncService* service() { return service_; }
+  MockChangeProcessor* change_processor() { return change_processor_; }
+
+ private:
+  TestingProfile profile_;
+  ManagedUserSyncService* service_;
+
+  // Owned by the ManagedUserSyncService.
+  MockChangeProcessor* change_processor_;
+
+  // A unique ID for creating "remote" Sync data.
+  int64 sync_data_id_;
+};
+
+ManagedUserSyncServiceTest::ManagedUserSyncServiceTest()
+    : change_processor_(NULL),
+      sync_data_id_(0) {
+  service_ = ManagedUserSyncServiceFactory::GetForProfile(&profile_);
+}
+
+ManagedUserSyncServiceTest::~ManagedUserSyncServiceTest() {}
+
+scoped_ptr<SyncChangeProcessor>
+ManagedUserSyncServiceTest::CreateChangeProcessor() {
+  EXPECT_FALSE(change_processor_);
+  change_processor_ = new MockChangeProcessor();
+  return scoped_ptr<SyncChangeProcessor>(change_processor_);
+}
+
+scoped_ptr<SyncErrorFactory>
+ManagedUserSyncServiceTest::CreateErrorFactory() {
+  return scoped_ptr<SyncErrorFactory>(new syncer::SyncErrorFactoryMock());
+}
+
+SyncData ManagedUserSyncServiceTest::CreateRemoteData(
+    const std::string& id,
+    const std::string& name) {
+  ::sync_pb::EntitySpecifics specifics;
+  specifics.mutable_managed_user()->set_id(id);
+  specifics.mutable_managed_user()->set_name(name);
+  specifics.mutable_managed_user()->set_acknowledged(true);
+  return SyncData::CreateRemoteData(++sync_data_id_, specifics, base::Time());
+}
+
+SyncMergeResult ManagedUserSyncServiceTest::StartInitialSync() {
+  SyncDataList initial_sync_data;
+  SyncMergeResult result =
+      service()->MergeDataAndStartSyncing(MANAGED_USERS,
+                                          initial_sync_data,
+                                          CreateChangeProcessor(),
+                                          CreateErrorFactory());
+  EXPECT_FALSE(result.error().IsSet());
+  return result;
+}
+
+void ManagedUserSyncServiceTest::ResetService() {
+  service_->StopSyncing(MANAGED_USERS);
+  service_->Shutdown();
+}
+
+TEST_F(ManagedUserSyncServiceTest, MergeEmpty) {
+  SyncMergeResult result = StartInitialSync();
+  EXPECT_EQ(0, result.num_items_added());
+  EXPECT_EQ(0, result.num_items_modified());
+  EXPECT_EQ(0, result.num_items_deleted());
+  EXPECT_EQ(0, result.num_items_before_association());
+  EXPECT_EQ(0, result.num_items_after_association());
+  EXPECT_EQ(0u, prefs()->GetDictionary(prefs::kManagedUsers)->size());
+  EXPECT_EQ(0u, change_processor()->changes().size());
+
+  ResetService();
+}
+
+TEST_F(ManagedUserSyncServiceTest, MergeExisting) {
+  const char kNameKey[] = "name";
+  const char kAcknowledgedKey[] = "acknowledged";
+
+  const char kUserId1[] = "aaaaa";
+  const char kUserId2[] = "bbbbb";
+  const char kUserId3[] = "ccccc";
+  const char kUserId4[] = "ddddd";
+  const char kName1[] = "Anchor";
+  const char kName2[] = "Buzz";
+  const char kName3[] = "Crush";
+  const char kName4[] = "Dory";
+  {
+    DictionaryPrefUpdate update(prefs(), prefs::kManagedUsers);
+    DictionaryValue* managed_users = update.Get();
+    DictionaryValue* dict = new DictionaryValue;
+    dict->SetString(kNameKey, kName1);
+    managed_users->Set(kUserId1, dict);
+    dict = new DictionaryValue;
+    dict->SetString(kNameKey, kName2);
+    dict->SetBoolean(kAcknowledgedKey, true);
+    managed_users->Set(kUserId2, dict);
+  }
+
+  SyncDataList initial_sync_data;
+  initial_sync_data.push_back(CreateRemoteData(kUserId2, kName2));
+  initial_sync_data.push_back(CreateRemoteData(kUserId3, kName3));
+  initial_sync_data.push_back(CreateRemoteData(kUserId4, kName4));
+
+  SyncMergeResult result =
+      service()->MergeDataAndStartSyncing(MANAGED_USERS,
+                                          initial_sync_data,
+                                          CreateChangeProcessor(),
+                                          CreateErrorFactory());
+  EXPECT_FALSE(result.error().IsSet());
+  EXPECT_EQ(2, result.num_items_added());
+  EXPECT_EQ(1, result.num_items_modified());
+  EXPECT_EQ(0, result.num_items_deleted());
+  EXPECT_EQ(2, result.num_items_before_association());
+  EXPECT_EQ(4, result.num_items_after_association());
+
+  const DictionaryValue* managed_users =
+      prefs()->GetDictionary(prefs::kManagedUsers);
+  EXPECT_EQ(4u, managed_users->size());
+  {
+    const DictionaryValue* managed_user = NULL;
+    ASSERT_TRUE(managed_users->GetDictionary(kUserId2, &managed_user));
+    ASSERT_TRUE(managed_user);
+    std::string name;
+    EXPECT_TRUE(managed_user->GetString(kNameKey, &name));
+    EXPECT_EQ(kName2, name);
+    bool acknowledged = false;
+    EXPECT_TRUE(managed_user->GetBoolean(kAcknowledgedKey, &acknowledged));
+    EXPECT_TRUE(acknowledged);
+  }
+  {
+    const DictionaryValue* managed_user = NULL;
+    ASSERT_TRUE(managed_users->GetDictionary(kUserId3, &managed_user));
+    ASSERT_TRUE(managed_user);
+    std::string name;
+    EXPECT_TRUE(managed_user->GetString(kNameKey, &name));
+    EXPECT_EQ(kName3, name);
+    bool acknowledged = false;
+    EXPECT_TRUE(managed_user->GetBoolean(kAcknowledgedKey, &acknowledged));
+    EXPECT_TRUE(acknowledged);
+  }
+  {
+    const DictionaryValue* managed_user = NULL;
+    ASSERT_TRUE(managed_users->GetDictionary(kUserId4, &managed_user));
+    ASSERT_TRUE(managed_user);
+    std::string name;
+    EXPECT_TRUE(managed_user->GetString(kNameKey, &name));
+    EXPECT_EQ(kName4, name);
+    bool acknowledged = false;
+    EXPECT_TRUE(managed_user->GetBoolean(kAcknowledgedKey, &acknowledged));
+    EXPECT_TRUE(acknowledged);
+  }
+
+  EXPECT_EQ(1u, change_processor()->changes().size());
+  {
+    SyncChange change = change_processor()->GetChange(kUserId1);
+    ASSERT_TRUE(change.IsValid());
+    EXPECT_EQ(SyncChange::ACTION_ADD, change.change_type());
+    const ManagedUserSpecifics& managed_user =
+        change.sync_data().GetSpecifics().managed_user();
+    EXPECT_EQ(kName1, managed_user.name());
+    EXPECT_FALSE(managed_user.acknowledged());
+  }
+}
diff --git a/chrome/browser/media/DEPS b/chrome/browser/media/DEPS
index 05f22d2..2b87894 100644
--- a/chrome/browser/media/DEPS
+++ b/chrome/browser/media/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+media/base",
+  "+third_party/webrtc",
   "+third_party/zlib"
 ]
diff --git a/chrome/browser/media/OWNERS b/chrome/browser/media/OWNERS
index c15b1b5..fba96a5 100644
--- a/chrome/browser/media/OWNERS
+++ b/chrome/browser/media/OWNERS
@@ -3,6 +3,7 @@
 ddorwin@chromium.org
 fischman@chromium.org
 scherkus@chromium.org
+sergeyu@chromium.org
 shadi@chromium.org
 tommi@chromium.org
 vrk@chromium.org
diff --git a/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc b/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc
index 6579636..b7eff1f 100644
--- a/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc
+++ b/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/file_util.h"
 #include "base/path_service.h"
 #include "base/strings/stringprintf.h"
@@ -18,22 +19,20 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/content_settings_types.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/ui/ui_test.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 static const char kMainWebrtcTestHtmlPage[] =
     "files/webrtc/webrtc_jsep01_test.html";
 
 // Media stream infobar test for WebRTC.
 class MediaStreamInfobarTest : public WebRtcTestBase {
  public:
+  // InProcessBrowserTest:
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     // This test expects to run with fake devices but real UI.
     command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
@@ -69,7 +68,7 @@
                        TestAcceptThenDenyWhichShouldBeSticky) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/media/chrome_midi_permission_context.cc b/chrome/browser/media/chrome_midi_permission_context.cc
new file mode 100644
index 0000000..c233400
--- /dev/null
+++ b/chrome/browser/media/chrome_midi_permission_context.cc
@@ -0,0 +1,131 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/media/chrome_midi_permission_context.h"
+
+#include "base/prefs/pref_service.h"
+#include "chrome/browser/content_settings/host_content_settings_map.h"
+#include "chrome/browser/content_settings/permission_queue_controller.h"
+#include "chrome/browser/content_settings/permission_request_id.h"
+#include "chrome/browser/content_settings/tab_specific_content_settings.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+
+ChromeMIDIPermissionContext::ChromeMIDIPermissionContext(Profile* profile)
+    : profile_(profile),
+      shutting_down_(false) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+}
+
+ChromeMIDIPermissionContext::~ChromeMIDIPermissionContext() {
+  DCHECK(!permission_queue_controller_);
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+}
+
+void ChromeMIDIPermissionContext::Shutdown() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  permission_queue_controller_.reset();
+  shutting_down_ = true;
+}
+
+void ChromeMIDIPermissionContext::RequestMIDISysExPermission(
+    int render_process_id,
+    int render_view_id,
+    const GURL& requesting_frame,
+    const content::BrowserContext::MIDISysExPermissionCallback& callback) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK(!shutting_down_);
+
+  // TODO(toyoshim): Support Extension's manifest declared permission.
+  // http://crbug.com/266338 .
+
+  content::WebContents* web_contents =
+    tab_util::GetWebContentsByID(render_process_id, render_view_id);
+
+  // The page doesn't exist any more.
+  if (!web_contents)
+    return;
+
+  const PermissionRequestID id(render_process_id, render_view_id, 0);
+
+  GURL embedder = web_contents->GetURL();
+  if (!requesting_frame.is_valid() || !embedder.is_valid()) {
+    LOG(WARNING) << "Attempt to use MIDI sysex from an invalid URL: "
+                 << requesting_frame << "," << embedder
+                 << " (Web MIDI is not supported in popups)";
+    PermissionDecided(id, requesting_frame, embedder, callback, false);
+    return;
+  }
+
+  DecidePermission(id, requesting_frame, embedder, callback);
+}
+
+void ChromeMIDIPermissionContext::DecidePermission(
+    const PermissionRequestID& id,
+    const GURL& requesting_frame,
+    const GURL& embedder,
+    const content::BrowserContext::MIDISysExPermissionCallback& callback) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  ContentSetting content_setting =
+      profile_->GetHostContentSettingsMap()->GetContentSetting(
+          requesting_frame,
+          embedder,
+          CONTENT_SETTINGS_TYPE_MIDI_SYSEX,
+          std::string());
+  switch (content_setting) {
+    case CONTENT_SETTING_BLOCK:
+      PermissionDecided(id, requesting_frame, embedder, callback, false);
+      break;
+    case CONTENT_SETTING_ALLOW:
+      PermissionDecided(id, requesting_frame, embedder, callback, true);
+      break;
+    default:
+      GetQueueController()->CreateInfoBarRequest(
+          id, requesting_frame, embedder, base::Bind(
+              &ChromeMIDIPermissionContext::NotifyPermissionSet,
+              base::Unretained(this), id, requesting_frame, callback));
+  }
+}
+
+void ChromeMIDIPermissionContext::PermissionDecided(
+    const PermissionRequestID& id,
+    const GURL& requesting_frame,
+    const GURL& embedder,
+    const content::BrowserContext::MIDISysExPermissionCallback& callback,
+    bool allowed) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  NotifyPermissionSet(id, requesting_frame, callback, allowed);
+}
+
+void ChromeMIDIPermissionContext::NotifyPermissionSet(
+    const PermissionRequestID& id,
+    const GURL& requesting_frame,
+    const content::BrowserContext::MIDISysExPermissionCallback& callback,
+    bool allowed) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  TabSpecificContentSettings* content_settings =
+      TabSpecificContentSettings::Get(id.render_process_id(),
+                                      id.render_view_id());
+  if (content_settings) {
+    if (allowed)
+      content_settings->OnMIDISysExAccessed(requesting_frame);
+    else
+      content_settings->OnMIDISysExAccessBlocked(requesting_frame);
+  }
+
+  callback.Run(allowed);
+}
+
+PermissionQueueController* ChromeMIDIPermissionContext::GetQueueController() {
+  if (!permission_queue_controller_) {
+    permission_queue_controller_.reset(
+        new PermissionQueueController(profile_,
+                                      CONTENT_SETTINGS_TYPE_MIDI_SYSEX));
+  }
+  return permission_queue_controller_.get();
+}
diff --git a/chrome/browser/media/chrome_midi_permission_context.h b/chrome/browser/media/chrome_midi_permission_context.h
new file mode 100644
index 0000000..7f2ff32
--- /dev/null
+++ b/chrome/browser/media/chrome_midi_permission_context.h
@@ -0,0 +1,69 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MEDIA_CHROME_MIDI_PERMISSION_CONTEXT_H_
+#define CHROME_BROWSER_MEDIA_CHROME_MIDI_PERMISSION_CONTEXT_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+#include "content/public/browser/browser_context.h"
+
+class GURL;
+class PermissionQueueController;
+class PermissionRequestID;
+class Profile;
+
+// This class manages MIDI permissions flow. Used on the UI thread.
+class ChromeMIDIPermissionContext : public BrowserContextKeyedService {
+ public:
+  explicit ChromeMIDIPermissionContext(Profile* profile);
+  virtual ~ChromeMIDIPermissionContext();
+
+  // BrowserContextKeyedService methods:
+  virtual void Shutdown() OVERRIDE;
+
+  // Request to ask users permission about MIDI.
+  void RequestMIDISysExPermission(
+      int render_process_id,
+      int render_view_id,
+      const GURL& requesting_frame,
+      const content::BrowserContext::MIDISysExPermissionCallback& callback);
+
+ private:
+  // Decide whether the permission should be granted.
+  // Calls PermissionDecided if permission can be decided non-interactively,
+  // or NotifyPermissionSet if permission decided by presenting an infobar.
+  void DecidePermission(
+      const PermissionRequestID& id,
+      const GURL& requesting_frame,
+      const GURL& embedder,
+      const content::BrowserContext::MIDISysExPermissionCallback& callback);
+
+  // Called when permission is granted without interactively asking the user.
+  void PermissionDecided(
+      const PermissionRequestID& id,
+      const GURL& requesting_frame,
+      const GURL& embedder,
+      const content::BrowserContext::MIDISysExPermissionCallback& callback,
+      bool allowed);
+
+  // Called when the permission decision is made. It may be by the
+  // InfoBarDelegate to notify permission has been set.
+  void NotifyPermissionSet(
+      const PermissionRequestID& id,
+      const GURL& requesting_frame,
+      const content::BrowserContext::MIDISysExPermissionCallback& callback,
+      bool allowed);
+
+  // Return an instance of the infobar queue controller, creating it if needed.
+  PermissionQueueController* GetQueueController();
+
+  Profile* const profile_;
+  bool shutting_down_;
+  scoped_ptr<PermissionQueueController> permission_queue_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeMIDIPermissionContext);
+};
+
+#endif  // CHROME_BROWSER_MEDIA_CHROME_MIDI_PERMISSION_CONTEXT_H_
diff --git a/chrome/browser/media/chrome_midi_permission_context_factory.cc b/chrome/browser/media/chrome_midi_permission_context_factory.cc
new file mode 100644
index 0000000..85b0c79
--- /dev/null
+++ b/chrome/browser/media/chrome_midi_permission_context_factory.cc
@@ -0,0 +1,46 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/media/chrome_midi_permission_context_factory.h"
+
+#include "chrome/browser/media/chrome_midi_permission_context.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
+
+// static
+ChromeMIDIPermissionContext*
+ChromeMIDIPermissionContextFactory::GetForProfile(Profile* profile) {
+  return static_cast<ChromeMIDIPermissionContext*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+ChromeMIDIPermissionContextFactory*
+ChromeMIDIPermissionContextFactory::GetInstance() {
+  return Singleton<ChromeMIDIPermissionContextFactory>::get();
+}
+
+ChromeMIDIPermissionContextFactory::
+ChromeMIDIPermissionContextFactory()
+    : BrowserContextKeyedServiceFactory(
+          "ChromeMIDIPermissionContext",
+          BrowserContextDependencyManager::GetInstance()) {
+}
+
+ChromeMIDIPermissionContextFactory::
+~ChromeMIDIPermissionContextFactory() {
+}
+
+BrowserContextKeyedService*
+ChromeMIDIPermissionContextFactory::BuildServiceInstanceFor(
+    content::BrowserContext* profile) const {
+  return new ChromeMIDIPermissionContext(static_cast<Profile*>(profile));
+}
+
+content::BrowserContext*
+ChromeMIDIPermissionContextFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextOwnInstanceInIncognito(context);
+}
diff --git a/chrome/browser/media/chrome_midi_permission_context_factory.h b/chrome/browser/media/chrome_midi_permission_context_factory.h
new file mode 100644
index 0000000..2980169
--- /dev/null
+++ b/chrome/browser/media/chrome_midi_permission_context_factory.h
@@ -0,0 +1,35 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MEDIA_CHROME_MIDI_PERMISSION_CONTEXT_FACTORY_H_
+#define CHROME_BROWSER_MEDIA_CHROME_MIDI_PERMISSION_CONTEXT_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
+
+class ChromeMIDIPermissionContext;
+class Profile;
+
+class ChromeMIDIPermissionContextFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static ChromeMIDIPermissionContext* GetForProfile(Profile* profile);
+  static ChromeMIDIPermissionContextFactory* GetInstance();
+
+ private:
+  friend struct DefaultSingletonTraits<ChromeMIDIPermissionContextFactory>;
+
+  ChromeMIDIPermissionContextFactory();
+  virtual ~ChromeMIDIPermissionContextFactory();
+
+  // BrowserContextKeyedBaseFactory methods:
+  virtual BrowserContextKeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* profile) const OVERRIDE;
+  virtual content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const OVERRIDE;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeMIDIPermissionContextFactory);
+};
+
+#endif  // CHROME_BROWSER_MEDIA_CHROME_MIDI_PERMISSION_CONTEXT_FACTORY_H_
diff --git a/chrome/browser/media/desktop_media_picker.h b/chrome/browser/media/desktop_media_picker.h
new file mode 100644
index 0000000..8720698
--- /dev/null
+++ b/chrome/browser/media/desktop_media_picker.h
@@ -0,0 +1,41 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_PICKER_H_
+#define CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_PICKER_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/media/desktop_media_picker_model.h"
+#include "ui/gfx/native_widget_types.h"
+
+
+// Abstract interface for desktop media picker UI. It's used by Desktop Media
+// API to let user choose a desktop media source.
+class DesktopMediaPicker {
+ public:
+  typedef base::Callback<void(DesktopMediaPickerModel::SourceId)> DoneCallback;
+
+  // Creates default implementation of DesktopMediaPicker for the current
+  // platform.
+  static scoped_ptr<DesktopMediaPicker> Create();
+
+  DesktopMediaPicker() {}
+  virtual ~DesktopMediaPicker() {}
+
+  // Shows dialog with list of desktop media sources (screens, windows, tabs)
+  // provided by |model| and calls |done_callback| when user chooses one of the
+  // sources or closes the dialog.
+  virtual void Show(gfx::NativeWindow context,
+                    gfx::NativeWindow parent,
+                    const string16& app_name,
+                    scoped_ptr<DesktopMediaPickerModel> model,
+                    const DoneCallback& done_callback) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DesktopMediaPicker);
+};
+
+#endif  // CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_PICKER_H_
diff --git a/chrome/browser/media/desktop_media_picker_model.cc b/chrome/browser/media/desktop_media_picker_model.cc
new file mode 100644
index 0000000..37f4959
--- /dev/null
+++ b/chrome/browser/media/desktop_media_picker_model.cc
@@ -0,0 +1,340 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/media/desktop_media_picker_model.h"
+
+#include <map>
+
+#include "base/hash.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "content/public/browser/browser_thread.h"
+#include "grit/generated_resources.h"
+#include "media/base/video_util.h"
+#include "third_party/libyuv/include/libyuv/scale_argb.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
+#include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/skia_util.h"
+
+using content::BrowserThread;
+
+namespace {
+
+// Update the list every second.
+const int kDefaultUpdatePeriod = 1000;
+
+// Returns a hash of a DesktopFrame content to detect when image for a desktop
+// media source has changed.
+uint32 GetFrameHash(webrtc::DesktopFrame* frame) {
+  int data_size = frame->stride() * frame->size().height();
+  return base::SuperFastHash(reinterpret_cast<char*>(frame->data()), data_size);
+}
+
+gfx::ImageSkia ScaleDesktopFrame(scoped_ptr<webrtc::DesktopFrame> frame,
+                                 gfx::Size size) {
+  gfx::Rect scaled_rect = media::ComputeLetterboxRegion(
+      gfx::Rect(0, 0, size.width(), size.height()),
+      gfx::Size(frame->size().width(), frame->size().height()));
+
+  SkBitmap result;
+  result.setConfig(SkBitmap::kARGB_8888_Config,
+                   scaled_rect.width(), scaled_rect.height());
+  result.allocPixels();
+  result.lockPixels();
+
+  uint8* pixels_data = reinterpret_cast<uint8*>(result.getPixels());
+  libyuv::ARGBScale(frame->data(), frame->stride(),
+                    frame->size().width(), frame->size().height(),
+                    pixels_data, result.rowBytes(),
+                    scaled_rect.width(), scaled_rect.height(),
+                    libyuv::kFilterBilinear);
+
+  // Set alpha channel values to 255 for all pixels.
+  // TODO(sergeyu): Fix screen/window capturers to capture alpha channel and
+  // remove this code. Currently screen/window capturers (at least some
+  // implementations) only capture R, G and B channels and set Alpha to 0.
+  // crbug.com/264424
+  for (int y = 0; y < result.height(); ++y) {
+    for (int x = 0; x < result.width(); ++x) {
+      pixels_data[result.rowBytes() * y + x * result.bytesPerPixel() + 3] =
+          0xff;
+    }
+  }
+
+  result.unlockPixels();
+  result.setIsOpaque(true);
+
+  return gfx::ImageSkia::CreateFrom1xBitmap(result);
+}
+
+}  // namespace
+
+DesktopMediaPickerModel::SourceId::SourceId()
+    : type(content::MEDIA_NO_SERVICE),
+      id(0) {
+}
+
+DesktopMediaPickerModel::SourceId::SourceId(content::MediaStreamType type,
+                                            intptr_t id)
+    : type(type),
+      id(id) {
+}
+
+DesktopMediaPickerModel::Source::Source(SourceId id, const string16 name)
+    : id(id),
+      name(name) {
+}
+
+bool DesktopMediaPickerModel::SourceId::operator<(
+    const DesktopMediaPickerModel::SourceId& other) const {
+  return type < other.type || (type == other.type && id < other.id);
+}
+
+bool DesktopMediaPickerModel::SourceId::operator==(
+    const DesktopMediaPickerModel::SourceId& other) const {
+  return type == other.type && id == other.id;
+}
+
+DesktopMediaPickerModel::SourceDescription::SourceDescription(
+    DesktopMediaPickerModel::SourceId id,
+    const string16& name)
+    : id(id),
+      name(name) {
+}
+
+class DesktopMediaPickerModel::Worker
+    : public webrtc::DesktopCapturer::Callback {
+ public:
+  Worker(base::WeakPtr<DesktopMediaPickerModel> model,
+         scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
+         scoped_ptr<webrtc::WindowCapturer> window_capturer);
+  virtual ~Worker();
+
+  void Refresh(const gfx::Size& thumbnail_size);
+
+ private:
+  typedef std::map<SourceId, uint32> ImageHashesMap;
+
+  // webrtc::DesktopCapturer::Callback interface.
+  virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE;
+  virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE;
+
+  base::WeakPtr<DesktopMediaPickerModel> model_;
+
+  scoped_ptr<webrtc::ScreenCapturer> screen_capturer_;
+  scoped_ptr<webrtc::WindowCapturer> window_capturer_;
+
+  scoped_ptr<webrtc::DesktopFrame> current_frame_;
+
+  ImageHashesMap image_hashes_;
+
+  DISALLOW_COPY_AND_ASSIGN(Worker);
+};
+
+DesktopMediaPickerModel::Worker::Worker(
+    base::WeakPtr<DesktopMediaPickerModel> model,
+    scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
+    scoped_ptr<webrtc::WindowCapturer> window_capturer)
+    : model_(model),
+      screen_capturer_(screen_capturer.Pass()),
+      window_capturer_(window_capturer.Pass()) {
+  if (screen_capturer_)
+    screen_capturer_->Start(this);
+  if (window_capturer_)
+    window_capturer_->Start(this);
+}
+
+DesktopMediaPickerModel::Worker::~Worker() {}
+
+void DesktopMediaPickerModel::Worker::Refresh(const gfx::Size& thumbnail_size) {
+  std::vector<SourceDescription> sources;
+
+  if (screen_capturer_) {
+    // TODO(sergeyu): Enumerate each screen when ScreenCapturer supports it.
+    sources.push_back(SourceDescription(SourceId(
+        content::MEDIA_SCREEN_VIDEO_CAPTURE, 0),
+        l10n_util::GetStringUTF16(IDS_DESKTOP_MEDIA_PICKER_SCREEN_NAME)));
+  }
+
+  if (window_capturer_) {
+    webrtc::WindowCapturer::WindowList windows;
+    if (window_capturer_->GetWindowList(&windows)) {
+      for (webrtc::WindowCapturer::WindowList::iterator it = windows.begin();
+           it != windows.end(); ++it) {
+        sources.push_back(SourceDescription(
+            DesktopMediaPickerModel::SourceId(
+                content::MEDIA_WINDOW_VIDEO_CAPTURE, it->id),
+            base::UTF8ToUTF16(it->title)));
+      }
+    }
+  }
+
+  // Sort the list of sources so that they appear in a predictable order.
+  std::sort(sources.begin(), sources.end(), CompareSources);
+
+  // Update list of windows before updating thumbnails.
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&DesktopMediaPickerModel::OnSourcesList, model_, sources));
+
+  ImageHashesMap new_image_hashes;
+
+  // Get a thumbnail for each source.
+  for (size_t i = 0; i < sources.size(); ++i) {
+    SourceDescription& source = sources[i];
+    switch (source.id.type) {
+      case content::MEDIA_SCREEN_VIDEO_CAPTURE:
+        screen_capturer_->Capture(webrtc::DesktopRegion());
+        DCHECK(current_frame_);
+        break;
+
+      case content::MEDIA_WINDOW_VIDEO_CAPTURE:
+        if (!window_capturer_->SelectWindow(source.id.id))
+          continue;
+        window_capturer_->Capture(webrtc::DesktopRegion());
+        break;
+
+      default:
+        NOTREACHED();
+    }
+
+    // Expect that DesktopCapturer to always captures frames synchronously.
+    // |current_frame_| may be NULL if capture failed (e.g. because window has
+    // been closed).
+    if (current_frame_) {
+      uint32 frame_hash = GetFrameHash(current_frame_.get());
+      new_image_hashes[source.id] = frame_hash;
+
+      // Scale the image only if it has changed.
+      ImageHashesMap::iterator it = image_hashes_.find(source.id);
+      if (it == image_hashes_.end() || it->second != frame_hash) {
+        gfx::ImageSkia thumbnail =
+            ScaleDesktopFrame(current_frame_.Pass(), thumbnail_size);
+        BrowserThread::PostTask(
+            BrowserThread::UI, FROM_HERE,
+            base::Bind(&DesktopMediaPickerModel::OnSourceThumbnail, model_,
+                       i, thumbnail));
+      }
+    }
+  }
+
+  image_hashes_.swap(new_image_hashes);
+
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&DesktopMediaPickerModel::OnRefreshFinished, model_));
+}
+
+webrtc::SharedMemory* DesktopMediaPickerModel::Worker::CreateSharedMemory(
+    size_t size) {
+  return NULL;
+}
+
+void DesktopMediaPickerModel::Worker::OnCaptureCompleted(
+    webrtc::DesktopFrame* frame) {
+  current_frame_.reset(frame);
+}
+
+DesktopMediaPickerModel::DesktopMediaPickerModel()
+    : update_period_(base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)),
+      thumbnail_size_(100, 100),
+      observer_(NULL),
+      weak_factory_(this) {
+  base::SequencedWorkerPool* worker_pool = BrowserThread::GetBlockingPool();
+  capture_task_runner_ = worker_pool->GetSequencedTaskRunner(
+      worker_pool->GetSequenceToken());
+}
+
+DesktopMediaPickerModel::~DesktopMediaPickerModel() {
+  capture_task_runner_->DeleteSoon(FROM_HERE, worker_.release());
+}
+
+void DesktopMediaPickerModel::SetCapturers(
+    scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
+    scoped_ptr<webrtc::WindowCapturer> window_capturer) {
+  DCHECK(!observer_);
+  screen_capturer_ = screen_capturer.Pass();
+  window_capturer_ = window_capturer.Pass();
+}
+
+void DesktopMediaPickerModel::SetUpdatePeriod(base::TimeDelta period) {
+  DCHECK(!observer_);
+  update_period_ = period;
+}
+
+void DesktopMediaPickerModel::SetThumbnailSize(
+    const gfx::Size& thumbnail_size) {
+  thumbnail_size_ = thumbnail_size;
+}
+
+void DesktopMediaPickerModel::StartUpdating(Observer* observer) {
+  DCHECK(!observer_);
+  DCHECK(screen_capturer_ || window_capturer_);
+
+  observer_ = observer;
+
+  worker_.reset(new Worker(weak_factory_.GetWeakPtr(),
+                           screen_capturer_.Pass(), window_capturer_.Pass()));
+  Refresh();
+}
+
+// static
+bool DesktopMediaPickerModel::CompareSources(const SourceDescription& a,
+                                             const SourceDescription& b) {
+  return a.id < b.id;
+}
+
+void DesktopMediaPickerModel::Refresh() {
+  capture_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&Worker::Refresh, base::Unretained(worker_.get()),
+                            thumbnail_size_));
+}
+
+void DesktopMediaPickerModel::OnSourcesList(
+    const std::vector<SourceDescription>& new_sources) {
+  // Step through |new_sources| adding and removing entries from |sources_|, and
+  // notifying the |observer_|, until two match. Requires that |sources| and
+  // |sources_| have the same ordering.
+  size_t pos = 0;
+  while (pos < sources_.size() || pos < new_sources.size()) {
+    // If |sources_[pos]| is not in |new_sources| then remove it.
+    if (pos < sources_.size() &&
+        (pos == new_sources.size() || sources_[pos].id < new_sources[pos].id)) {
+      sources_.erase(sources_.begin() + pos);
+      observer_->OnSourceRemoved(pos);
+      continue;
+    }
+
+    if (pos == sources_.size() || !(sources_[pos].id == new_sources[pos].id)) {
+      sources_.insert(sources_.begin() + pos,
+                      Source(new_sources[pos].id, new_sources[pos].name));
+      observer_->OnSourceAdded(pos);
+    } else if (sources_[pos].name != new_sources[pos].name) {
+      sources_[pos].name = new_sources[pos].name;
+      observer_->OnSourceNameChanged(pos);
+    }
+
+    ++pos;
+  }
+
+  DCHECK_EQ(new_sources.size(), sources_.size());
+}
+
+void DesktopMediaPickerModel::OnSourceThumbnail(int index,
+                                                const gfx::ImageSkia& image) {
+  DCHECK_LT(index, static_cast<int>(sources_.size()));
+  sources_[index].thumbnail = image;
+  observer_->OnSourceThumbnailChanged(index);
+}
+
+void DesktopMediaPickerModel::OnRefreshFinished() {
+  BrowserThread::PostDelayedTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&DesktopMediaPickerModel::Refresh, weak_factory_.GetWeakPtr()),
+      update_period_);
+}
diff --git a/chrome/browser/media/desktop_media_picker_model.h b/chrome/browser/media/desktop_media_picker_model.h
new file mode 100644
index 0000000..8dfe9b7
--- /dev/null
+++ b/chrome/browser/media/desktop_media_picker_model.h
@@ -0,0 +1,153 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_PICKER_MODEL_H_
+#define CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_PICKER_MODEL_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "content/public/common/media_stream_request.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace webrtc {
+class ScreenCapturer;
+class WindowCapturer;
+}
+
+// DesktopMediaPickerModel provides the list of desktop media source (screens,
+// windows, tabs), and their thumbnails, to the desktop media picker dialog. It
+// transparently updates the list in the background, and notifies the desktop
+// media picker when something changes.
+class DesktopMediaPickerModel {
+ public:
+  // Interface implemented by the picker dialog to receive notifications when
+  // the model's contents change.
+  class Observer {
+   public:
+    virtual ~Observer() {}
+
+    virtual void OnSourceAdded(int index) = 0;
+    virtual void OnSourceRemoved(int index) = 0;
+    virtual void OnSourceNameChanged(int index) = 0;
+    virtual void OnSourceThumbnailChanged(int index) = 0;
+  };
+
+  // Type used to identify desktop media sources.
+  struct SourceId {
+    SourceId();
+    SourceId(content::MediaStreamType type, intptr_t id);
+    bool operator<(const SourceId& other) const;
+    bool operator==(const SourceId& other) const;
+
+    content::MediaStreamType type;
+    intptr_t id;
+  };
+
+  // Struct used to represent each entry in the model.
+  struct Source {
+    Source(SourceId id, const string16 name);
+
+    // Id of the source.
+    SourceId id;
+
+    // Name of the source that should be shown to the user.
+    string16 name;
+
+    // The thumbnail for the source.
+    gfx::ImageSkia thumbnail;
+  };
+
+  DesktopMediaPickerModel();
+  virtual ~DesktopMediaPickerModel();
+
+  // Sets screen/window capturer implementations to use (e.g. for tests). Caller
+  // may pass NULL for either of the arguments in case when only some types of
+  // sources the model should be populated with (e.g. it will only contain
+  // windows, if |screen_capturer| is NULL). Must be called before
+  // StartUpdating().
+  void SetCapturers(scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
+                    scoped_ptr<webrtc::WindowCapturer> window_capturer);
+
+  // Sets time interval between updates. By default list of sources and their
+  // thumbnail are updated once per second. If called after StartUpdating() then
+  // it will take effect only after the next update.
+  void SetUpdatePeriod(base::TimeDelta period);
+
+  // Sets size to which the thumbnails should be scaled. If called after
+  // StartUpdating() then some thumbnails may be still scaled to the old size
+  // until they are updated.
+  void SetThumbnailSize(const gfx::Size& thumbnail_size);
+
+  // Starts updating the model. The model is initially empty, so OnSourceAdded()
+  // notifications will be generated for each existing source as it is
+  // enumerated. After the initial enumeration the model will be refreshed based
+  // on the update period, and notifications generated only for changes in the
+  // model.
+  void StartUpdating(Observer* observer);
+
+  int source_count() const { return sources_.size(); }
+  const Source& source(int index) const { return sources_.at(index); }
+
+ private:
+  class Worker;
+  friend class Worker;
+
+  // Struct used to represent sources list the model gets from the Worker.
+  struct SourceDescription {
+    SourceDescription(SourceId id, const string16& name);
+
+    SourceId id;
+    string16 name;
+  };
+
+  // Order comparator for sources. Used to sort list of sources.
+  static bool CompareSources(const SourceDescription& a,
+                             const SourceDescription& b);
+
+  // Post a task for the |worker_| to update list of windows and get thumbnails.
+  void Refresh();
+
+  // Called by |worker_| to refresh the model. First it posts tasks for
+  // OnSourcesList() with the fresh list of sources, then follows with
+  // OnSourceThumbnail() for each changed thumbnail and then calls
+  // OnRefreshFinished() at the end.
+  void OnSourcesList(const std::vector<SourceDescription>& sources);
+  void OnSourceThumbnail(int index, const gfx::ImageSkia& thumbnail);
+  void OnRefreshFinished();
+
+  // Flags passed to the constructor.
+  int flags_;
+
+  // Capturers specified in SetCapturers() and passed to the |worker_| later.
+  scoped_ptr<webrtc::ScreenCapturer> screen_capturer_;
+  scoped_ptr<webrtc::WindowCapturer> window_capturer_;
+
+  // Time interval between mode updates.
+  base::TimeDelta update_period_;
+
+  // Size of thumbnails generated by the model.
+  gfx::Size thumbnail_size_;
+
+  // The observer passed to StartUpdating().
+  Observer* observer_;
+
+  // Task runner used for the |worker_|.
+  scoped_refptr<base::SequencedTaskRunner> capture_task_runner_;
+
+  // An object that does all the work of getting list of sources on a background
+  // thread (see |capture_task_runner_|). Destroyed on |capture_task_runner_|
+  // after the model is destroyed.
+  scoped_ptr<Worker> worker_;
+
+  // Current list of sources.
+  std::vector<Source> sources_;
+
+  base::WeakPtrFactory<DesktopMediaPickerModel> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerModel);
+};
+
+#endif  // CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_PICKER_MODEL_H_
diff --git a/chrome/browser/media/desktop_media_picker_model_unittest.cc b/chrome/browser/media/desktop_media_picker_model_unittest.cc
new file mode 100644
index 0000000..6cd5fa1
--- /dev/null
+++ b/chrome/browser/media/desktop_media_picker_model_unittest.cc
@@ -0,0 +1,408 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/media/desktop_media_picker_model.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
+#include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
+
+using testing::_;
+using testing::AtMost;
+using testing::DoAll;
+
+namespace {
+
+class MockObserver : public DesktopMediaPickerModel::Observer {
+ public:
+  MOCK_METHOD1(OnSourceAdded, void(int index));
+  MOCK_METHOD1(OnSourceRemoved, void(int index));
+  MOCK_METHOD1(OnSourceNameChanged, void(int index));
+  MOCK_METHOD1(OnSourceThumbnailChanged, void(int index));
+};
+
+class FakeScreenCapturer : public webrtc::ScreenCapturer {
+ public:
+  FakeScreenCapturer() {}
+  virtual ~FakeScreenCapturer() {}
+
+  // webrtc::ScreenCapturer implementation.
+  virtual void Start(Callback* callback) OVERRIDE {
+    callback_ = callback;
+  }
+
+  virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE {
+    DCHECK(callback_);
+    webrtc::DesktopFrame* frame =
+        new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10));
+    memset(frame->data(), 0, frame->stride() * frame->size().height());
+    callback_->OnCaptureCompleted(frame);
+  }
+
+  virtual void SetMouseShapeObserver(
+      MouseShapeObserver* mouse_shape_observer) OVERRIDE {
+    NOTIMPLEMENTED();
+  }
+
+ protected:
+  Callback* callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeScreenCapturer);
+};
+
+class FakeWindowCapturer : public webrtc::WindowCapturer {
+ public:
+  FakeWindowCapturer()
+      : callback_(NULL) {
+  }
+  virtual ~FakeWindowCapturer() {
+    STLDeleteContainerPairSecondPointers(frames_.begin(), frames_.end());
+  }
+
+  void SetWindowList(const WindowList& list) {
+    base::AutoLock lock(window_list_lock_);
+    window_list_ = list;
+  }
+
+  void SetNextFrame(WindowId window_id,
+                    scoped_ptr<webrtc::DesktopFrame> frame) {
+      frames_[window_id] = frame.release();
+  }
+
+  // webrtc::WindowCapturer implementation.
+  virtual void Start(Callback* callback) OVERRIDE {
+    callback_ = callback;
+  }
+
+  virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE {
+    DCHECK(callback_);
+
+    webrtc::DesktopFrame* frame;
+    std::map<WindowId, webrtc::DesktopFrame*>::iterator it =
+        frames_.find(selected_window_id_);
+    if (it == frames_.end()) {
+      frame = new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10));
+      memset(frame->data(), 0, frame->stride() * frame->size().height());
+    } else {
+      frame = it->second;
+      frames_.erase(it);
+    }
+    callback_->OnCaptureCompleted(frame);
+  }
+
+  virtual bool GetWindowList(WindowList* windows) OVERRIDE {
+    base::AutoLock lock(window_list_lock_);
+    *windows = window_list_;
+    return true;
+  }
+
+  virtual bool SelectWindow(WindowId id) OVERRIDE {
+    selected_window_id_ = id;
+    return true;
+  }
+
+ private:
+  Callback* callback_;
+  WindowList window_list_;
+  base::Lock window_list_lock_;
+
+  WindowId selected_window_id_;
+
+  // Frames to be captured per window.
+  std::map<WindowId, webrtc::DesktopFrame*> frames_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeWindowCapturer);
+};
+
+class DesktopMediaPickerModelTest : public testing::Test {
+ public:
+  DesktopMediaPickerModelTest()
+      : window_capturer_(NULL),
+        ui_thread_(content::BrowserThread::UI,
+                   &message_loop_) {
+    // Set update period to reduce the time it takes to run tests.
+    model_.SetUpdatePeriod(base::TimeDelta::FromMilliseconds(0));
+  }
+
+  void SetDefaultCapturers() {
+    window_capturer_ = new FakeWindowCapturer();
+    model_.SetCapturers(
+        scoped_ptr<webrtc::ScreenCapturer>(new FakeScreenCapturer()),
+        scoped_ptr<webrtc::WindowCapturer>(window_capturer_));
+  }
+
+ protected:
+  // Must be listed before |model_|, so it's destroyed last.
+  MockObserver observer_;
+
+  // Owned by |model_|;
+  FakeWindowCapturer* window_capturer_;
+
+  DesktopMediaPickerModel model_;
+
+  base::MessageLoop message_loop_;
+  content::TestBrowserThread ui_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerModelTest);
+};
+
+ACTION_P2(CheckListSize, model, expected_list_size) {
+  EXPECT_EQ(expected_list_size, model->source_count());
+}
+
+ACTION_P(QuitMessageLoop, message_loop) {
+  message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
+}
+
+TEST_F(DesktopMediaPickerModelTest, InitialSourceList) {
+  SetDefaultCapturers();
+
+  webrtc::WindowCapturer::WindowList list;
+  webrtc::WindowCapturer::Window window;
+  window.id = 0;
+  window.title = "Test window";
+  list.push_back(window);
+  window_capturer_->SetWindowList(list);
+
+  {
+    testing::InSequence dummy;
+    EXPECT_CALL(observer_, OnSourceAdded(0))
+      .WillOnce(CheckListSize(&model_, 1));
+    EXPECT_CALL(observer_, OnSourceAdded(1))
+      .WillOnce(CheckListSize(&model_, 2));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(0));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(1))
+      .WillOnce(QuitMessageLoop(&message_loop_));
+  }
+  model_.StartUpdating(&observer_);
+
+  message_loop_.Run();
+
+  EXPECT_EQ(model_.source(0).id.type, content::MEDIA_SCREEN_VIDEO_CAPTURE);
+  EXPECT_EQ(model_.source(0).id.id, 0);
+  EXPECT_EQ(model_.source(1).id.type, content::MEDIA_WINDOW_VIDEO_CAPTURE);
+  EXPECT_EQ(model_.source(1).id.id, 0);
+  EXPECT_EQ(model_.source(1).name, UTF8ToUTF16(window.title));
+}
+
+TEST_F(DesktopMediaPickerModelTest, WindowsOnly) {
+  window_capturer_ = new FakeWindowCapturer();
+  model_.SetCapturers(
+      scoped_ptr<webrtc::ScreenCapturer>(),
+      scoped_ptr<webrtc::WindowCapturer>(window_capturer_));
+
+  webrtc::WindowCapturer::WindowList list;
+  webrtc::WindowCapturer::Window window;
+  window.id = 0;
+  window.title = "Test window";
+  list.push_back(window);
+  window_capturer_->SetWindowList(list);
+
+  {
+    testing::InSequence dummy;
+    EXPECT_CALL(observer_, OnSourceAdded(0))
+      .WillOnce(CheckListSize(&model_, 1));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(0))
+      .WillOnce(QuitMessageLoop(&message_loop_));
+  }
+  model_.StartUpdating(&observer_);
+
+  message_loop_.Run();
+
+  EXPECT_EQ(model_.source(0).id.type, content::MEDIA_WINDOW_VIDEO_CAPTURE);
+}
+
+TEST_F(DesktopMediaPickerModelTest, ScreenOnly) {
+  model_.SetCapturers(
+      scoped_ptr<webrtc::ScreenCapturer>(new FakeScreenCapturer),
+      scoped_ptr<webrtc::WindowCapturer>());
+
+  {
+    testing::InSequence dummy;
+    EXPECT_CALL(observer_, OnSourceAdded(0))
+      .WillOnce(CheckListSize(&model_, 1));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(0))
+      .WillOnce(QuitMessageLoop(&message_loop_));
+  }
+  model_.StartUpdating(&observer_);
+
+  message_loop_.Run();
+
+  EXPECT_EQ(model_.source(0).id.type, content::MEDIA_SCREEN_VIDEO_CAPTURE);
+}
+
+TEST_F(DesktopMediaPickerModelTest, AddWindow) {
+  SetDefaultCapturers();
+
+  webrtc::WindowCapturer::WindowList list;
+  webrtc::WindowCapturer::Window window;
+  window.id = 1;
+  window.title = "Test window 1";
+  list.push_back(window);
+  window_capturer_->SetWindowList(list);
+
+  {
+    testing::InSequence dummy;
+    EXPECT_CALL(observer_, OnSourceAdded(0))
+      .WillOnce(CheckListSize(&model_, 1));
+    EXPECT_CALL(observer_, OnSourceAdded(1))
+      .WillOnce(CheckListSize(&model_, 2));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(0));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(1))
+      .WillOnce(QuitMessageLoop(&message_loop_));
+  }
+  model_.StartUpdating(&observer_);
+
+  message_loop_.Run();
+
+  testing::Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(observer_, OnSourceAdded(1))
+    .WillOnce(DoAll(CheckListSize(&model_, 3),
+                    QuitMessageLoop(&message_loop_)));
+
+  window.id = 0;
+  window.title = "Test window 0";
+  list.push_back(window);
+  window_capturer_->SetWindowList(list);
+
+  message_loop_.Run();
+
+  EXPECT_EQ(model_.source(1).id.type, content::MEDIA_WINDOW_VIDEO_CAPTURE);
+  EXPECT_EQ(model_.source(1).id.id, 0);
+}
+
+TEST_F(DesktopMediaPickerModelTest, RemoveWindow) {
+  SetDefaultCapturers();
+
+  webrtc::WindowCapturer::WindowList list;
+  webrtc::WindowCapturer::Window window;
+  window.id = 0;
+  window.title = "Test window 0";
+  list.push_back(window);
+  window.id = 1;
+  window.title = "Test window 1";
+  list.push_back(window);
+  window_capturer_->SetWindowList(list);
+
+  {
+    testing::InSequence dummy;
+    EXPECT_CALL(observer_, OnSourceAdded(0))
+      .WillOnce(CheckListSize(&model_, 1));
+    EXPECT_CALL(observer_, OnSourceAdded(1))
+      .WillOnce(CheckListSize(&model_, 2));
+    EXPECT_CALL(observer_, OnSourceAdded(2))
+      .WillOnce(CheckListSize(&model_, 3));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(0));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(1));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(2))
+      .WillOnce(QuitMessageLoop(&message_loop_));
+  }
+  model_.StartUpdating(&observer_);
+
+  message_loop_.Run();
+
+  testing::Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(observer_, OnSourceRemoved(1))
+    .WillOnce(DoAll(CheckListSize(&model_, 2),
+                    QuitMessageLoop(&message_loop_)));
+
+  list.erase(list.begin());
+  window_capturer_->SetWindowList(list);
+
+  message_loop_.Run();
+}
+
+TEST_F(DesktopMediaPickerModelTest, UpdateTitle) {
+  SetDefaultCapturers();
+
+  webrtc::WindowCapturer::WindowList list;
+  webrtc::WindowCapturer::Window window;
+  window.id = 0;
+  window.title = "Test window";
+  list.push_back(window);
+  window_capturer_->SetWindowList(list);
+
+  {
+    testing::InSequence dummy;
+    EXPECT_CALL(observer_, OnSourceAdded(0))
+      .WillOnce(CheckListSize(&model_, 1));
+    EXPECT_CALL(observer_, OnSourceAdded(1))
+      .WillOnce(CheckListSize(&model_, 2));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(0));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(1))
+      .WillOnce(QuitMessageLoop(&message_loop_));
+  }
+  model_.StartUpdating(&observer_);
+
+  message_loop_.Run();
+
+  testing::Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(observer_, OnSourceNameChanged(1))
+    .WillOnce(QuitMessageLoop(&message_loop_));
+
+  const std::string kTestTitle = "New Title";
+
+  list[0].title = kTestTitle;
+  window_capturer_->SetWindowList(list);
+
+  message_loop_.Run();
+
+  EXPECT_EQ(model_.source(1).name, base::UTF8ToUTF16(kTestTitle));
+}
+
+TEST_F(DesktopMediaPickerModelTest, UpdateThumbnail) {
+  SetDefaultCapturers();
+
+  webrtc::WindowCapturer::WindowList list;
+  webrtc::WindowCapturer::Window window;
+  window.id = 0;
+  window.title = "Test window 1";
+  list.push_back(window);
+  window.id = 1;
+  window.title = "Test window 2";
+  list.push_back(window);
+  window_capturer_->SetWindowList(list);
+
+  {
+    testing::InSequence dummy;
+    EXPECT_CALL(observer_, OnSourceAdded(0))
+      .WillOnce(CheckListSize(&model_, 1));
+    EXPECT_CALL(observer_, OnSourceAdded(1))
+      .WillOnce(CheckListSize(&model_, 2));
+    EXPECT_CALL(observer_, OnSourceAdded(2))
+      .WillOnce(CheckListSize(&model_, 3));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(0));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(1));
+    EXPECT_CALL(observer_, OnSourceThumbnailChanged(2))
+      .WillOnce(QuitMessageLoop(&message_loop_));
+  }
+  model_.StartUpdating(&observer_);
+
+  message_loop_.Run();
+
+  testing::Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(observer_, OnSourceThumbnailChanged(1))
+    .WillOnce(QuitMessageLoop(&message_loop_));
+
+  // Update frame for the window and verify that we get notification about it.
+  scoped_ptr<webrtc::DesktopFrame> frame(
+      new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10)));
+  memset(frame->data(), 1, frame->stride() * frame->size().height());
+  window_capturer_->SetNextFrame(0, frame.Pass());
+
+  message_loop_.Run();
+}
+
+}  // namespace
diff --git a/chrome/browser/media/media_capture_devices_dispatcher.cc b/chrome/browser/media/media_capture_devices_dispatcher.cc
index ff9418c..1b2af4a 100644
--- a/chrome/browser/media/media_capture_devices_dispatcher.cc
+++ b/chrome/browser/media/media_capture_devices_dispatcher.cc
@@ -175,7 +175,8 @@
     const extensions::Extension* extension) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  if (request.video_type == content::MEDIA_SCREEN_VIDEO_CAPTURE) {
+  if (request.video_type == content::MEDIA_SCREEN_VIDEO_CAPTURE ||
+      request.audio_type == content::MEDIA_SYSTEM_AUDIO_CAPTURE) {
     ProcessScreenCaptureAccessRequest(
         web_contents, request, callback,
         extension && extension->location() == extensions::Manifest::COMPONENT);
@@ -210,19 +211,26 @@
   //  1. Screen capturing is enabled via command line switch or white-listed for
   //     the given origin.
   //  2. Request comes from a page with a secure origin or from an extension.
-  //  3. Audio capture was not requested (it's not supported yet).
+  //  3. Video capture is requested for screen video.
+  //  4. Audio capture is either not requested, or requested for system audio.
   if (screen_capture_enabled && origin_is_secure &&
-      request.audio_type == content::MEDIA_NO_SERVICE) {
+      request.video_type == content::MEDIA_SCREEN_VIDEO_CAPTURE &&
+      (request.audio_type == content::MEDIA_NO_SERVICE ||
+       request.audio_type == content::MEDIA_SYSTEM_AUDIO_CAPTURE)) {
     // For component extensions, bypass message box.
     bool user_approved = false;
     if (!component_extension) {
       string16 application_name = UTF8ToUTF16(request.security_origin.spec());
+      string16 confirmation_text = l10n_util::GetStringFUTF16(
+          request.audio_type == content::MEDIA_NO_SERVICE ?
+              IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT :
+              IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT,
+          application_name);
       chrome::MessageBoxResult result = chrome::ShowMessageBox(
           NULL,
           l10n_util::GetStringFUTF16(
               IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name),
-          l10n_util::GetStringFUTF16(
-              IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT, application_name),
+          confirmation_text,
           chrome::MESSAGE_BOX_TYPE_QUESTION);
       user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES);
     }
@@ -230,6 +238,10 @@
     if (user_approved || component_extension) {
       devices.push_back(content::MediaStreamDevice(
           content::MEDIA_SCREEN_VIDEO_CAPTURE, std::string(), "Screen"));
+      if (request.audio_type == content::MEDIA_SYSTEM_AUDIO_CAPTURE) {
+        devices.push_back(content::MediaStreamDevice(
+            content::MEDIA_SYSTEM_AUDIO_CAPTURE, std::string(), std::string()));
+      }
     }
   }
 
diff --git a/chrome/browser/media/media_stream_capture_indicator.cc b/chrome/browser/media/media_stream_capture_indicator.cc
index 456fc9d..50e4ea7 100644
--- a/chrome/browser/media/media_stream_capture_indicator.cc
+++ b/chrome/browser/media/media_stream_capture_indicator.cc
@@ -366,7 +366,8 @@
   UpdateNotificationUserInterface();
 }
 
-void MediaStreamCaptureIndicator::MaybeCreateStatusTrayIcon() {
+void MediaStreamCaptureIndicator::MaybeCreateStatusTrayIcon(bool audio,
+                                                            bool video) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   if (status_icon_)
     return;
@@ -379,10 +380,16 @@
   if (!status_tray)
     return;
 
-  status_icon_ =
-      status_tray->CreateStatusIcon(StatusTray::MEDIA_STREAM_CAPTURE_ICON);
-
   EnsureStatusTrayIconResources();
+
+  gfx::ImageSkia image;
+  string16 tool_tip;
+  GetStatusTrayIconInfo(audio, video, &image, &tool_tip);
+  DCHECK(!image.isNull());
+  DCHECK(!tool_tip.empty());
+
+  status_icon_ = status_tray->CreateStatusIcon(
+      StatusTray::MEDIA_STREAM_CAPTURE_ICON, image, tool_tip);
 }
 
 void MediaStreamCaptureIndicator::EnsureStatusTrayIconResources() {
@@ -481,32 +488,32 @@
   }
 
   // The icon will take the ownership of the passed context menu.
-  MaybeCreateStatusTrayIcon();
+  MaybeCreateStatusTrayIcon(audio, video);
   if (status_icon_) {
     status_icon_->SetContextMenu(menu.release());
-    UpdateStatusTrayIconDisplay(audio, video);
   }
 }
 
-void MediaStreamCaptureIndicator::UpdateStatusTrayIconDisplay(
-    bool audio, bool video) {
+void MediaStreamCaptureIndicator::GetStatusTrayIconInfo(bool audio,
+                                                        bool video,
+                                                        gfx::ImageSkia* image,
+                                                        string16* tool_tip) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(audio || video);
-  DCHECK(status_icon_);
+
   int message_id = 0;
   if (audio && video) {
     message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_AND_VIDEO;
-    status_icon_->SetImage(*camera_image_);
+    *image = *camera_image_;
   } else if (audio && !video) {
     message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_AUDIO_ONLY;
-    status_icon_->SetImage(*mic_image_);
+    *image = *mic_image_;
   } else if (!audio && video) {
     message_id = IDS_MEDIA_STREAM_STATUS_TRAY_TEXT_VIDEO_ONLY;
-    status_icon_->SetImage(*camera_image_);
+    *image = *camera_image_;
   }
 
-  status_icon_->SetToolTip(l10n_util::GetStringFUTF16(
-      message_id, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
+  *tool_tip = l10n_util::GetStringUTF16(message_id);
 }
 
 void MediaStreamCaptureIndicator::OnStopScreenCapture(
diff --git a/chrome/browser/media/media_stream_capture_indicator.h b/chrome/browser/media/media_stream_capture_indicator.h
index 4c311cf..9fc861e 100644
--- a/chrome/browser/media/media_stream_capture_indicator.h
+++ b/chrome/browser/media/media_stream_capture_indicator.h
@@ -84,9 +84,14 @@
   // Helpers to create and destroy status tray icon. Called from
   // UpdateNotificationUserInterface().
   void EnsureStatusTrayIconResources();
-  void MaybeCreateStatusTrayIcon();
+  void MaybeCreateStatusTrayIcon(bool audio, bool video);
   void MaybeDestroyStatusTrayIcon();
-  void UpdateStatusTrayIconDisplay(bool audio, bool video);
+
+  // Gets the status icon image and the string to use as the tooltip.
+  void GetStatusTrayIconInfo(bool audio,
+                             bool video,
+                             gfx::ImageSkia* image,
+                             string16* tool_tip);
 
   // Callback for ScreenCaptureNotificationUI.
   void OnStopScreenCapture(const base::Closure& stop);
diff --git a/chrome/browser/media/midi_permission_infobar_delegate.cc b/chrome/browser/media/midi_permission_infobar_delegate.cc
new file mode 100644
index 0000000..3a982e5
--- /dev/null
+++ b/chrome/browser/media/midi_permission_infobar_delegate.cc
@@ -0,0 +1,96 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/media/midi_permission_infobar_delegate.h"
+
+#include "chrome/browser/content_settings/permission_queue_controller.h"
+#include "chrome/browser/content_settings/permission_request_id.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "content/public/browser/navigation_details.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/web_contents.h"
+#include "grit/generated_resources.h"
+#include "grit/locale_settings.h"
+#include "grit/theme_resources.h"
+#include "net/base/net_util.h"
+#include "ui/base/l10n/l10n_util.h"
+
+// static
+InfoBarDelegate* MIDIPermissionInfoBarDelegate::Create(
+    InfoBarService* infobar_service,
+    PermissionQueueController* controller,
+    const PermissionRequestID& id,
+    const GURL& requesting_frame,
+    const std::string& display_languages) {
+  return infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>(
+      new MIDIPermissionInfoBarDelegate(infobar_service,
+                                        controller,
+                                        id,
+                                        requesting_frame,
+                                        display_languages)));
+}
+
+MIDIPermissionInfoBarDelegate::MIDIPermissionInfoBarDelegate(
+    InfoBarService* infobar_service,
+    PermissionQueueController* controller,
+    const PermissionRequestID& id,
+    const GURL& requesting_frame,
+    const std::string& display_languages)
+    : ConfirmInfoBarDelegate(infobar_service),
+      controller_(controller),
+      id_(id),
+      requesting_frame_(requesting_frame),
+      display_languages_(display_languages) {
+  const content::NavigationEntry* committed_entry = infobar_service->
+      web_contents()->GetController().GetLastCommittedEntry();
+  contents_unique_id_ = committed_entry ? committed_entry->GetUniqueID() : 0;
+}
+
+int MIDIPermissionInfoBarDelegate::GetIconID() const {
+  return IDR_INFOBAR_MIDI_SYSEX;
+}
+
+InfoBarDelegate::Type MIDIPermissionInfoBarDelegate::GetInfoBarType() const {
+  return PAGE_ACTION_TYPE;
+}
+
+bool MIDIPermissionInfoBarDelegate::ShouldExpireInternal(
+    const content::LoadCommittedDetails& details) const {
+  // This implementation matches InfoBarDelegate::ShouldExpireInternal(), but
+  // uses the unique ID we set in the constructor instead of that stored in the
+  // base class.
+  return (contents_unique_id_ != details.entry->GetUniqueID()) ||
+      (content::PageTransitionStripQualifier(
+          details.entry->GetTransitionType()) ==
+              content::PAGE_TRANSITION_RELOAD);
+}
+
+string16 MIDIPermissionInfoBarDelegate::GetMessageText() const {
+  return l10n_util::GetStringFUTF16(IDS_MIDI_SYSEX_INFOBAR_QUESTION,
+      net::FormatUrl(requesting_frame_.GetOrigin(), display_languages_));
+}
+
+string16 MIDIPermissionInfoBarDelegate::GetButtonLabel(
+    InfoBarButton button) const {
+  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
+      IDS_MIDI_SYSEX_ALLOW_BUTTON : IDS_MIDI_SYSEX_DENY_BUTTON);
+}
+
+bool MIDIPermissionInfoBarDelegate::Accept() {
+  SetPermission(true, true);
+  return true;
+}
+
+bool MIDIPermissionInfoBarDelegate::Cancel() {
+  SetPermission(true, false);
+  return true;
+}
+
+void MIDIPermissionInfoBarDelegate::SetPermission(bool update_content_setting,
+                                                  bool allowed) {
+  controller_->OnPermissionSet(id_, requesting_frame_,
+                               web_contents()->GetURL(),
+                               update_content_setting, allowed);
+}
+
diff --git a/chrome/browser/media/midi_permission_infobar_delegate.h b/chrome/browser/media/midi_permission_infobar_delegate.h
new file mode 100644
index 0000000..1ec1d29
--- /dev/null
+++ b/chrome/browser/media/midi_permission_infobar_delegate.h
@@ -0,0 +1,64 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_MEDIA_MIDI_PERMISSION_INFOBAR_DELEGATE_H_
+#define CHROME_BROWSER_MEDIA_MIDI_PERMISSION_INFOBAR_DELEGATE_H_
+
+#include <string>
+
+#include "chrome/browser/content_settings/permission_request_id.h"
+#include "chrome/browser/infobars/confirm_infobar_delegate.h"
+#include "url/gurl.h"
+
+class PermissionQueueController;
+class InfoBarService;
+
+// TODO(toyoshim): Much more code can be shared with GeolocationInfoBarDelegate.
+// http://crbug.com/266743
+
+// MIDIPermissionInfoBarDelegates are created by the
+// ChromeMIDIPermissionContext to control the display and handling of MIDI
+// permission infobars to the user.
+class MIDIPermissionInfoBarDelegate : public ConfirmInfoBarDelegate {
+ public:
+  // Creates a infobar delegate and adds it to |infobar_service|.
+  // Returns the delegate if it was successfully added.
+  static InfoBarDelegate* Create(InfoBarService* infobar_service,
+                                 PermissionQueueController* controller,
+                                 const PermissionRequestID& id,
+                                 const GURL& requesting_frame,
+                                 const std::string& display_languages);
+
+ protected:
+  MIDIPermissionInfoBarDelegate(InfoBarService* infobar_service,
+                                PermissionQueueController* controller,
+                                const PermissionRequestID& id,
+                                const GURL& requesting_frame,
+                                const std::string& display_languages);
+
+ private:
+  // ConfirmInfoBarDelegate:
+  virtual int GetIconID() const OVERRIDE;
+  virtual Type GetInfoBarType() const OVERRIDE;
+  virtual bool ShouldExpireInternal(
+      const content::LoadCommittedDetails& details) const OVERRIDE;
+  virtual string16 GetMessageText() const OVERRIDE;
+  virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
+  virtual bool Accept() OVERRIDE;
+  virtual bool Cancel() OVERRIDE;
+
+  // Callback to the controller to inform of the user's decision.
+  void SetPermission(bool update_content_setting, bool allowed);
+
+ private:
+  PermissionQueueController* controller_;
+  const PermissionRequestID id_;
+  GURL requesting_frame_;
+  int contents_unique_id_;
+  std::string display_languages_;
+
+  DISALLOW_COPY_AND_ASSIGN(MIDIPermissionInfoBarDelegate);
+};
+
+#endif  // CHROME_BROWSER_MEDIA_MIDI_PERMISSION_INFOBAR_DELEGATE_H_
diff --git a/chrome/browser/media_galleries/fileapi/itunes_data_provider.cc b/chrome/browser/media_galleries/fileapi/itunes_data_provider.cc
index 5ecc091..f51a69b 100644
--- a/chrome/browser/media_galleries/fileapi/itunes_data_provider.cc
+++ b/chrome/browser/media_galleries/fileapi/itunes_data_provider.cc
@@ -15,7 +15,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
-#include "chrome/common/itunes_library.h"
+#include "chrome/common/media_galleries/itunes_library.h"
 #include "content/public/browser/browser_thread.h"
 
 using chrome::MediaFileSystemBackend;
diff --git a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
index 59809bb..d1907cc 100644
--- a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
+++ b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
@@ -24,11 +24,10 @@
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_file_stream_reader.h"
 #include "webkit/browser/fileapi/file_system_operation_context.h"
-#include "webkit/browser/fileapi/file_system_task_runners.h"
+#include "webkit/browser/fileapi/file_system_operation_impl.h"
 #include "webkit/browser/fileapi/isolated_context.h"
 #include "webkit/browser/fileapi/isolated_file_util.h"
 #include "webkit/browser/fileapi/local_file_stream_writer.h"
-#include "webkit/browser/fileapi/local_file_system_operation.h"
 #include "webkit/browser/fileapi/native_file_util.h"
 #include "webkit/common/fileapi/file_system_types.h"
 #include "webkit/common/fileapi/file_system_util.h"
@@ -181,8 +180,8 @@
                                     url.filesystem_id());
   }
 
-  return new fileapi::LocalFileSystemOperation(url, context,
-                                               operation_context.Pass());
+  return new fileapi::FileSystemOperationImpl(url, context,
+                                              operation_context.Pass());
 }
 
 scoped_ptr<webkit_blob::FileStreamReader>
@@ -193,7 +192,7 @@
     FileSystemContext* context) const {
   return scoped_ptr<webkit_blob::FileStreamReader>(
       new webkit_blob::LocalFileStreamReader(
-          context->task_runners()->file_task_runner(),
+          context->default_file_task_runner(),
           url.path(), offset, expected_modification_time));
 }
 
@@ -204,7 +203,7 @@
     FileSystemContext* context) const {
   return scoped_ptr<fileapi::FileStreamWriter>(
       new fileapi::LocalFileStreamWriter(
-          context->task_runners()->file_task_runner(),
+          context->default_file_task_runner(),
           url.path(), offset));
 }
 
diff --git a/chrome/browser/media_galleries/fileapi/media_file_validator_factory.cc b/chrome/browser/media_galleries/fileapi/media_file_validator_factory.cc
index 8625186..e180460 100644
--- a/chrome/browser/media_galleries/fileapi/media_file_validator_factory.cc
+++ b/chrome/browser/media_galleries/fileapi/media_file_validator_factory.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/media_galleries/fileapi/supported_image_type_validator.h"
 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
 #include "webkit/browser/fileapi/file_system_url.h"
-#include "webkit/common/blob/shareable_file_reference.h"
 
 namespace chrome {
 
diff --git a/chrome/browser/media_galleries/fileapi/native_media_file_util.cc b/chrome/browser/media_galleries/fileapi/native_media_file_util.cc
index 107e728..a9b922a 100644
--- a/chrome/browser/media_galleries/fileapi/native_media_file_util.cc
+++ b/chrome/browser/media_galleries/fileapi/native_media_file_util.cc
@@ -20,7 +20,6 @@
 #include "url/gurl.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_operation_context.h"
-#include "webkit/browser/fileapi/file_system_task_runners.h"
 #include "webkit/browser/fileapi/native_file_util.h"
 #include "webkit/common/blob/shareable_file_reference.h"
 
diff --git a/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc b/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc
index a023b75..4eb0d18 100644
--- a/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc
+++ b/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc
@@ -21,7 +21,6 @@
 #include "webkit/browser/fileapi/file_system_backend.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_operation_runner.h"
-#include "webkit/browser/fileapi/file_system_task_runners.h"
 #include "webkit/browser/fileapi/file_system_url.h"
 #include "webkit/browser/fileapi/isolated_context.h"
 #include "webkit/browser/fileapi/mock_file_system_options.h"
@@ -130,7 +129,8 @@
         data_dir_.path(), base::MessageLoopProxy::current().get()));
 
     file_system_context_ = new fileapi::FileSystemContext(
-        fileapi::FileSystemTaskRunners::CreateMockTaskRunners(),
+        base::MessageLoopProxy::current().get(),
+        base::MessageLoopProxy::current().get(),
         fileapi::ExternalMountPoints::CreateRefCounted().get(),
         storage_policy.get(),
         NULL,
diff --git a/chrome/browser/media_galleries/fileapi/picasa/picasa_file_util_unittest.cc b/chrome/browser/media_galleries/fileapi/picasa/picasa_file_util_unittest.cc
index bed537d..978b0dc 100644
--- a/chrome/browser/media_galleries/fileapi/picasa/picasa_file_util_unittest.cc
+++ b/chrome/browser/media_galleries/fileapi/picasa/picasa_file_util_unittest.cc
@@ -30,7 +30,6 @@
 #include "webkit/browser/fileapi/file_system_file_util.h"
 #include "webkit/browser/fileapi/file_system_operation_context.h"
 #include "webkit/browser/fileapi/file_system_operation_runner.h"
-#include "webkit/browser/fileapi/file_system_task_runners.h"
 #include "webkit/browser/fileapi/isolated_context.h"
 #include "webkit/browser/fileapi/mock_file_system_options.h"
 #include "webkit/browser/quota/mock_special_storage_policy.h"
@@ -234,7 +233,8 @@
         new TestPicasaFileUtil(picasa_data_provider_.get())));
 
     file_system_context_ = new fileapi::FileSystemContext(
-        fileapi::FileSystemTaskRunners::CreateMockTaskRunners(),
+        base::MessageLoopProxy::current().get(),
+        base::MessageLoopProxy::current().get(),
         fileapi::ExternalMountPoints::CreateRefCounted().get(),
         storage_policy.get(),
         NULL,
diff --git a/chrome/browser/media_galleries/fileapi/safe_itunes_library_parser.h b/chrome/browser/media_galleries/fileapi/safe_itunes_library_parser.h
index 6a0e7f0..f041951 100644
--- a/chrome/browser/media_galleries/fileapi/safe_itunes_library_parser.h
+++ b/chrome/browser/media_galleries/fileapi/safe_itunes_library_parser.h
@@ -12,7 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/platform_file.h"
-#include "chrome/common/itunes_library.h"
+#include "chrome/common/media_galleries/itunes_library.h"
 #include "content/public/browser/utility_process_host.h"
 #include "content/public/browser/utility_process_host_client.h"
 
diff --git a/chrome/browser/media_galleries/media_galleries_dialog_controller_unittest.cc b/chrome/browser/media_galleries/media_galleries_dialog_controller_unittest.cc
index 890750e..a5ae6ba 100644
--- a/chrome/browser/media_galleries/media_galleries_dialog_controller_unittest.cc
+++ b/chrome/browser/media_galleries/media_galleries_dialog_controller_unittest.cc
@@ -9,19 +9,23 @@
 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
 #include "chrome/browser/storage_monitor/storage_info.h"
+#include "chrome/browser/storage_monitor/test_storage_monitor.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-std::string GalleryName(const chrome::MediaGalleryPrefInfo& gallery) {
+namespace chrome {
+
+std::string GalleryName(const MediaGalleryPrefInfo& gallery) {
   string16 name = gallery.GetGalleryDisplayName();
   return UTF16ToASCII(name);
 }
 
 TEST(MediaGalleriesDialogControllerTest, TestNameGeneration) {
-  chrome::MediaGalleryPrefInfo gallery;
+  ASSERT_TRUE(test::TestStorageMonitor::CreateAndInstall());
+  MediaGalleryPrefInfo gallery;
   gallery.pref_id = 1;
-  gallery.device_id = chrome::StorageInfo::MakeDeviceId(
-      chrome::StorageInfo::FIXED_MASS_STORAGE, "/path/to/gallery");
-  gallery.type = chrome::MediaGalleryPrefInfo::kAutoDetected;
+  gallery.device_id = StorageInfo::MakeDeviceId(
+      StorageInfo::FIXED_MASS_STORAGE, "/path/to/gallery");
+  gallery.type = MediaGalleryPrefInfo::kAutoDetected;
   EXPECT_EQ("gallery", GalleryName(gallery));
 
   gallery.display_name = ASCIIToUTF16("override");
@@ -35,8 +39,8 @@
   EXPECT_EQ("gallery2", GalleryName(gallery));
 
   gallery.path = base::FilePath();
-  gallery.device_id = chrome::StorageInfo::MakeDeviceId(
-      chrome::StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
+  gallery.device_id = StorageInfo::MakeDeviceId(
+      StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
       "/path/to/dcim");
   gallery.display_name = ASCIIToUTF16("override");
   EXPECT_EQ("override", GalleryName(gallery));
@@ -58,3 +62,5 @@
   gallery.path = base::FilePath(FILE_PATH_LITERAL("sub/path"));
   EXPECT_EQ("path - 977 KB vendor, model", GalleryName(gallery));
 }
+
+}  // namespace chrome
diff --git a/chrome/browser/media_galleries/media_galleries_test_util.cc b/chrome/browser/media_galleries/media_galleries_test_util.cc
index c73a5be..0f01e1e 100644
--- a/chrome/browser/media_galleries/media_galleries_test_util.cc
+++ b/chrome/browser/media_galleries/media_galleries_test_util.cc
@@ -57,7 +57,9 @@
     return NULL;
 
   extension_prefs->OnExtensionInstalled(
-      extension.get(), extensions::Extension::ENABLED,
+      extension.get(),
+      extensions::Extension::ENABLED,
+      extensions::Blacklist::NOT_BLACKLISTED,
       syncer::StringOrdinal::CreateInitialOrdinal());
   ExtensionService* extension_service =
       extensions::ExtensionSystem::Get(profile)->extension_service();
diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc
index 48d2d0a..c589ef2 100644
--- a/chrome/browser/memory_details.cc
+++ b/chrome/browser/memory_details.cc
@@ -160,9 +160,14 @@
       }
       log += "]";
     }
-    log += StringPrintf(" %d MB private, %d MB shared\n",
+    log += StringPrintf(" %d MB private, %d MB shared",
                         static_cast<int>(iter1->working_set.priv) / 1024,
                         static_cast<int>(iter1->working_set.shared) / 1024);
+#if defined(OS_CHROMEOS)
+    log += StringPrintf(", %d MB swapped",
+                        static_cast<int>(iter1->working_set.swapped) / 1024);
+#endif
+    log += "\n";
   }
   return log;
 }
@@ -578,8 +583,12 @@
   UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.MemUsedTotal",
                               swap_data_.mem_used_total / (1024 * 1024),
                               1, 4096, 50);
-  UMA_HISTOGRAM_COUNTS("Memory.Swap.NumReads", swap_data_.num_reads);
-  UMA_HISTOGRAM_COUNTS("Memory.Swap.NumWrites", swap_data_.num_writes);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.NumReads",
+                              swap_data_.num_reads,
+                              1, 100000000, 100);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.NumWrites",
+                              swap_data_.num_writes,
+                              1, 100000000, 100);
 
   if (swap_data_.orig_data_size > 0 && swap_data_.compr_data_size > 0) {
     UMA_HISTOGRAM_CUSTOM_COUNTS(
diff --git a/chrome/browser/memory_details_linux.cc b/chrome/browser/memory_details_linux.cc
index cbb9f4c..ec149da 100644
--- a/chrome/browser/memory_details_linux.cc
+++ b/chrome/browser/memory_details_linux.cc
@@ -168,7 +168,8 @@
   std::string file_as_string;
   if (!file_util::ReadFileToString(file, &file_as_string))
     return 0;
-  uint64 file_as_uint64;
+  TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string);
+  uint64 file_as_uint64 = 0;
   if (!base::StringToUint64(file_as_string, &file_as_uint64))
     return 0;
   return file_as_uint64;
diff --git a/chrome/browser/metrics/metrics_service.cc b/chrome/browser/metrics/metrics_service.cc
index d48eb7b..3e8d17a 100644
--- a/chrome/browser/metrics/metrics_service.cc
+++ b/chrome/browser/metrics/metrics_service.cc
@@ -194,6 +194,7 @@
 #include "chrome/common/child_process_logging.h"
 #include "chrome/common/chrome_result_codes.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/metrics/caching_permuted_entropy_provider.h"
 #include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/common/metrics/metrics_log_manager.h"
 #include "chrome/common/net/test_server_locations.h"
diff --git a/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc b/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc
index c90aa92..9e17b2f 100644
--- a/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc
+++ b/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/nacl_host/nacl_browser_delegate_impl.h"
 
 #include "base/path_service.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/component_updater/pnacl/pnacl_component_installer.h"
 #include "chrome/browser/nacl_host/nacl_infobar_delegate.h"
 #include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h"
 #include "chrome/common/chrome_paths.h"
@@ -56,3 +58,11 @@
     content::BrowserPpapiHost* ppapi_host) {
   return new chrome::ChromeBrowserPepperHostFactory(ppapi_host);
 }
+
+void NaClBrowserDelegateImpl::TryInstallPnacl(
+    const base::Callback<void(bool)>& installed) {
+  ComponentUpdateService* cus = g_browser_process->component_updater();
+  PnaclComponentInstaller* pci =
+      g_browser_process->pnacl_component_installer();
+  RequestFirstInstall(cus, pci, installed);
+}
diff --git a/chrome/browser/nacl_host/nacl_browser_delegate_impl.h b/chrome/browser/nacl_host/nacl_browser_delegate_impl.h
index fc9c2b7..84933e0 100644
--- a/chrome/browser/nacl_host/nacl_browser_delegate_impl.h
+++ b/chrome/browser/nacl_host/nacl_browser_delegate_impl.h
@@ -23,6 +23,8 @@
   virtual std::string GetVersionString() const OVERRIDE;
   virtual ppapi::host::HostFactory* CreatePpapiHostFactory(
       content::BrowserPpapiHost* ppapi_host) OVERRIDE;
+  virtual void TryInstallPnacl(
+      const base::Callback<void(bool)>& installed) OVERRIDE;
 };
 
 
diff --git a/chrome/browser/nacl_host/nacl_file_host.cc b/chrome/browser/nacl_host/nacl_file_host.cc
index 8b40b8a..2610de1 100644
--- a/chrome/browser/nacl_host/nacl_file_host.cc
+++ b/chrome/browser/nacl_host/nacl_file_host.cc
@@ -11,8 +11,6 @@
 #include "base/platform_file.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/sequenced_worker_pool.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/component_updater/pnacl/pnacl_component_installer.h"
 #include "chrome/browser/extensions/extension_info_map.h"
 #include "chrome/browser/nacl_host/nacl_browser.h"
 #include "chrome/browser/nacl_host/nacl_host_message_filter.h"
@@ -87,15 +85,11 @@
     const std::string& filename,
     IPC::Message* reply_msg) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  ComponentUpdateService* cus = g_browser_process->component_updater();
-  PnaclComponentInstaller* pci =
-      g_browser_process->pnacl_component_installer();
-  RequestFirstInstall(cus,
-                      pci,
-                      base::Bind(&PnaclCheckDone,
-                                 nacl_host_message_filter,
-                                 filename,
-                                 reply_msg));
+  NaClBrowser::GetDelegate()->TryInstallPnacl(
+      base::Bind(&PnaclCheckDone,
+                 nacl_host_message_filter,
+                 filename,
+                 reply_msg));
 }
 
 void DoOpenPnaclFile(
diff --git a/chrome/browser/nacl_host/nacl_file_host_unittest.cc b/chrome/browser/nacl_host/nacl_file_host_unittest.cc
index 7b27bed..f4db7b9 100644
--- a/chrome/browser/nacl_host/nacl_file_host_unittest.cc
+++ b/chrome/browser/nacl_host/nacl_file_host_unittest.cc
@@ -52,6 +52,11 @@
     return NULL;
   }
 
+  virtual void TryInstallPnacl(
+      const base::Callback<void(bool)>& installed) OVERRIDE {
+    NOTREACHED();
+  }
+
   void SetPnaclDirectory(const base::FilePath& pnacl_dir) {
     pnacl_path_ = pnacl_dir;
   }
diff --git a/chrome/browser/nacl_host/nacl_process_host.cc b/chrome/browser/nacl_host/nacl_process_host.cc
index 21754ba..c915a5f 100644
--- a/chrome/browser/nacl_host/nacl_process_host.cc
+++ b/chrome/browser/nacl_host/nacl_process_host.cc
@@ -284,6 +284,22 @@
   reply_msg_ = reply_msg;
   manifest_path_ = manifest_path;
 
+  const CommandLine* cmd = CommandLine::ForCurrentProcess();
+#if defined(OS_WIN)
+  if (cmd->HasSwitch(switches::kEnableNaClDebug) &&
+      !cmd->HasSwitch(switches::kNoSandbox)) {
+    // We don't switch off sandbox automatically for security reasons.
+    SendErrorToRenderer("NaCl's GDB debug stub requires --no-sandbox flag"
+                        " on Windows. See crbug.com/265624.");
+    delete this;
+    return;
+  }
+#endif
+  if (cmd->HasSwitch(switches::kNaClGdb) &&
+      !cmd->HasSwitch(switches::kEnableNaClDebug)) {
+    LOG(WARNING) << "--nacl-gdb flag requires --enable-nacl-debug flag";
+  }
+
   // Start getting the IRT open asynchronously while we launch the NaCl process.
   // We'll make sure this actually finished in StartWithLaunchedProcess, below.
   NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
diff --git a/chrome/browser/nacl_host/pnacl_translation_cache.cc b/chrome/browser/nacl_host/pnacl_translation_cache.cc
index 6492e36..fa548f1 100644
--- a/chrome/browser/nacl_host/pnacl_translation_cache.cc
+++ b/chrome/browser/nacl_host/pnacl_translation_cache.cc
@@ -8,12 +8,15 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_checker.h"
+#include "components/nacl/common/pnacl_types.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "net/disk_cache/disk_cache.h"
 
+using base::IntToString;
 using content::BrowserThread;
 
 namespace {
@@ -23,7 +26,7 @@
 }  // namespace
 
 namespace pnacl {
-// These are in pnacl_cache namespace instead of static so they can be used
+// These are in pnacl namespace instead of static so they can be used
 // by the unit test.
 const int kMaxDiskCacheSize = 1000 * 1024 * 1024;
 const int kMaxMemCacheSize = 100 * 1024 * 1024;
@@ -38,12 +41,16 @@
 class PnaclTranslationCacheEntry
     : public base::RefCounted<PnaclTranslationCacheEntry> {
  public:
-  PnaclTranslationCacheEntry(base::WeakPtr<PnaclTranslationCache> cache,
-                             const std::string& key,
-                             std::string* read_nexe,
-                             const std::string& write_nexe,
-                             const CompletionCallback& callback,
-                             bool is_read);
+  static PnaclTranslationCacheEntry* GetReadEntry(
+      base::WeakPtr<PnaclTranslationCache> cache,
+      const std::string& key,
+      const GetNexeCallback& callback);
+  static PnaclTranslationCacheEntry* GetWriteEntry(
+      base::WeakPtr<PnaclTranslationCache> cache,
+      const std::string& key,
+      net::DrainableIOBuffer* write_nexe,
+      const CompletionCallback& callback);
+
   void Start();
 
   // Writes:                                ---
@@ -66,6 +73,9 @@
 
  private:
   friend class base::RefCounted<PnaclTranslationCacheEntry>;
+  PnaclTranslationCacheEntry(base::WeakPtr<PnaclTranslationCache> cache,
+                             const std::string& key,
+                             bool is_read);
   ~PnaclTranslationCacheEntry();
 
   // Try to open an existing entry in the backend
@@ -84,47 +94,57 @@
   // Used as the callback for all operations to the backend. Handle state
   // transitions, track bytes transferred, and call the other helper methods.
   void DispatchNext(int rv);
-  // Get the total transfer size. For reads, must be called after the backend
-  // entry has been opened.
-  int GetTransferSize();
 
   base::WeakPtr<PnaclTranslationCache> cache_;
-
   std::string key_;
-  std::string* read_nexe_;
-  std::string write_nexe_;
   disk_cache::Entry* entry_;
   CacheStep step_;
   bool is_read_;
-  int bytes_transferred_;
-  int bytes_to_transfer_;
-  scoped_refptr<net::IOBufferWithSize> read_buf_;
-  CompletionCallback finish_callback_;
+  GetNexeCallback read_callback_;
+  CompletionCallback write_callback_;
+  scoped_refptr<net::DrainableIOBuffer> io_buf_;
   base::ThreadChecker thread_checker_;
   DISALLOW_COPY_AND_ASSIGN(PnaclTranslationCacheEntry);
 };
 
+// static
+PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetReadEntry(
+    base::WeakPtr<PnaclTranslationCache> cache,
+    const std::string& key,
+    const GetNexeCallback& callback) {
+  PnaclTranslationCacheEntry* entry(
+      new PnaclTranslationCacheEntry(cache, key, true));
+  entry->read_callback_ = callback;
+  return entry;
+}
+
+// static
+PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetWriteEntry(
+    base::WeakPtr<PnaclTranslationCache> cache,
+    const std::string& key,
+    net::DrainableIOBuffer* write_nexe,
+    const CompletionCallback& callback) {
+  PnaclTranslationCacheEntry* entry(
+      new PnaclTranslationCacheEntry(cache, key, false));
+  entry->io_buf_ = write_nexe;
+  entry->write_callback_ = callback;
+  return entry;
+}
+
 PnaclTranslationCacheEntry::PnaclTranslationCacheEntry(
     base::WeakPtr<PnaclTranslationCache> cache,
     const std::string& key,
-    std::string* read_nexe,
-    const std::string& write_nexe,
-    const CompletionCallback& callback,
     bool is_read)
     : cache_(cache),
       key_(key),
-      read_nexe_(read_nexe),
-      write_nexe_(write_nexe),
       entry_(NULL),
       step_(UNINITIALIZED),
-      is_read_(is_read),
-      bytes_transferred_(0),
-      bytes_to_transfer_(-1),
-      finish_callback_(callback) {}
+      is_read_(is_read) {}
 
 PnaclTranslationCacheEntry::~PnaclTranslationCacheEntry() {
   // Ensure we have called the user's callback
-  DCHECK(finish_callback_.is_null());
+  DCHECK(read_callback_.is_null());
+  DCHECK(write_callback_.is_null());
 }
 
 void PnaclTranslationCacheEntry::Start() {
@@ -154,12 +174,11 @@
 }
 
 void PnaclTranslationCacheEntry::WriteEntry(int offset, int len) {
-  scoped_refptr<net::StringIOBuffer> io_buf =
-      new net::StringIOBuffer(write_nexe_.substr(offset, len));
+  DCHECK(io_buf_->BytesRemaining() == len);
   int rv = entry_->WriteData(
       1,
       offset,
-      io_buf.get(),
+      io_buf_.get(),
       len,
       base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this),
       false);
@@ -168,25 +187,16 @@
 }
 
 void PnaclTranslationCacheEntry::ReadEntry(int offset, int len) {
-  read_buf_ = new net::IOBufferWithSize(len);
   int rv = entry_->ReadData(
       1,
       offset,
-      read_buf_.get(),
+      io_buf_.get(),
       len,
       base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this));
   if (rv != net::ERR_IO_PENDING)
     DispatchNext(rv);
 }
 
-int PnaclTranslationCacheEntry::GetTransferSize() {
-  if (is_read_) {
-    DCHECK(entry_);
-    return entry_->GetDataSize(1);
-  }
-  return write_nexe_.size();
-}
-
 void PnaclTranslationCacheEntry::CloseEntry(int rv) {
   DCHECK(entry_);
   if (rv < 0)
@@ -197,9 +207,16 @@
 }
 
 void PnaclTranslationCacheEntry::Finish(int rv) {
-  if (!finish_callback_.is_null()) {
-    finish_callback_.Run(rv);
-    finish_callback_.Reset();
+  if (is_read_) {
+    if (!read_callback_.is_null()) {
+      read_callback_.Run(rv, io_buf_);
+      read_callback_.Reset();
+    }
+  } else {
+    if (!write_callback_.is_null()) {
+      write_callback_.Run(rv);
+      write_callback_.Reset();
+    }
   }
   cache_->OpComplete(this);
 }
@@ -217,26 +234,30 @@
     case OPEN_ENTRY:
       if (rv == net::OK) {
         step_ = TRANSFER_ENTRY;
-        bytes_to_transfer_ = GetTransferSize();
-        is_read_ ? ReadEntry(0, bytes_to_transfer_)
-                 : WriteEntry(0, bytes_to_transfer_);
+        if (is_read_) {
+          int bytes_to_transfer = entry_->GetDataSize(1);
+          io_buf_ = new net::DrainableIOBuffer(
+              new net::IOBuffer(bytes_to_transfer), bytes_to_transfer);
+          ReadEntry(0, bytes_to_transfer);
+        } else {
+          WriteEntry(0, io_buf_->size());
+        }
       } else {
         if (is_read_) {
           // Just a cache miss, not necessarily an error.
           entry_ = NULL;
           Finish(rv);
-          break;
+        } else {
+          step_ = CREATE_ENTRY;
+          CreateEntry();
         }
-        step_ = CREATE_ENTRY;
-        CreateEntry();
       }
       break;
 
     case CREATE_ENTRY:
       if (rv == net::OK) {
         step_ = TRANSFER_ENTRY;
-        bytes_to_transfer_ = GetTransferSize();
-        WriteEntry(bytes_transferred_, bytes_to_transfer_ - bytes_transferred_);
+        WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
       } else {
         LOG(ERROR) << "Failed to Create a PNaCl Translation Cache Entry";
         Finish(rv);
@@ -254,19 +275,19 @@
         CloseEntry(rv);
         break;
       } else if (rv > 0) {
-        // For reads, copy the data that was just returned
-        if (is_read_)
-          read_nexe_->append(read_buf_->data(), rv);
-        bytes_transferred_ += rv;
-        if (bytes_transferred_ < bytes_to_transfer_) {
-          int len = bytes_to_transfer_ - bytes_transferred_;
-          is_read_ ? ReadEntry(bytes_transferred_, len)
-                   : WriteEntry(bytes_transferred_, len);
+        io_buf_->DidConsume(rv);
+        if (io_buf_->BytesRemaining() > 0) {
+          is_read_
+              ? ReadEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining())
+              : WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
           break;
         }
       }
       // rv == 0 or we fell through (i.e. we have transferred all the bytes)
       step_ = CLOSE_ENTRY;
+      DCHECK(io_buf_->BytesConsumed() == io_buf_->size());
+      if (is_read_)
+        io_buf_->SetOffset(0);
       CloseEntry(0);
       break;
 
@@ -283,10 +304,9 @@
 
 //////////////////////////////////////////////////////////////////////
 // Construction and cache backend initialization
-PnaclTranslationCache::PnaclTranslationCache()
-    : disk_cache_(NULL), in_memory_(false) {}
+PnaclTranslationCache::PnaclTranslationCache() : in_memory_(false) {}
 
-PnaclTranslationCache::~PnaclTranslationCache() { delete disk_cache_; }
+PnaclTranslationCache::~PnaclTranslationCache() {}
 
 int PnaclTranslationCache::InitWithDiskBackend(
     const base::FilePath& cache_dir,
@@ -334,24 +354,23 @@
 // High-level API
 
 void PnaclTranslationCache::StoreNexe(const std::string& key,
-                                      const std::string& nexe) {
-  StoreNexe(key, nexe, CompletionCallback());
+                                      net::DrainableIOBuffer* nexe_data) {
+  StoreNexe(key, nexe_data, CompletionCallback());
 }
 
 void PnaclTranslationCache::StoreNexe(const std::string& key,
-                                      const std::string& nexe,
+                                      net::DrainableIOBuffer* nexe_data,
                                       const CompletionCallback& callback) {
-  PnaclTranslationCacheEntry* entry = new PnaclTranslationCacheEntry(
-      AsWeakPtr(), key, NULL, nexe, callback, false);
+  PnaclTranslationCacheEntry* entry = PnaclTranslationCacheEntry::GetWriteEntry(
+      AsWeakPtr(), key, nexe_data, callback);
   open_entries_[entry] = entry;
   entry->Start();
 }
 
 void PnaclTranslationCache::GetNexe(const std::string& key,
-                                    std::string* nexe,
-                                    const CompletionCallback& callback) {
-  PnaclTranslationCacheEntry* entry = new PnaclTranslationCacheEntry(
-      AsWeakPtr(), key, nexe, std::string(), callback, true);
+                                    const GetNexeCallback& callback) {
+  PnaclTranslationCacheEntry* entry =
+      PnaclTranslationCacheEntry::GetReadEntry(AsWeakPtr(), key, callback);
   open_entries_[entry] = entry;
   entry->Start();
 }
@@ -364,9 +383,7 @@
   if (in_memory_) {
     rv = InitWithMemBackend(kMaxMemCacheSize, callback);
   } else {
-    rv = InitWithDiskBackend(cache_directory,
-                             kMaxDiskCacheSize,
-                             callback);
+    rv = InitWithDiskBackend(cache_directory, kMaxDiskCacheSize, callback);
   }
 
   return rv;
@@ -378,4 +395,36 @@
   return disk_cache_->GetEntryCount();
 }
 
+// static
+std::string PnaclTranslationCache::GetKey(const nacl::PnaclCacheInfo& info) {
+  if (!info.pexe_url.is_valid() || info.abi_version < 0 || info.opt_level < 0)
+    return std::string();
+  std::string retval("ABI:");
+  retval += IntToString(info.abi_version) + ";" +
+      "opt:" + IntToString(info.opt_level) + ";" +
+      "URL:";
+  // Filter the username, password, and ref components from the URL
+  GURL::Replacements replacements;
+  replacements.ClearUsername();
+  replacements.ClearPassword();
+  replacements.ClearRef();
+  GURL key_url(info.pexe_url.ReplaceComponents(replacements));
+  retval += key_url.spec() + ";";
+  // You would think that there is already code to format base::Time values
+  // somewhere, but I haven't found it yet. In any case, doing it ourselves
+  // here means we can keep the format stable.
+  base::Time::Exploded exploded;
+  info.last_modified.UTCExplode(&exploded);
+  if (info.last_modified.is_null() || !exploded.HasValidValues()) {
+    memset(&exploded, 0, sizeof(exploded));
+  }
+  retval += "modified:" + IntToString(exploded.year) + ":" +
+      IntToString(exploded.month) + ":" +
+      IntToString(exploded.day_of_month) + ":" +
+      IntToString(exploded.hour) + ":" + IntToString(exploded.minute) + ":" +
+      IntToString(exploded.second) + ":" +
+      IntToString(exploded.millisecond) + ":UTC;";
+  retval += "etag:" + info.etag;
+  return retval;
+}
 }  // namespace pnacl
diff --git a/chrome/browser/nacl_host/pnacl_translation_cache.h b/chrome/browser/nacl_host/pnacl_translation_cache.h
index 96f03c0..8f2d9ac 100644
--- a/chrome/browser/nacl_host/pnacl_translation_cache.h
+++ b/chrome/browser/nacl_host/pnacl_translation_cache.h
@@ -20,8 +20,18 @@
 class Backend;
 }
 
+namespace nacl {
+struct PnaclCacheInfo;
+}
+
+namespace net {
+class DrainableIOBuffer;
+}
+
 namespace pnacl {
 typedef base::Callback<void(int)> CompletionCallback;
+typedef base::Callback<void(int, scoped_refptr<net::DrainableIOBuffer>)>
+    GetNexeCallback;
 class PnaclTranslationCacheEntry;
 extern const int kMaxMemCacheSize;
 
@@ -38,31 +48,36 @@
                 bool in_memory,
                 const CompletionCallback& callback);
 
-  // Store the nexe in the translation cache.
-  void StoreNexe(const std::string& key, const std::string& nexe);
+  // Store the nexe in the translation cache. A reference to |nexe_data| is
+  // held until completion or cancellation.
+  void StoreNexe(const std::string& key, net::DrainableIOBuffer* nexe_data);
 
   // Store the nexe in the translation cache, and call |callback| with
   // the result. The result passed to the callback is 0 on success and
-  // <0 otherwise.
+  // <0 otherwise. A reference to |nexe_data| is held until completion
+  // or cancellation.
   void StoreNexe(const std::string& key,
-                 const std::string& nexe,
+                 net::DrainableIOBuffer* nexe_data,
                  const CompletionCallback& callback);
 
   // Retrieve the nexe from the translation cache. Write the data into |nexe|
-  // and call |callback| with the result (0 on success and <0 otherwise)
-  void GetNexe(const std::string& key,
-               std::string* nexe,
-               const CompletionCallback& callback);
+  // and call |callback|, passing a result code (0 on success and <0 otherwise),
+  // and a DrainableIOBuffer with the data.
+  void GetNexe(const std::string& key, const GetNexeCallback& callback);
 
   // Return the number of entries in the cache backend.
   int Size();
 
+  // Return the cache key for |info|
+  static std::string GetKey(const nacl::PnaclCacheInfo& info);
+
  private:
   friend class PnaclTranslationCacheEntry;
+  friend class PnaclTranslationCacheTest;
   // PnaclTranslationCacheEntry should only use the
   // OpComplete and backend methods on PnaclTranslationCache.
   void OpComplete(PnaclTranslationCacheEntry* entry);
-  disk_cache::Backend* backend() { return disk_cache_; }
+  disk_cache::Backend* backend() { return disk_cache_.get(); }
 
   int InitWithDiskBackend(const base::FilePath& disk_cache_dir,
                           int cache_size,
@@ -77,7 +92,7 @@
 
   void OnCreateBackendComplete(int rv);
 
-  disk_cache::Backend* disk_cache_;
+  scoped_ptr<disk_cache::Backend> disk_cache_;
   CompletionCallback init_callback_;
   bool in_memory_;
   std::map<void*, scoped_refptr<PnaclTranslationCacheEntry> > open_entries_;
diff --git a/chrome/browser/nacl_host/pnacl_translation_cache_unittest.cc b/chrome/browser/nacl_host/pnacl_translation_cache_unittest.cc
index b2e8061..16138fd 100644
--- a/chrome/browser/nacl_host/pnacl_translation_cache_unittest.cc
+++ b/chrome/browser/nacl_host/pnacl_translation_cache_unittest.cc
@@ -8,8 +8,10 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "components/nacl/common/pnacl_types.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "net/base/io_buffer.h"
 #include "net/base/test_completion_callback.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -38,7 +40,6 @@
   void StoreNexe(const std::string& key, const std::string& nexe);
   std::string GetNexe(const std::string& key);
 
- protected:
   PnaclTranslationCache* cache_;
   content::TestBrowserThreadBundle thread_bundle_;
   base::ScopedTempDir temp_dir_;
@@ -59,24 +60,118 @@
 void PnaclTranslationCacheTest::StoreNexe(const std::string& key,
                                           const std::string& nexe) {
   net::TestCompletionCallback store_cb;
-  cache_->StoreNexe(key, nexe, store_cb.callback());
+  scoped_refptr<net::DrainableIOBuffer> nexe_buf(
+      new net::DrainableIOBuffer(new net::StringIOBuffer(nexe), nexe.size()));
+  cache_->StoreNexe(key, nexe_buf, store_cb.callback());
   // Using ERR_IO_PENDING here causes the callback to wait for the result
   // which should be harmless even if it returns OK immediately. This is because
   // we don't plumb the intermediate writing stages all the way out.
   EXPECT_EQ(net::OK, store_cb.GetResult(net::ERR_IO_PENDING));
 }
 
+// Inspired by net::TestCompletionCallback. Instantiate a TestNexeCallback and
+// pass the GetNexeCallback returned by the callback() method to GetNexe.
+// Then call GetResult, which will pump the message loop until it gets a result,
+// return the resulting IOBuffer and fill in the return value
+class TestNexeCallback {
+ public:
+  TestNexeCallback()
+      : have_result_(false),
+        result_(-1),
+        cb_(base::Bind(&TestNexeCallback::SetResult, base::Unretained(this))) {}
+  GetNexeCallback callback() { return cb_; }
+  net::DrainableIOBuffer* GetResult(int* result) {
+    while (!have_result_)
+      base::RunLoop().RunUntilIdle();
+    have_result_ = false;
+    *result = result_;
+    return buf_.get();
+  }
+
+ private:
+  void SetResult(int rv, scoped_refptr<net::DrainableIOBuffer> buf) {
+    have_result_ = true;
+    result_ = rv;
+    buf_ = buf;
+  }
+  bool have_result_;
+  int result_;
+  scoped_refptr<net::DrainableIOBuffer> buf_;
+  const GetNexeCallback cb_;
+};
+
 std::string PnaclTranslationCacheTest::GetNexe(const std::string& key) {
-  net::TestCompletionCallback load_cb;
-  std::string nexe;
-  cache_->GetNexe(key, &nexe, load_cb.callback());
-  EXPECT_EQ(net::OK, load_cb.GetResult(net::ERR_IO_PENDING));
+  TestNexeCallback load_cb;
+  cache_->GetNexe(key, load_cb.callback());
+  int rv;
+  scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv));
+  EXPECT_EQ(net::OK, rv);
+  std::string nexe(buf->data(), buf->size());
   return nexe;
 }
 
 static const std::string test_key("1");
 static const std::string test_store_val("testnexe");
-static const int kLargeNexeSize = 16 * 1024 *1024;
+static const int kLargeNexeSize = 16 * 1024 * 1024;
+
+TEST(PnaclTranslationCacheKeyTest, CacheKeyTest) {
+  nacl::PnaclCacheInfo info;
+  info.pexe_url = GURL("http://www.google.com");
+  info.abi_version = 0;
+  info.opt_level = 0;
+  std::string test_time("Wed, 15 Nov 1995 06:25:24 GMT");
+  base::Time::FromString(test_time.c_str(), &info.last_modified);
+  // Basic check for URL and time components
+  EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/;"
+            "modified:1995:11:15:6:25:24:0:UTC;etag:",
+            PnaclTranslationCache::GetKey(info));
+  // Check that query portion of URL is not stripped
+  info.pexe_url = GURL("http://www.google.com/?foo=bar");
+  EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/?foo=bar;"
+            "modified:1995:11:15:6:25:24:0:UTC;etag:",
+            PnaclTranslationCache::GetKey(info));
+  // Check that username, password, and normal port are stripped
+  info.pexe_url = GURL("https://user:host@www.google.com:443/");
+  EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com/;"
+            "modified:1995:11:15:6:25:24:0:UTC;etag:",
+            PnaclTranslationCache::GetKey(info));
+  // Check that unusual port is not stripped but ref is stripped
+  info.pexe_url = GURL("https://www.google.com:444/#foo");
+  EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com:444/;"
+            "modified:1995:11:15:6:25:24:0:UTC;etag:",
+            PnaclTranslationCache::GetKey(info));
+  // Check chrome-extesnsion scheme
+  info.pexe_url = GURL("chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/");
+  EXPECT_EQ("ABI:0;opt:0;"
+            "URL:chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/;"
+            "modified:1995:11:15:6:25:24:0:UTC;etag:",
+            PnaclTranslationCache::GetKey(info));
+  // Check that ABI version, opt level, and etag are in the key
+  info.pexe_url = GURL("http://www.google.com/");
+  info.abi_version = 2;
+  EXPECT_EQ("ABI:2;opt:0;URL:http://www.google.com/;"
+            "modified:1995:11:15:6:25:24:0:UTC;etag:",
+            PnaclTranslationCache::GetKey(info));
+  info.opt_level = 2;
+  EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
+            "modified:1995:11:15:6:25:24:0:UTC;etag:",
+            PnaclTranslationCache::GetKey(info));
+  info.etag = std::string("etag");
+  EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
+            "modified:1995:11:15:6:25:24:0:UTC;etag:etag",
+            PnaclTranslationCache::GetKey(info));
+
+  // Check for all the time components, and null time
+  info.last_modified = base::Time();
+  EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
+            "modified:0:0:0:0:0:0:0:UTC;etag:etag",
+            PnaclTranslationCache::GetKey(info));
+  test_time.assign("Fri, 29 Feb 2008 13:04:12 GMT");
+  base::Time::FromString(test_time.c_str(), &info.last_modified);
+  EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
+            "modified:2008:2:29:13:4:12:0:UTC;etag:etag",
+            PnaclTranslationCache::GetKey(info));
+}
 
 TEST_F(PnaclTranslationCacheTest, StoreSmallInMem) {
   // Test that a single store puts something in the mem backend
@@ -94,9 +189,6 @@
 
 TEST_F(PnaclTranslationCacheTest, StoreLargeOnDisk) {
   // Test a value too large(?) for a single I/O operation
-  // TODO(dschuff): we only seem to ever have one operation go through into the
-  // backend. Find out what the 'offset' field means, and if it can ever require
-  // multiple writes.
   InitBackend(false);
   const std::string large_buffer(kLargeNexeSize, 'a');
   StoreNexe(test_key, large_buffer);
@@ -105,7 +197,9 @@
 
 TEST_F(PnaclTranslationCacheTest, InMemSizeLimit) {
   InitBackend(true);
-  const std::string large_buffer(kMaxMemCacheSize + 1, 'a');
+  scoped_refptr<net::DrainableIOBuffer> large_buffer(new net::DrainableIOBuffer(
+      new net::StringIOBuffer(std::string(kMaxMemCacheSize + 1, 'a')),
+      kMaxMemCacheSize + 1));
   net::TestCompletionCallback store_cb;
   cache_->StoreNexe(test_key, large_buffer, store_cb.callback());
   EXPECT_EQ(net::ERR_FAILED, store_cb.GetResult(net::ERR_IO_PENDING));
@@ -120,6 +214,13 @@
   EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
 }
 
+TEST_F(PnaclTranslationCacheTest, GetOneOnDisk) {
+  InitBackend(false);
+  StoreNexe(test_key, test_store_val);
+  EXPECT_EQ(1, cache_->Size());
+  EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
+}
+
 TEST_F(PnaclTranslationCacheTest, GetLargeOnDisk) {
   InitBackend(false);
   const std::string large_buffer(kLargeNexeSize, 'a');
@@ -149,10 +250,12 @@
 TEST_F(PnaclTranslationCacheTest, GetMiss) {
   InitBackend(true);
   StoreNexe(test_key, test_store_val);
-  net::TestCompletionCallback load_cb;
+  TestNexeCallback load_cb;
   std::string nexe;
-  cache_->GetNexe(test_key + "a", &nexe, load_cb.callback());
-  EXPECT_EQ(net::ERR_FAILED, load_cb.GetResult(net::ERR_IO_PENDING));
+  cache_->GetNexe(test_key + "a", load_cb.callback());
+  int rv;
+  scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv));
+  EXPECT_EQ(net::ERR_FAILED, rv);
 }
 
 }  // namespace pnacl
diff --git a/chrome/browser/net/dns_probe_service.cc b/chrome/browser/net/dns_probe_service.cc
index 36e28af..c2fe4ae 100644
--- a/chrome/browser/net/dns_probe_service.cc
+++ b/chrome/browser/net/dns_probe_service.cc
@@ -72,50 +72,9 @@
 void HistogramProbe(DnsProbeStatus status, base::TimeDelta elapsed) {
   DCHECK(chrome_common_net::DnsProbeStatusIsFinished(status));
 
-  int result = status - chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE;
-
-  const int kMaxResult = chrome_common_net::DNS_PROBE_MAX -
-                         chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE;
-
-  UMA_HISTOGRAM_ENUMERATION("DnsProbe.Status", result, kMaxResult);
-  UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed", elapsed);
-
-  if (NetworkChangeNotifier::IsOffline()) {
-    UMA_HISTOGRAM_ENUMERATION("DnsProbe.Status_NcnOffline",
-                              result, kMaxResult);
-    UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_NcnOffline", elapsed);
-  } else {
-    UMA_HISTOGRAM_ENUMERATION("DnsProbe.Status_NcnOnline",
-                              result, kMaxResult);
-    UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_NcnOnline", elapsed);
-  }
-
-  switch (status) {
-    case chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE:
-      UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_Unknown",
-                                 elapsed);
-      break;
-    case chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET:
-      UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_NoInternet",
-                                 elapsed);
-      break;
-    case chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG:
-      UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_BadConfig",
-                                 elapsed);
-      break;
-    case chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN:
-      UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.Elapsed_Nxdomain",
-                                 elapsed);
-      break;
-
-    // These aren't actually results.
-    case chrome_common_net::DNS_PROBE_POSSIBLE:
-    case chrome_common_net::DNS_PROBE_NOT_RUN:
-    case chrome_common_net::DNS_PROBE_STARTED:
-    case chrome_common_net::DNS_PROBE_MAX:
-      NOTREACHED();
-      break;
-  }
+  UMA_HISTOGRAM_ENUMERATION("DnsProbe.ProbeResult", status,
+                            chrome_common_net::DNS_PROBE_MAX);
+  UMA_HISTOGRAM_MEDIUM_TIMES("DnsProbe.ProbeDuration", elapsed);
 }
 
 }  // namespace
diff --git a/chrome/browser/net/network_stats.cc b/chrome/browser/net/network_stats.cc
index a118927..23a8a6a 100644
--- a/chrome/browser/net/network_stats.cc
+++ b/chrome/browser/net/network_stats.cc
@@ -112,7 +112,8 @@
                             uint32 max,
                             uint32 bucket_count) {
   base::HistogramBase* histogram_pointer = base::Histogram::FactoryGet(
-      name, min, max, bucket_count, base::HistogramBase::kNoFlags);
+      name, min, max, bucket_count,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
   histogram_pointer->Add(sample);
 }
 
@@ -562,7 +563,7 @@
 
 void NetworkStats::RecordInterArrivalHistograms(TestType test_type) {
   std::string histogram_name =
-      base::StringPrintf("NetConnectivity4.%s.Sent%02d.PacketDelay.%d.%dB",
+      base::StringPrintf("NetConnectivity4.%s.Sent%d.PacketDelay.%d.%dB",
                          kTestName[test_type],
                          maximum_sequential_packets_,
                          histogram_port_,
@@ -574,7 +575,7 @@
 void NetworkStats::RecordPacketsReceivedHistograms(TestType test_type) {
   const char* test_name = kTestName[test_type];
   std::string histogram_prefix = base::StringPrintf(
-      "NetConnectivity4.%s.Sent%02d.", test_name, maximum_sequential_packets_);
+      "NetConnectivity4.%s.Sent%d.", test_name, maximum_sequential_packets_);
   std::string histogram_suffix =
       base::StringPrintf(".%d.%dB", histogram_port_, probe_packet_bytes_);
   std::string name = histogram_prefix + "GotAPacket" + histogram_suffix;
@@ -686,7 +687,7 @@
     return;  // Probe packet never received.
 
   std::string rtt_histogram_name = base::StringPrintf(
-      "NetConnectivity4.%s.Sent%02d.Success.RTT.Packet%02d.%d.%dB",
+      "NetConnectivity4.%s.Sent%d.Success.RTT.Packet%02d.%d.%dB",
       kTestName[test_type],
       maximum_sequential_packets_,
       index + 1,
@@ -701,7 +702,7 @@
   uint32 packets_sent = test_type == NAT_BIND_TEST
       ? maximum_NAT_packets_ : maximum_sequential_packets_;
   std::string histogram_name = base::StringPrintf(
-      "NetConnectivity4.%s.Sent%02d.SendToLastRecvDelay.%d.%dB",
+      "NetConnectivity4.%s.Sent%d.SendToLastRecvDelay.%d.%dB",
       kTestName[test_type],
       packets_sent,
       histogram_port_,
diff --git a/chrome/browser/notifications/desktop_notification_service.cc b/chrome/browser/notifications/desktop_notification_service.cc
index 577bf50..45eb30c 100644
--- a/chrome/browser/notifications/desktop_notification_service.cc
+++ b/chrome/browser/notifications/desktop_notification_service.cc
@@ -358,11 +358,15 @@
   if (!enabled_sync_notifier_ids_.empty()) {
     notifier::ChromeNotifierService* notifier_service =
         notifier::ChromeNotifierServiceFactory::GetInstance()->GetForProfile(
-            profile, Profile::EXPLICIT_ACCESS);
-    for (std::set<std::string>::const_iterator it =
-             enabled_sync_notifier_ids_.begin();
-         it != enabled_sync_notifier_ids_.end(); ++it) {
-      notifier_service->OnSyncedNotificationServiceEnabled(*it, true);
+            profile_, Profile::EXPLICIT_ACCESS);
+    // incognito profiles have enabled sync notifier ids but not a notifier
+    // service.
+    if (notifier_service) {
+      for (std::set<std::string>::const_iterator it =
+               enabled_sync_notifier_ids_.begin();
+           it != enabled_sync_notifier_ids_.end(); ++it) {
+        notifier_service->OnSyncedNotificationServiceEnabled(*it, true);
+      }
     }
   }
 }
diff --git a/chrome/browser/notifications/message_center_notification_manager.cc b/chrome/browser/notifications/message_center_notification_manager.cc
index 76458b4..0ccc840 100644
--- a/chrome/browser/notifications/message_center_notification_manager.cc
+++ b/chrome/browser/notifications/message_center_notification_manager.cc
@@ -164,7 +164,12 @@
 
 bool MessageCenterNotificationManager::UpdateNotification(
     const Notification& notification, Profile* profile) {
-  if (message_center_->IsMessageCenterVisible())
+  // Only progress notification update can be reflected immediately in the
+  // message center.
+  bool update_progress_notification =
+      notification.type() == message_center::NOTIFICATION_TYPE_PROGRESS;
+  bool is_message_center_visible = message_center_->IsMessageCenterVisible();
+  if (!update_progress_notification && is_message_center_visible)
     return false;
 
   const string16& replace_id = notification.replace_id();
@@ -183,13 +188,20 @@
     if (old_notification->notification().replace_id() == replace_id &&
         old_notification->notification().origin_url() == origin_url &&
         old_notification->profile() == profile) {
+      // Changing the type from non-progress to progress does not count towards
+      // the immediate update allowed in the message center.
+      if (update_progress_notification && is_message_center_visible &&
+          old_notification->notification().type() !=
+              message_center::NOTIFICATION_TYPE_PROGRESS) {
+        return false;
+      }
+
       std::string old_id =
           old_notification->notification().notification_id();
       DCHECK(message_center_->HasNotification(old_id));
 
       // Add/remove notification in the local list but just update the same
       // one in MessageCenter.
-      old_notification->notification().Close(false); // Not by user.
       delete old_notification;
       profile_notifications_.erase(old_id);
       ProfileNotification* new_notification =
diff --git a/chrome/browser/notifications/message_center_notifications_browsertest.cc b/chrome/browser/notifications/message_center_notifications_browsertest.cc
index 2d2b78b..28b4728 100644
--- a/chrome/browser/notifications/message_center_notifications_browsertest.cc
+++ b/chrome/browser/notifications/message_center_notifications_browsertest.cc
@@ -6,7 +6,6 @@
 
 #include "base/command_line.h"
 #include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -17,40 +16,38 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/message_center_switches.h"
 #include "ui/message_center/message_center_util.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 class TestAddObserver : public message_center::MessageCenterObserver {
  public:
-  TestAddObserver(const std::string& id,
-                  message_center::MessageCenter* message_center)
-      : id_(id), message_center_(message_center) {
-    quit_closure_ = run_loop_.QuitClosure();
+  explicit TestAddObserver(message_center::MessageCenter* message_center)
+      : message_center_(message_center) {
     message_center_->AddObserver(this);
   }
 
   virtual ~TestAddObserver() { message_center_->RemoveObserver(this); }
 
   virtual void OnNotificationAdded(const std::string& id) OVERRIDE {
-    log_ += "_" + id;
-    if (id == id_)
-      base::MessageLoop::current()->PostTask(FROM_HERE, quit_closure_);
+    if (log_ != "")
+      log_ += "_";
+    log_ += "add-" + id;
   }
 
-  void Run() { run_loop_.Run(); }
+  virtual void OnNotificationUpdated(const std::string& id) OVERRIDE {
+    if (log_ != "")
+      log_ += "_";
+    log_ += "update-" + id;
+  }
+
   const std::string log() const { return log_; }
+  void reset_log() { log_ = ""; }
 
  private:
-  std::string id_;
   std::string log_;
   message_center::MessageCenter* message_center_;
-  base::RunLoop run_loop_;
-  base::Closure quit_closure_;
 };
 
 class MessageCenterNotificationsTest : public InProcessBrowserTest {
@@ -168,7 +165,7 @@
 IN_PROC_BROWSER_TEST_F(MessageCenterNotificationsTest, MAYBE_BasicAddCancel) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -189,7 +186,7 @@
 IN_PROC_BROWSER_TEST_F(MessageCenterNotificationsTest, MAYBE_BasicDelegate) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -215,7 +212,7 @@
                        MAYBE_ButtonClickedDelegate) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -239,7 +236,7 @@
                        MAYBE_UpdateExistingNotification) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -250,7 +247,7 @@
   manager()->Add(CreateRichTestNotification("n", &delegate2), profile());
 
   manager()->CancelById("n");
-  EXPECT_EQ("Display_Close_programmatically_", delegate->log());
+  EXPECT_EQ("Display_", delegate->log());
   EXPECT_EQ("Close_programmatically_", delegate2->log());
 
   delegate->Release();
@@ -268,12 +265,12 @@
                        MAYBE_QueueWhenCenterVisible) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
   EXPECT_TRUE(NotificationUIManager::DelegatesToMessageCenter());
-  TestAddObserver observer("n2", message_center());
+  TestAddObserver observer(message_center());
 
   TestDelegate* delegate;
   TestDelegate* delegate2;
@@ -282,15 +279,130 @@
   message_center()->SetMessageCenterVisible(true);
   manager()->Add(CreateTestNotification("n2", &delegate2), profile());
 
-  EXPECT_EQ("_n", observer.log());
+  EXPECT_EQ("add-n", observer.log());
 
   message_center()->SetMessageCenterVisible(false);
-  observer.Run();
 
-  EXPECT_EQ("_n_n2", observer.log());
+  EXPECT_EQ("add-n_add-n2", observer.log());
 
   delegate->Release();
   delegate2->Release();
 }
 
+// MessaceCenter-specific test.
+#if defined(RUN_MESSAGE_CENTER_TESTS)
+#define MAYBE_UpdateNonProgressNotificationWhenCenterVisible \
+    UpdateNonProgressNotificationWhenCenterVisible
+#else
+#define MAYBE_UpdateNonProgressNotificationWhenCenterVisible \
+    DISABLED_UpdateNonProgressNotificationWhenCenterVisible
+#endif
+
+IN_PROC_BROWSER_TEST_F(MessageCenterNotificationsTest,
+                       MAYBE_UpdateNonProgressNotificationWhenCenterVisible) {
+#if defined(OS_WIN) && defined(USE_ASH)
+  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
+    return;
+#endif
+
+  EXPECT_TRUE(NotificationUIManager::DelegatesToMessageCenter());
+  TestAddObserver observer(message_center());
+
+  TestDelegate* delegate;
+
+  // Add a non-progress notification and update it while the message center
+  // is visible.
+  Notification notification = CreateTestNotification("n", &delegate);
+  manager()->Add(notification, profile());
+  message_center()->ClickOnNotification("n");
+  message_center()->SetMessageCenterVisible(true);
+  observer.reset_log();
+  notification.set_title(ASCIIToUTF16("title2"));
+  manager()->Update(notification, profile());
+
+  // Expect that the notification update is not done.
+  EXPECT_EQ("", observer.log());
+
+  delegate->Release();
+}
+
+// MessaceCenter-specific test.
+#if defined(RUN_MESSAGE_CENTER_TESTS)
+#define MAYBE_UpdateNonProgressToProgressNotificationWhenCenterVisible \
+    UpdateNonProgressToProgressNotificationWhenCenterVisible
+#else
+#define MAYBE_UpdateNonProgressToProgressNotificationWhenCenterVisible \
+    DISABLED_UpdateNonProgressToProgressNotificationWhenCenterVisible
+#endif
+
+IN_PROC_BROWSER_TEST_F(
+    MessageCenterNotificationsTest,
+    MAYBE_UpdateNonProgressToProgressNotificationWhenCenterVisible) {
+#if defined(OS_WIN) && defined(USE_ASH)
+  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
+    return;
+#endif
+
+  EXPECT_TRUE(NotificationUIManager::DelegatesToMessageCenter());
+  TestAddObserver observer(message_center());
+
+  TestDelegate* delegate;
+
+  // Add a non-progress notification and change the type to progress while the
+  // message center is visible.
+  Notification notification = CreateTestNotification("n", &delegate);
+  manager()->Add(notification, profile());
+  message_center()->ClickOnNotification("n");
+  message_center()->SetMessageCenterVisible(true);
+  observer.reset_log();
+  notification.set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
+  manager()->Update(notification, profile());
+
+  // Expect that the notification update is not done.
+  EXPECT_EQ("", observer.log());
+
+  delegate->Release();
+}
+
+// MessaceCenter-specific test.
+#if defined(RUN_MESSAGE_CENTER_TESTS)
+#define MAYBE_UpdateProgressNotificationWhenCenterVisible \
+    UpdateProgressNotificationWhenCenterVisible
+#else
+#define MAYBE_UpdateProgressNotificationWhenCenterVisible \
+    DISABLED_UpdateProgressNotificationWhenCenterVisible
+#endif
+
+IN_PROC_BROWSER_TEST_F(MessageCenterNotificationsTest,
+                       MAYBE_UpdateProgressNotificationWhenCenterVisible) {
+#if defined(OS_WIN) && defined(USE_ASH)
+  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
+    return;
+#endif
+
+  EXPECT_TRUE(NotificationUIManager::DelegatesToMessageCenter());
+  TestAddObserver observer(message_center());
+
+  TestDelegate* delegate;
+
+  // Add a progress notification and update it while the message center
+  // is visible.
+  Notification notification = CreateTestNotification("n", &delegate);
+  notification.set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
+  manager()->Add(notification, profile());
+  message_center()->ClickOnNotification("n");
+  message_center()->SetMessageCenterVisible(true);
+  observer.reset_log();
+  notification.set_progress(50);
+  manager()->Update(notification, profile());
+
+  // Expect that the progress notification update is performed.
+  EXPECT_EQ("update-n", observer.log());
+
+  delegate->Release();
+}
+
 #endif  // !defined(OS_MACOSX)
diff --git a/chrome/browser/notifications/notification_browsertest.cc b/chrome/browser/notifications/notification_browsertest.cc
index f015843..da54243 100644
--- a/chrome/browser/notifications/notification_browsertest.cc
+++ b/chrome/browser/notifications/notification_browsertest.cc
@@ -50,11 +50,6 @@
 #include "ui/message_center/message_center_util.h"
 #include "url/gurl.h"
 
-// TODO(kbr): remove: http://crbug.com/222296
-#if defined(OS_MACOSX)
-#import "base/mac/mac_util.h"
-#endif
-
 namespace {
 
 const char kExpectedIconUrl[] = "/notifications/no_such_file.png";
@@ -520,12 +515,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCreateSimpleNotification) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Creates a simple notification.
@@ -554,12 +543,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCloseNotification) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Creates a notification and closes it.
@@ -585,12 +568,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCancelNotification) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Creates a notification and cancels it in the origin page.
@@ -617,12 +594,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestAllowOnPermissionInfobar) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Tries to create a notification and clicks allow on the infobar.
@@ -668,12 +639,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestAllowNotificationsFromAllSites) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Verify that all domains can be allowed to show notifications.
@@ -718,12 +683,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestAllowDomainAndDenyAll) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Verify that allowing a domain and denying all others should show
@@ -740,12 +699,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestDenyAndThenAllowDomain) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Verify that denying and again allowing should show notifications.
@@ -768,12 +721,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCreateDenyCloseNotifications) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Verify able to create, deny, and close the notification.
@@ -870,12 +817,6 @@
   if (message_center::IsRichNotificationEnabled())
     return;
 
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Test killing a notification doesn't crash Chrome.
@@ -891,12 +832,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestIncognitoNotification) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Test notifications in incognito window.
@@ -932,12 +867,6 @@
 IN_PROC_BROWSER_TEST_F(
     NotificationsTest,
     TestNavigateAwayWithPermissionInfobar) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Test navigating away when an infobar is present,
@@ -968,12 +897,6 @@
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest,
                        MAYBE_TestCrashRendererNotificationRemain) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Test crashing renderer does not close or crash notification.
@@ -992,12 +915,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestNotificationReplacement) {
-#if defined(OS_MACOSX)
-  // TODO(kbr): re-enable: http://crbug.com/222296
-  if (base::mac::IsOSMountainLionOrLater())
-    return;
-#endif
-
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Test that we can replace a notification using the replaceId.
diff --git a/chrome/browser/notifications/notification_ui_manager.h b/chrome/browser/notifications/notification_ui_manager.h
index a86f1d2..5018e0e 100644
--- a/chrome/browser/notifications/notification_ui_manager.h
+++ b/chrome/browser/notifications/notification_ui_manager.h
@@ -26,8 +26,11 @@
   static NotificationUIManager* Create(PrefService* local_state);
 
   // Adds a notification to be displayed. Virtual for unit test override.
-  virtual void Add(const Notification& notification,
-                   Profile* profile) = 0;
+  virtual void Add(const Notification& notification, Profile* profile) = 0;
+
+  // Updates an existing notification. If |update_progress_only|, assume
+  // only message and progress properties are updated.
+  virtual bool Update(const Notification& notification, Profile* profile) = 0;
 
   // Returns the pointer to a notification if it match the supplied ID, either
   // currently displayed or in the queue.
diff --git a/chrome/browser/notifications/notification_ui_manager_impl.cc b/chrome/browser/notifications/notification_ui_manager_impl.cc
index d2b08d0..258c84b 100644
--- a/chrome/browser/notifications/notification_ui_manager_impl.cc
+++ b/chrome/browser/notifications/notification_ui_manager_impl.cc
@@ -63,7 +63,7 @@
 
 void NotificationUIManagerImpl::Add(const Notification& notification,
                                     Profile* profile) {
-  if (TryReplacement(notification, profile)) {
+  if (Update(notification, profile)) {
     return;
   }
 
@@ -74,6 +74,30 @@
   CheckAndShowNotifications();
 }
 
+bool NotificationUIManagerImpl::Update(const Notification& notification,
+                                       Profile* profile) {
+  const GURL& origin = notification.origin_url();
+  const string16& replace_id = notification.replace_id();
+
+  if (replace_id.empty())
+    return false;
+
+  // First check the queue of pending notifications for replacement.
+  // Then check the list of notifications already being shown.
+  for (NotificationDeque::const_iterator iter = show_queue_.begin();
+       iter != show_queue_.end(); ++iter) {
+    if (profile == (*iter)->profile() &&
+        origin == (*iter)->notification().origin_url() &&
+        replace_id == (*iter)->notification().replace_id()) {
+      (*iter)->Replace(notification);
+      return true;
+    }
+  }
+
+  // Give the subclass the opportunity to update existing notification.
+  return UpdateNotification(notification, profile);
+}
+
 const Notification* NotificationUIManagerImpl::FindById(
     const std::string& id) const {
   for (NotificationDeque::const_iterator iter = show_queue_.begin();
@@ -189,30 +213,6 @@
   }
 }
 
-bool NotificationUIManagerImpl::TryReplacement(
-    const Notification& notification, Profile* profile) {
-  const GURL& origin = notification.origin_url();
-  const string16& replace_id = notification.replace_id();
-
-  if (replace_id.empty())
-    return false;
-
-  // First check the queue of pending notifications for replacement.
-  // Then check the list of notifications already being shown.
-  for (NotificationDeque::const_iterator iter = show_queue_.begin();
-       iter != show_queue_.end(); ++iter) {
-    if (profile == (*iter)->profile() &&
-        origin == (*iter)->notification().origin_url() &&
-        replace_id == (*iter)->notification().replace_id()) {
-      (*iter)->Replace(notification);
-      return true;
-    }
-  }
-
-  // Give the subclass the opportunity to update existing notification.
-  return UpdateNotification(notification, profile);
-}
-
 void NotificationUIManagerImpl::GetQueuedNotificationsForTesting(
     std::vector<const Notification*>* notifications) {
   for (NotificationDeque::const_iterator iter = show_queue_.begin();
diff --git a/chrome/browser/notifications/notification_ui_manager_impl.h b/chrome/browser/notifications/notification_ui_manager_impl.h
index 5ced721..a56d439 100644
--- a/chrome/browser/notifications/notification_ui_manager_impl.h
+++ b/chrome/browser/notifications/notification_ui_manager_impl.h
@@ -34,6 +34,8 @@
   // NotificationUIManager:
   virtual void Add(const Notification& notification,
                    Profile* profile) OVERRIDE;
+  virtual bool Update(const Notification& notification,
+                      Profile* profile) OVERRIDE;
   virtual const Notification* FindById(
       const std::string& notification_id) const OVERRIDE;
   virtual bool CancelById(const std::string& notification_id) OVERRIDE;
@@ -55,17 +57,17 @@
   virtual bool ShowNotification(const Notification& notification,
                                 Profile* profile) = 0;
 
- // Replace an existing notification of the same id with this one if applicable;
- // subclass returns 'true' if the replacement happened.
- virtual bool UpdateNotification(const Notification& notification,
-                                 Profile* profile) = 0;
+  // Replace an existing notification of the same id with this one if
+  // applicable; subclass returns 'true' if the replacement happened.
+  virtual bool UpdateNotification(const Notification& notification,
+                                  Profile* profile) = 0;
 
- // Attempts to display notifications from the show_queue. Invoked by subclasses
- // if they previously returned 'false' from ShowNotifications, which may happen
- // when there is no room to show another notification. When room appears, the
- // subclass should call this method to cause an attempt to show more
- // notifications from the waiting queue.
- void CheckAndShowNotifications();
+  // Attempts to display notifications from the show_queue. Invoked by subclass
+  // if it previously returned 'false' from ShowNotifications, which may happen
+  // when there is no room to show another notification. When room appears, the
+  // subclass should call this method to cause an attempt to show more
+  // notifications from the waiting queue.
+  void CheckAndShowNotifications();
 
  private:
   // content::NotificationObserver override.
@@ -76,10 +78,6 @@
   // Attempts to display notifications from the show_queue.
   void ShowNotifications();
 
-  // Replace an existing notification with this one if applicable;
-  // returns true if the replacement happened.
-  bool TryReplacement(const Notification& notification, Profile* profile);
-
   // Checks the user state to decide if we want to show the notification.
   void CheckUserState();
 
diff --git a/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc b/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc
index d19153b..8b51eb8 100644
--- a/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc
+++ b/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/notifications/notification.h"
 #include "chrome/browser/notifications/notification_ui_manager.h"
 #include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
 #include "grit/generated_resources.h"
 #include "grit/theme_resources.h"
 #include "sync/api/sync_change.h"
@@ -56,8 +57,7 @@
       const syncer::SyncDataList& initial_sync_data,
       scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
       scoped_ptr<syncer::SyncErrorFactory> error_handler) {
-  // TODO(petewil): After I add the infrastructure for the test, add a check
-  // that we are currently on the UI thread here.
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
   syncer::SyncMergeResult merge_result(syncer::SYNCED_NOTIFICATIONS);
   // A list of local changes to send up to the sync server.
@@ -159,7 +159,7 @@
 syncer::SyncError ChromeNotifierService::ProcessSyncChanges(
       const tracked_objects::Location& from_here,
       const syncer::SyncChangeList& change_list) {
-  // TODO(petewil): Add a check that we are called on the thread we expect.
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   syncer::SyncError error;
 
   for (syncer::SyncChangeList::const_iterator it = change_list.begin();
@@ -305,6 +305,7 @@
 
 void ChromeNotifierService::MarkNotificationAsDismissed(
     const std::string& key) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   SyncedNotification* notification = FindNotificationById(key);
   CHECK(notification != NULL);
 
diff --git a/chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.cc b/chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.cc
index 16216a7..97b3cea 100644
--- a/chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.cc
+++ b/chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/chrome_version_info.h"
 #include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
 
 namespace notifier {
@@ -31,6 +32,15 @@
     return false;
   if (command_line->HasSwitch(switches::kEnableSyncSyncedNotifications))
     return true;
+
+  // enable it by default for canary and dev
+  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
+  if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN ||
+      channel == chrome::VersionInfo::CHANNEL_DEV ||
+      channel == chrome::VersionInfo::CHANNEL_CANARY)
+    return true;
+  else
+
   return false;
 }
 
diff --git a/chrome/browser/notifications/sync_notifier/chrome_notifier_service_unittest.cc b/chrome/browser/notifications/sync_notifier/chrome_notifier_service_unittest.cc
index 2906a37..17fe01f 100644
--- a/chrome/browser/notifications/sync_notifier/chrome_notifier_service_unittest.cc
+++ b/chrome/browser/notifications/sync_notifier/chrome_notifier_service_unittest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.h"
 #include "chrome/browser/notifications/sync_notifier/synced_notification.h"
 #include "chrome/browser/profiles/profile.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "sync/api/sync_change.h"
 #include "sync/api/sync_change_processor.h"
 #include "sync/api/sync_error_factory.h"
@@ -63,6 +64,14 @@
     profile_ = profile;
   }
 
+  virtual bool Update(const Notification& notification, Profile* profile)
+      OVERRIDE {
+    // Make a deep copy of the notification that we can inspect.
+    notification_ = notification;
+    profile_ = profile;
+    return true;
+  }
+
   // Returns true if any notifications match the supplied ID, either currently
   // displayed or in the queue.
   virtual const Notification* FindById(const std::string& id) const OVERRIDE {
@@ -195,7 +204,7 @@
 
   TestChangeProcessor* processor() {
     return static_cast<TestChangeProcessor*>(sync_processor_.get());
-}
+  }
 
   scoped_ptr<syncer::SyncChangeProcessor> PassProcessor() {
     return sync_processor_delegate_.Pass();
@@ -232,6 +241,7 @@
  private:
   scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
   scoped_ptr<syncer::SyncChangeProcessor> sync_processor_delegate_;
+  content::TestBrowserThreadBundle thread_bundle_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeNotifierServiceTest);
 };
diff --git a/chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.cc b/chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.cc
index 11b0345..23a11dc 100644
--- a/chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.cc
+++ b/chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.cc
@@ -41,6 +41,7 @@
 const char kText5[] = "Neptune wins, pi to e";
 const char kText6[] = "Beef flavored base for soups";
 const char kText7[] = "You now have the latest version of Push Messaging App.";
+const char kText1And1[] = "Space Needle, 12:00 pm\nSpace Needle, 12:00 pm";
 const char kImageUrl1[] = "http://www.google.com/image1.jpg";
 const char kImageUrl2[] = "http://www.google.com/image2.jpg";
 const char kImageUrl3[] = "http://www.google.com/image3.jpg";
@@ -49,7 +50,7 @@
 const char kImageUrl6[] = "http://www.google.com/image6.jpg";
 const char kImageUrl7[] = "http://www.google.com/image7.jpg";
 const char kExpectedOriginUrl[] =
-    "chrome-extension://fboilmbenheemaomgaeehigklolhkhnf/";
+    "synced-notification://fboilmbenheemaomgaeehigklolhkhnf";
 const char kDefaultDestinationTitle[] = "Open web page";
 const char kDefaultDestinationIconUrl[] = "http://www.google.com/image4.jpg";
 const char kDefaultDestinationUrl[] = "chrome://flags";
diff --git a/chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.h b/chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.h
index be33257..4bd8897 100644
--- a/chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.h
+++ b/chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.h
@@ -49,6 +49,7 @@
 extern const char kText5[];
 extern const char kText6[];
 extern const char kText7[];
+extern const char kText1And1[];
 extern const char kImageUrl1[];
 extern const char kImageUrl2[];
 extern const char kImageUrl3[];
diff --git a/chrome/browser/notifications/sync_notifier/synced_notification.cc b/chrome/browser/notifications/sync_notifier/synced_notification.cc
index 7dd6c5e..5c427ff 100644
--- a/chrome/browser/notifications/sync_notifier/synced_notification.cc
+++ b/chrome/browser/notifications/sync_notifier/synced_notification.cc
@@ -21,7 +21,7 @@
 #include "ui/message_center/notification_types.h"
 
 namespace {
-const char kExtensionScheme[] = "chrome-extension://";
+const char kExtensionScheme[] = "synced-notification://";
 const char kDefaultSyncedNotificationScheme[] = "https:";
 
 // The name of our first synced notification service.
@@ -203,10 +203,11 @@
   string16 description = UTF8ToUTF16(GetDescription());
   string16 annotation = UTF8ToUTF16(GetAnnotation());
   // TODO(petewil): Eventually put the display name of the sending service here.
-  string16 display_source = UTF8ToUTF16(GetOriginUrl().spec());
+  string16 display_source = UTF8ToUTF16(GetAppId());
   string16 replace_key = UTF8ToUTF16(GetKey());
   string16 notification_heading = heading;
-  string16 notification_text = text;
+  string16 notification_text = description;
+  string16 newline = UTF8ToUTF16("\n");
 
   // The delegate will eventually catch calls that the notification
   // was read or deleted, and send the changes back to the server.
@@ -272,19 +273,10 @@
       }
     }
 
-    // Set the heading and text appropriately for the message type.
-    notification_text = annotation;
-    if (notification_type == message_center::NOTIFICATION_TYPE_IMAGE) {
-      // For an image, fill in the description field.
-      notification_text = description;
-    } else if (notification_count == 1) {
-      // For a single collapsed info entry, use the contained message if any.
-      std::string comment_body = GetContainedNotificationMessage(0);
-      std::string comment_header = GetContainedNotificationTitle(0);
-      if (!comment_header.empty() && !comment_body.empty())
-        notification_text = UTF8ToUTF16(comment_header) + UTF8ToUTF16(" ") +
-            UTF8ToUTF16(comment_body);
-    }
+    // The text encompasses both the description and the annotation.
+    if (!notification_text.empty())
+      notification_text = notification_text + newline;
+    notification_text = notification_text + annotation;
 
     // If there is a single person sending, use their picture instead of the app
     // icon.
diff --git a/chrome/browser/notifications/sync_notifier/synced_notification_unittest.cc b/chrome/browser/notifications/sync_notifier/synced_notification_unittest.cc
index 38794aa..49d3668 100644
--- a/chrome/browser/notifications/sync_notifier/synced_notification_unittest.cc
+++ b/chrome/browser/notifications/sync_notifier/synced_notification_unittest.cc
@@ -56,6 +56,14 @@
     profile_ = profile;
   }
 
+  virtual bool Update(const Notification& notification, Profile* profile)
+      OVERRIDE {
+    // Make a deep copy of the notification that we can inspect.
+    notification_ = notification;
+    profile_ = profile;
+    return true;
+  }
+
   // Returns true if any notifications match the supplied ID, either currently
   // displayed or in the queue.
   virtual const Notification* FindById(const std::string& id) const OVERRIDE {
@@ -319,7 +327,7 @@
   // Check the base fields of the notification.
   EXPECT_EQ(message_center::NOTIFICATION_TYPE_IMAGE, notification.type());
   EXPECT_EQ(std::string(kTitle1), UTF16ToUTF8(notification.title()));
-  EXPECT_EQ(std::string(kText1), UTF16ToUTF8(notification.message()));
+  EXPECT_EQ(std::string(kText1And1), UTF16ToUTF8(notification.message()));
   EXPECT_EQ(std::string(kExpectedOriginUrl), notification.origin_url().spec());
   EXPECT_EQ(std::string(kKey1), UTF16ToUTF8(notification.replace_id()));
 
@@ -398,7 +406,7 @@
             notification_manager.notification().type());
   EXPECT_EQ(std::string(kTitle1),
             UTF16ToUTF8(notification_manager.notification().title()));
-  EXPECT_EQ(std::string(kText1),
+  EXPECT_EQ(std::string(kText1And1),
             UTF16ToUTF8(notification_manager.notification().message()));
 
   // TODO(petewil): Check that the bitmap in the notification is what we expect.
diff --git a/chrome/browser/omnibox/omnibox_field_trial.cc b/chrome/browser/omnibox/omnibox_field_trial.cc
index 09c2d1a..83b9143 100644
--- a/chrome/browser/omnibox/omnibox_field_trial.cc
+++ b/chrome/browser/omnibox/omnibox_field_trial.cc
@@ -22,7 +22,10 @@
     "OmniboxHUPCreateShorterMatch";
 const char kStopTimerFieldTrialName[] = "OmniboxStopTimer";
 const char kShortcutsScoringFieldTrialName[] = "OmniboxShortcutsScoring";
-const char kSearchHistoryFieldTrialName[] = "OmniboxSearchHistory";
+const char kBundledExperimentFieldTrialName[] = "OmniboxBundledExperimentV1";
+
+// Rule names used by the bundled experiment.
+const char kSearchHistoryRule[] = "SearchHistory";
 
 // The autocomplete dynamic field trial name prefix.  Each field trial is
 // configured dynamically and is retrieved automatically by Chrome during
@@ -218,12 +221,53 @@
   return true;
 }
 
-bool OmniboxFieldTrial::SearchHistoryPreventInlining() {
-  return (base::FieldTrialList::FindFullName(kSearchHistoryFieldTrialName) ==
-          "PreventInlining");
+bool OmniboxFieldTrial::SearchHistoryPreventInlining(
+    AutocompleteInput::PageClassification current_page_classification) {
+  return OmniboxFieldTrial::GetValueForRuleInContext(
+      kSearchHistoryRule, current_page_classification) == "PreventInlining";
 }
 
-bool OmniboxFieldTrial::SearchHistoryDisable() {
-  return (base::FieldTrialList::FindFullName(kSearchHistoryFieldTrialName) ==
-          "Disable");
+bool OmniboxFieldTrial::SearchHistoryDisable(
+    AutocompleteInput::PageClassification current_page_classification) {
+  return OmniboxFieldTrial::GetValueForRuleInContext(
+      kSearchHistoryRule, current_page_classification) == "Disable";
+}
+
+// Background and implementation details:
+//
+// Each experiment group in any field trial can come with an optional set of
+// parameters (key-value pairs).  In the bundled omnibox experiment
+// (kBundledExperimentFieldTrialName), each experiment group comes with a
+// list of parameters in the form:
+//   key=<Rule>:<AutocompleteInput::PageClassification (as an int)>
+//   value=<arbitrary string>
+// The AutocompleteInput::PageClassification can also be "*", which means
+// this rule applies in all page classification contexts.
+// One example parameter is
+//   key=SearchHistory:6
+//   value=PreventInlining
+// This means in page classification context 6 (a search result page doing
+// search term replacement), the SearchHistory experiment should
+// PreventInlining.
+//
+// In short, this function tries to find the value associated with key
+// |rule|:|page_classification|, failing that it looks up |rule|:*,
+// and failing that it returns the empty string.
+std::string OmniboxFieldTrial::GetValueForRuleInContext(
+    const std::string& rule,
+    AutocompleteInput::PageClassification page_classification) {
+  std::map<std::string, std::string> params;
+  if (!chrome_variations::GetVariationParams(kBundledExperimentFieldTrialName,
+                                             &params)) {
+    return std::string();
+  }
+  // Look up rule in this exact context.
+  std::map<std::string, std::string>::iterator it =
+      params.find(rule + ":" + base::IntToString(
+          static_cast<int>(page_classification)));
+  if (it != params.end())
+    return it->second;
+  // Look up rule in the global context.
+  it = params.find(rule + ":*");
+  return (it != params.end()) ? it->second : std::string();
 }
diff --git a/chrome/browser/omnibox/omnibox_field_trial.h b/chrome/browser/omnibox/omnibox_field_trial.h
index d555a9f..1ad3b16 100644
--- a/chrome/browser/omnibox/omnibox_field_trial.h
+++ b/chrome/browser/omnibox/omnibox_field_trial.h
@@ -9,6 +9,8 @@
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "chrome/browser/autocomplete/autocomplete_input.h"
 
 // This class manages the Omnibox field trials.
 class OmniboxFieldTrial {
@@ -101,18 +103,44 @@
   static bool ShortcutsScoringMaxRelevance(int* max_relevance);
 
   // ---------------------------------------------------------
-  // For the SearchHistory field trial.
+  // For the SearchHistory experiment that's part of the bundled omnibox
+  // field trial.
 
-  // Returns true if the user is in the experiment group that scores
-  // search history query suggestions less aggressively so that they don't
-  // inline.
-  static bool SearchHistoryPreventInlining();
+  // Returns true if the user is in the experiment group that, given the
+  // provided |current_page_classification| context, scores search history
+  // query suggestions less aggressively so that they don't inline.
+  static bool SearchHistoryPreventInlining(
+      AutocompleteInput::PageClassification current_page_classification);
 
-  // Returns true if the user is in the experiment group that disables
-  // all query suggestions from search history.
-  static bool SearchHistoryDisable();
+  // Returns true if the user is in the experiment group that, given the
+  // provided |current_page_classification| context, disables all query
+  // suggestions from search history.
+  static bool SearchHistoryDisable(
+      AutocompleteInput::PageClassification current_page_classification);
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(OmniboxFieldTrialTest, GetValueForRuleInContext);
+
+  // The bundled omnibox experiment comes with a set of parameters
+  // (key-value pairs).  Each key indicates a certain rule that applies in
+  // a certain context.  The value indicates what the consequences of
+  // applying the rule are.  For example, the value of a SearchHistory rule
+  // in the context of a search results page might indicate that we should
+  // prevent search history matches from inlining.
+  //
+  // This function returns the value associated with the |rule| that applies
+  // in the current context (which currently only consists of
+  // |page_classification| but will soon contain other features, some not
+  // passed in as parameters, such as whether Instant Extended is enabled).
+  // If no such rule exists in the current context, looks for that rule in
+  // the global context and return its value if found.  If the rule remains
+  // unfound in the global context, returns the empty string.  For more
+  // details, see the implementation.  How to interpret the value is left
+  // to the caller; this is rule-dependent.
+  static std::string GetValueForRuleInContext(
+      const std::string& rule,
+      AutocompleteInput::PageClassification page_classification);
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(OmniboxFieldTrial);
 };
 
diff --git a/chrome/browser/omnibox/omnibox_field_trial_unittest.cc b/chrome/browser/omnibox/omnibox_field_trial_unittest.cc
index b40be13..9ccb00f 100644
--- a/chrome/browser/omnibox/omnibox_field_trial_unittest.cc
+++ b/chrome/browser/omnibox/omnibox_field_trial_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/strings/string16.h"
 #include "chrome/common/metrics/entropy_provider.h"
+#include "chrome/common/metrics/variations/variations_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class OmniboxFieldTrialTest : public testing::Test {
@@ -129,3 +130,74 @@
     EXPECT_TRUE(OmniboxFieldTrial::InZeroSuggestFieldTrial());
   }
 }
+
+TEST_F(OmniboxFieldTrialTest, GetValueForRuleInContext) {
+  // Must be the same as kBundledExperimentFieldTrialName
+  // defined in omnibox_field_trial.cc.
+  const std::string kTrialName = "OmniboxBundledExperimentV1";
+  {
+    std::map<std::string, std::string> params;
+    // Rule 1 has some exact matches and a global fallback.
+    params["rule1:1"] = "rule1-1-value";  // NEW_TAB_PAGE
+    params["rule1:3"] = "rule1-3-value";  // HOMEPAGE
+    params["rule1:*"] = "rule1-*-value";  // global
+    // Rule 2 has no exact matches but has a global fallback.
+    params["rule2:*"] = "rule2-*-value";  // global
+    // Rule 3 has an exact match but no global fallback.
+    params["rule3:4"] = "rule3-4-value";  // OTHER
+    // Add a malformed rule to make sure it doesn't screw things up.
+    params["unrecognized"] = "unrecognized-value";
+    ASSERT_TRUE(chrome_variations::AssociateVariationParams(
+        kTrialName, "A", params));
+  }
+
+  base::FieldTrialList::CreateFieldTrial(kTrialName, "A");
+
+  // Tests for rule 1.
+  EXPECT_EQ(
+      "rule1-1-value",
+      OmniboxFieldTrial::GetValueForRuleInContext(
+          "rule1", AutocompleteInput::NEW_TAB_PAGE));  // exact match
+  EXPECT_EQ(
+      "rule1-*-value",
+      OmniboxFieldTrial::GetValueForRuleInContext(
+          "rule1", AutocompleteInput::BLANK));  // fallback to global
+  EXPECT_EQ(
+      "rule1-3-value",
+      OmniboxFieldTrial::GetValueForRuleInContext(
+          "rule1", AutocompleteInput::HOMEPAGE));  // exact match
+  EXPECT_EQ(
+      "rule1-*-value",
+      OmniboxFieldTrial::GetValueForRuleInContext(
+          "rule1", AutocompleteInput::OTHER));  // fallback to global
+
+  // Tests for rule 2.
+  EXPECT_EQ(
+      "rule2-*-value",
+      OmniboxFieldTrial::GetValueForRuleInContext(
+          "rule2", AutocompleteInput::HOMEPAGE));  // fallback to global
+  EXPECT_EQ(
+      "rule2-*-value",
+      OmniboxFieldTrial::GetValueForRuleInContext(
+          "rule2", AutocompleteInput::OTHER));  // fallback to global
+
+  // Tests for rule 3.
+  EXPECT_EQ(
+      "",
+      OmniboxFieldTrial::GetValueForRuleInContext(
+          "rule3", AutocompleteInput::BLANK));  // no global fallback
+  EXPECT_EQ(
+      "",
+      OmniboxFieldTrial::GetValueForRuleInContext(
+          "rule3", AutocompleteInput::HOMEPAGE));  // no global fallback
+  EXPECT_EQ(
+      "rule3-4-value",
+      OmniboxFieldTrial::GetValueForRuleInContext(
+          "rule3", AutocompleteInput::OTHER));  // exact match
+
+  // Tests for rule 4 (a missing rule).
+  EXPECT_EQ(
+      "",
+      OmniboxFieldTrial::GetValueForRuleInContext(
+          "rule4", AutocompleteInput::OTHER));  // no rule at all
+}
diff --git a/chrome/browser/password_manager/login_database.cc b/chrome/browser/password_manager/login_database.cc
index 7204385..959e881 100644
--- a/chrome/browser/password_manager/login_database.cc
+++ b/chrome/browser/password_manager/login_database.cc
@@ -47,6 +47,20 @@
   COLUMN_TIMES_USED
 };
 
+// Using the public suffix list for matching the origin is only needed for
+// websites that do not have a single hostname for entering credentials. It
+// would be better for their users if they did, but until then we help them find
+// credentials across different hostnames. We know that accounts.google.com is
+// the only hostname we should be accepting credentials on for any domain under
+// google.com, so we can apply a tighter policy for that domain.
+// For owners of domains where a single hostname is always used when your
+// users are entering their credentials, please contact palmer@chromium.org,
+// nyquist@chromium.org or file a bug at http://crbug.com/ to be added here.
+bool ShouldPSLDomainMatchingApply(
+      const std::string& registry_controlled_domain) {
+  return registry_controlled_domain != "google.com";
+}
+
 std::string GetRegistryControlledDomain(const GURL& signon_realm) {
   return net::registry_controlled_domains::GetDomainAndRegistry(
       signon_realm,
@@ -422,7 +436,10 @@
       "scheme, password_type, possible_usernames, times_used "
       "FROM logins WHERE signon_realm == ? ";
   sql::Statement s;
-  if (public_suffix_domain_matching_) {
+  const GURL signon_realm(form.signon_realm);
+  std::string registered_domain = GetRegistryControlledDomain(signon_realm);
+  if (public_suffix_domain_matching_ &&
+      ShouldPSLDomainMatchingApply(registered_domain)) {
     // We are extending the original SQL query with one that includes more
     // possible matches based on public suffix domain matching. Using a regexp
     // here is just an optimization to not have to parse all the stored entries
@@ -434,11 +451,9 @@
     // TODO(nyquist) Re-enable usage of GetCachedStatement when
     // http://crbug.com/248608 is fixed.
     s.Assign(db_.GetUniqueStatement(extended_sql_query.c_str()));
-    const GURL signon_realm(form.signon_realm);
-    std::string domain = GetRegistryControlledDomain(signon_realm);
     // We need to escape . in the domain. Since the domain has already been
     // sanitized using GURL, we do not need to escape any other characters.
-    ReplaceChars(domain, ".", "\\.", &domain);
+    ReplaceChars(registered_domain, ".", "\\.", &registered_domain);
     std::string scheme = signon_realm.scheme();
     // We need to escape . in the scheme. Since the scheme has already been
     // sanitized using GURL, we do not need to escape any other characters.
@@ -450,7 +465,7 @@
     // http://www.mobile.foo.bar/. It will not match http://notfoo.bar/.
     // The scheme and port has to be the same as the observed form.
     std::string regexp = "^(" + scheme + ":\\/\\/)([\\w-]+\\.)*" +
-                         domain + "(:" + port + ")?\\/$";
+                         registered_domain + "(:" + port + ")?\\/$";
     s.BindString(0, form.signon_realm);
     s.BindString(1, regexp);
   } else {
diff --git a/chrome/browser/password_manager/login_database_unittest.cc b/chrome/browser/password_manager/login_database_unittest.cc
index 4a44fc0..353968f 100644
--- a/chrome/browser/password_manager/login_database_unittest.cc
+++ b/chrome/browser/password_manager/login_database_unittest.cc
@@ -232,6 +232,53 @@
   result.clear();
 }
 
+TEST_F(LoginDatabaseTest, TestPublicSuffixDomainMatchingShouldMatchingApply) {
+  SetPublicSuffixMatching(true);
+  std::vector<PasswordForm*> result;
+
+  // Verify the database is empty.
+  EXPECT_TRUE(db_.GetAutofillableLogins(&result));
+  EXPECT_EQ(0U, result.size());
+
+  // Example password form.
+  PasswordForm form;
+  form.origin = GURL("https://accounts.google.com/");
+  form.action = GURL("https://accounts.google.com/login");
+  form.username_element = ASCIIToUTF16("username");
+  form.username_value = ASCIIToUTF16("test@gmail.com");
+  form.password_element = ASCIIToUTF16("password");
+  form.password_value = ASCIIToUTF16("test");
+  form.submit_element = ASCIIToUTF16("");
+  form.signon_realm = "https://accounts.google.com/";
+  form.ssl_valid = true;
+  form.preferred = false;
+  form.scheme = PasswordForm::SCHEME_HTML;
+
+  // Add it and make sure it is there.
+  EXPECT_TRUE(db_.AddLogin(form));
+  EXPECT_TRUE(db_.GetAutofillableLogins(&result));
+  EXPECT_EQ(1U, result.size());
+  delete result[0];
+  result.clear();
+
+  // Match against an exact copy.
+  EXPECT_TRUE(db_.GetLogins(form, &result));
+  EXPECT_EQ(1U, result.size());
+  delete result[0];
+  result.clear();
+
+  // We go to a different site on the same domain where feature is not needed.
+  PasswordForm form2(form);
+  form2.origin = GURL("https://some.other.google.com/");
+  form2.action = GURL("https://some.other.google.com/login");
+  form2.signon_realm = "https://some.other.google.com/";
+
+  // Match against the other site. Should not match since feature should not be
+  // enabled for this domain.
+  EXPECT_TRUE(db_.GetLogins(form2, &result));
+  EXPECT_EQ(0U, result.size());
+}
+
 // This test fails if the implementation of GetLogins uses GetCachedStatement
 // instead of GetUniqueStatement, since REGEXP is in use. See
 // http://crbug.com/248608.
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 6d8e145..b518d83 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <string>
 
+#include "base/command_line.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
 #include "chrome/browser/infobars/infobar_service.h"
@@ -12,6 +13,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
@@ -24,10 +26,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/keycodes/keyboard_codes.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 namespace {
 
 class NavigationObserver : public content::NotificationObserver,
@@ -45,14 +43,6 @@
 
   virtual ~NavigationObserver() {}
 
-  void Wait() {
-    message_loop_runner_->Run();
-  }
-
-  bool InfoBarWasShown() {
-    return info_bar_shown_;
-  }
-
   // content::NotificationObserver:
   virtual void Observe(int type,
                        const content::NotificationSource& source,
@@ -65,7 +55,7 @@
     info_bar_shown_ = true;
   }
 
-  // content::WebContentsObserver
+  // content::WebContentsObserver:
   virtual void DidFinishLoad(
       int64 frame_id,
       const GURL& validated_url,
@@ -74,6 +64,12 @@
     message_loop_runner_->Quit();
   }
 
+  bool infobar_shown() { return info_bar_shown_; }
+
+  void Wait() {
+    message_loop_runner_->Run();
+  }
+
  private:
   scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
   bool info_bar_shown_;
@@ -108,7 +104,7 @@
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForXHRSubmit) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -128,7 +124,7 @@
       "document.getElementById('submit_button').click()";
   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
   observer.Wait();
-  EXPECT_TRUE(observer.InfoBarWasShown());
+  EXPECT_TRUE(observer.infobar_shown());
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForOtherXHR) {
@@ -149,5 +145,5 @@
       "send_xhr()";
   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate));
   observer.Wait();
-  EXPECT_FALSE(observer.InfoBarWasShown());
+  EXPECT_FALSE(observer.infobar_shown());
 }
diff --git a/chrome/browser/password_manager/password_store.h b/chrome/browser/password_manager/password_store.h
index 4d6079f..9ae6bf3 100644
--- a/chrome/browser/password_manager/password_store.h
+++ b/chrome/browser/password_manager/password_store.h
@@ -21,6 +21,7 @@
 class Task;
 
 namespace browser_sync {
+class PasswordChangeProcessor;
 class PasswordDataTypeController;
 class PasswordModelAssociator;
 class PasswordModelWorker;
@@ -132,6 +133,7 @@
 
  protected:
   friend class base::RefCountedThreadSafe<PasswordStore>;
+  friend class browser_sync::PasswordChangeProcessor;
   friend class browser_sync::PasswordDataTypeController;
   friend class browser_sync::PasswordModelAssociator;
   friend class browser_sync::PasswordModelWorker;
diff --git a/chrome/browser/platform_util_chromeos.cc b/chrome/browser/platform_util_chromeos.cc
index ae27115..49df102 100644
--- a/chrome/browser/platform_util_chromeos.cc
+++ b/chrome/browser/platform_util_chromeos.cc
@@ -39,12 +39,12 @@
 
 void ShowItemInFolder(const base::FilePath& full_path) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  file_manager_util::ShowFileInFolder(full_path);
+  file_manager::util::ShowFileInFolder(full_path);
 }
 
 void OpenItem(const base::FilePath& full_path) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  file_manager_util::ViewItem(full_path);
+  file_manager::util::ViewItem(full_path);
 }
 
 void OpenExternal(const GURL& url) {
diff --git a/chrome/browser/policy/cloud/cloud_policy_client.cc b/chrome/browser/policy/cloud/cloud_policy_client.cc
index a9b30c3..8afbc94 100644
--- a/chrome/browser/policy/cloud/cloud_policy_client.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_client.cc
@@ -58,6 +58,8 @@
       submit_machine_id_(false),
       public_key_version_(-1),
       public_key_version_valid_(false),
+      invalidation_version_(0),
+      fetched_invalidation_version_(0),
       service_(service),                  // Can be NULL for unit tests.
       status_provider_(status_provider),  // Can be NULL for unit tests.
       status_(DM_STATUS_SUCCESS) {
@@ -125,6 +127,13 @@
                                  base::Unretained(this)));
 }
 
+void CloudPolicyClient::SetInvalidationInfo(
+    int64 version,
+    const std::string& payload) {
+  invalidation_version_ = version;
+  invalidation_payload_ = payload;
+}
+
 void CloudPolicyClient::FetchPolicy() {
   CHECK(is_registered());
   CHECK(!namespaces_to_fetch_.empty());
@@ -165,6 +174,10 @@
             last_policy_timestamp_ - base::Time::UnixEpoch());
         fetch_request->set_timestamp(timestamp.InMilliseconds());
       }
+      if (!invalidation_payload_.empty()) {
+        fetch_request->set_invalidation_version(invalidation_version_);
+        fetch_request->set_invalidation_payload(invalidation_payload_);
+      }
     }
   }
 
@@ -180,6 +193,10 @@
     }
   }
 
+  // Set the fetched invalidation version to the latest invalidation version
+  // since it is now the invalidation version used for the latest fetch.
+  fetched_invalidation_version_ = invalidation_version_;
+
   // Fire the job.
   request_job_->Start(base::Bind(&CloudPolicyClient::OnPolicyFetchCompleted,
                                  base::Unretained(this)));
diff --git a/chrome/browser/policy/cloud/cloud_policy_client.h b/chrome/browser/policy/cloud/cloud_policy_client.h
index f30fcc1..164db49 100644
--- a/chrome/browser/policy/cloud/cloud_policy_client.h
+++ b/chrome/browser/policy/cloud/cloud_policy_client.h
@@ -104,6 +104,11 @@
       bool is_auto_enrollment,
       const std::string& requisition);
 
+  // Sets information about a policy invalidation. Subsequent fetch operations
+  // will use the given info, and callers can use fetched_invalidation_version
+  // to determine which version of policy was fetched.
+  void SetInvalidationInfo(int64 version, const std::string& payload);
+
   // Requests a policy fetch. The client being registered is a prerequisite to
   // this operation and this call will CHECK if the client is not in registered
   // state. FetchPolicy() triggers a policy fetch from the cloud. A policy
@@ -186,6 +191,13 @@
     return robot_api_auth_code_;
   }
 
+  // Returns the invalidation version that was used for the last FetchPolicy.
+  // Observers can call this method from their OnPolicyFetched method to
+  // determine which at which invalidation version the policy was fetched.
+  int64 fetched_invalidation_version() const {
+    return fetched_invalidation_version_;
+  }
+
  protected:
   // A set of PolicyNamespaceKeys to fetch.
   typedef std::set<PolicyNamespaceKey> NamespaceSet;
@@ -245,6 +257,13 @@
   bool public_key_version_valid_;
   std::string robot_api_auth_code_;
 
+  // Information for the latest policy invalidation received.
+  int64 invalidation_version_;
+  std::string invalidation_payload_;
+
+  // The invalidation version used for the most recent fetch operation.
+  int64 fetched_invalidation_version_;
+
   // Used for issuing requests to the cloud.
   DeviceManagementService* service_;
   scoped_ptr<DeviceManagementRequestJob> request_job_;
diff --git a/chrome/browser/policy/cloud/cloud_policy_client_unittest.cc b/chrome/browser/policy/cloud/cloud_policy_client_unittest.cc
index 527a8c6..bafcc3d 100644
--- a/chrome/browser/policy/cloud/cloud_policy_client_unittest.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_client_unittest.cc
@@ -200,6 +200,7 @@
   EXPECT_CALL(service_, CreateJob(_)).Times(0);
   EXPECT_FALSE(client_->is_registered());
   EXPECT_FALSE(client_->GetPolicyFor(policy_ns_key_));
+  EXPECT_EQ(0, client_->fetched_invalidation_version());
 }
 
 TEST_F(CloudPolicyClientTest, SetupRegistrationAndPolicyFetch) {
@@ -345,6 +346,40 @@
   CheckPolicyResponse();
 }
 
+TEST_F(CloudPolicyClientTest, PolicyFetchWithInvalidation) {
+  Register();
+
+  int64 previous_version = client_->fetched_invalidation_version();
+  client_->SetInvalidationInfo(12345, "12345");
+  EXPECT_EQ(previous_version, client_->fetched_invalidation_version());
+  em::PolicyFetchRequest* policy_fetch_request =
+      policy_request_.mutable_policy_request()->mutable_request(0);
+  policy_fetch_request->set_invalidation_version(12345);
+  policy_fetch_request->set_invalidation_payload("12345");
+
+  ExpectPolicyFetch(kDMToken, dm_protocol::kValueUserAffiliationNone);
+  EXPECT_CALL(observer_, OnPolicyFetched(_));
+  EXPECT_CALL(status_provider_, OnSubmittedSuccessfully());
+  client_->FetchPolicy();
+  CheckPolicyResponse();
+  EXPECT_EQ(12345, client_->fetched_invalidation_version());
+}
+
+TEST_F(CloudPolicyClientTest, PolicyFetchWithInvalidationNoPayload) {
+  Register();
+
+  int64 previous_version = client_->fetched_invalidation_version();
+  client_->SetInvalidationInfo(-12345, std::string());
+  EXPECT_EQ(previous_version, client_->fetched_invalidation_version());
+
+  ExpectPolicyFetch(kDMToken, dm_protocol::kValueUserAffiliationNone);
+  EXPECT_CALL(observer_, OnPolicyFetched(_));
+  EXPECT_CALL(status_provider_, OnSubmittedSuccessfully());
+  client_->FetchPolicy();
+  CheckPolicyResponse();
+  EXPECT_EQ(-12345, client_->fetched_invalidation_version());
+}
+
 TEST_F(CloudPolicyClientTest, BadPolicyResponse) {
   Register();
 
diff --git a/chrome/browser/policy/cloud/cloud_policy_invalidator.cc b/chrome/browser/policy/cloud/cloud_policy_invalidator.cc
new file mode 100644
index 0000000..5012fe7
--- /dev/null
+++ b/chrome/browser/policy/cloud/cloud_policy_invalidator.cc
@@ -0,0 +1,336 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/policy/cloud/cloud_policy_invalidator.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/metrics/histogram.h"
+#include "base/rand_util.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/invalidation/invalidation_service.h"
+#include "chrome/browser/invalidation/invalidation_service_factory.h"
+#include "chrome/browser/policy/cloud/enterprise_metrics.h"
+#include "chrome/common/chrome_switches.h"
+#include "policy/policy_constants.h"
+#include "sync/notifier/object_id_invalidation_map.h"
+
+namespace policy {
+
+const int CloudPolicyInvalidator::kMissingPayloadDelay = 5;
+const int CloudPolicyInvalidator::kMaxFetchDelayDefault = 5000;
+const int CloudPolicyInvalidator::kMaxFetchDelayMin = 1000;
+const int CloudPolicyInvalidator::kMaxFetchDelayMax = 300000;
+
+CloudPolicyInvalidator::CloudPolicyInvalidator(
+    CloudPolicyInvalidationHandler* invalidation_handler,
+    CloudPolicyStore* store,
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+    : invalidation_handler_(invalidation_handler),
+      store_(store),
+      task_runner_(task_runner),
+      profile_(NULL),
+      invalidation_service_(NULL),
+      invalidations_enabled_(false),
+      invalidation_service_enabled_(false),
+      registered_timestamp_(0),
+      invalid_(false),
+      invalidation_version_(0),
+      unknown_version_invalidation_count_(0),
+      ack_handle_(syncer::AckHandle::InvalidAckHandle()),
+      weak_factory_(this),
+      max_fetch_delay_(kMaxFetchDelayDefault) {
+  DCHECK(invalidation_handler);
+  DCHECK(store);
+  DCHECK(task_runner.get());
+  DCHECK(!IsInitialized());
+}
+
+CloudPolicyInvalidator::~CloudPolicyInvalidator() {}
+
+void CloudPolicyInvalidator::InitializeWithProfile(Profile* profile) {
+  DCHECK(!IsInitialized());
+  DCHECK(profile);
+  profile_ = profile;
+  Initialize();
+}
+
+void CloudPolicyInvalidator::InitializeWithService(
+    invalidation::InvalidationService* invalidation_service) {
+  DCHECK(!IsInitialized());
+  DCHECK(invalidation_service);
+  invalidation_service_ = invalidation_service;
+  Initialize();
+}
+
+void CloudPolicyInvalidator::Shutdown() {
+  if (IsInitialized()) {
+    if (registered_timestamp_)
+      invalidation_service_->UnregisterInvalidationHandler(this);
+    store_->RemoveObserver(this);
+  }
+}
+
+void CloudPolicyInvalidator::OnInvalidatorStateChange(
+    syncer::InvalidatorState state) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  invalidation_service_enabled_ = state == syncer::INVALIDATIONS_ENABLED;
+  UpdateInvalidationsEnabled();
+}
+
+void CloudPolicyInvalidator::OnIncomingInvalidation(
+    const syncer::ObjectIdInvalidationMap& invalidation_map) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  const syncer::ObjectIdInvalidationMap::const_iterator invalidation =
+      invalidation_map.find(object_id_);
+  if (invalidation == invalidation_map.end()) {
+    NOTREACHED();
+    return;
+  }
+  HandleInvalidation(invalidation->second);
+}
+
+void CloudPolicyInvalidator::OnStoreLoaded(CloudPolicyStore* store) {
+  DCHECK(IsInitialized());
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (registered_timestamp_) {
+    // Update the kMetricPolicyRefresh histogram. In some cases, this object can
+    // be constructed during an OnStoreLoaded callback, which causes
+    // OnStoreLoaded to be called twice at initialization time, so make sure
+    // that the timestamp does not match the timestamp at which registration
+    // occurred. We only measure changes which occur after registration.
+    if (!store->policy() || !store->policy()->has_timestamp() ||
+        store->policy()->timestamp() != registered_timestamp_) {
+      UMA_HISTOGRAM_ENUMERATION(
+          kMetricPolicyRefresh,
+          GetPolicyRefreshMetric(),
+          METRIC_POLICY_REFRESH_SIZE);
+    }
+
+    // If the policy was invalid and the version stored matches the latest
+    // invalidation version, acknowledge the latest invalidation.
+    if (invalid_ && store->invalidation_version() == invalidation_version_)
+      AcknowledgeInvalidation();
+  }
+
+  UpdateRegistration(store->policy());
+  UpdateMaxFetchDelay(store->policy_map());
+}
+
+void CloudPolicyInvalidator::OnStoreError(CloudPolicyStore* store) {}
+
+base::WeakPtr<CloudPolicyInvalidator> CloudPolicyInvalidator::GetWeakPtr() {
+  DCHECK(!IsInitialized());
+  return weak_factory_.GetWeakPtr();
+}
+
+void CloudPolicyInvalidator::Initialize() {
+  OnStoreLoaded(store_);
+  store_->AddObserver(this);
+}
+
+bool CloudPolicyInvalidator::IsInitialized() {
+  // Could have been initialized with a profile or invalidation service.
+  return profile_ || invalidation_service_;
+}
+
+void CloudPolicyInvalidator::HandleInvalidation(
+    const syncer::Invalidation& invalidation) {
+  // The invalidation service may send an invalidation more than once if there
+  // is a delay in acknowledging it. Duplicate invalidations are ignored.
+  if (invalid_ && ack_handle_.Equals(invalidation.ack_handle))
+    return;
+
+  // If there is still a pending invalidation, acknowledge it, since we only
+  // care about the latest invalidation.
+  if (invalid_)
+    AcknowledgeInvalidation();
+
+  // Update invalidation state.
+  invalid_ = true;
+  ack_handle_ = invalidation.ack_handle;
+  invalidation_version_ = invalidation.version;
+
+  // When an invalidation with unknown version is received, use negative
+  // numbers based on the number of such invalidations received. This
+  // ensures that the version numbers do not collide with "real" versions
+  // (which are positive) or previous invalidations with unknown version.
+  if (invalidation_version_ == syncer::Invalidation::kUnknownVersion)
+    invalidation_version_ = -(++unknown_version_invalidation_count_);
+
+  // In order to prevent the cloud policy server from becoming overwhelmed when
+  // a policy with many users is modified, delay for a random period of time
+  // before fetching the policy. Delay for at least 20ms so that if multiple
+  // invalidations are received in quick succession, only one fetch will be
+  // performed.
+  base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
+      base::RandInt(20, max_fetch_delay_));
+
+  // If there is a payload, the invalidate callback can run at any time, so set
+  // the version and payload on the client immediately. Otherwise, the callback
+  // must only run after at least kMissingPayloadDelay minutes.
+  const std::string& payload = invalidation.payload;
+  if (!invalidation.payload.empty())
+    invalidation_handler_->SetInvalidationInfo(invalidation_version_, payload);
+  else
+    delay += base::TimeDelta::FromMinutes(kMissingPayloadDelay);
+
+  // Schedule the invalidate callback to run.
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(
+          &CloudPolicyInvalidator::RunInvalidateCallback,
+          weak_factory_.GetWeakPtr(),
+          payload.empty() /* is_missing_payload */),
+      delay);
+
+  // Update the kMetricPolicyInvalidations histogram.
+  UMA_HISTOGRAM_BOOLEAN(kMetricPolicyInvalidations, !payload.empty());
+}
+
+void CloudPolicyInvalidator::UpdateRegistration(
+    const enterprise_management::PolicyData* policy) {
+  // Create the ObjectId based on the policy data.
+  // If the policy does not specify an the ObjectId, then unregister.
+  if (!policy ||
+      !policy->has_timestamp() ||
+      !policy->has_invalidation_source() ||
+      !policy->has_invalidation_name()) {
+    Unregister();
+    return;
+  }
+  invalidation::ObjectId object_id(
+      policy->invalidation_source(),
+      policy->invalidation_name());
+
+  // If the policy object id in the policy data is different from the currently
+  // registered object id, update the object registration.
+  if (!registered_timestamp_ || !(object_id == object_id_))
+    Register(policy->timestamp(), object_id);
+}
+
+void CloudPolicyInvalidator::Register(
+    int64 timestamp,
+    const invalidation::ObjectId& object_id) {
+  // Get the invalidation service from the profile if needed.
+  if (!invalidation_service_) {
+    DCHECK(profile_);
+    invalidation_service_ =
+        invalidation::InvalidationServiceFactory::GetForProfile(profile_);
+    if (!invalidation_service_)
+      return;
+  }
+
+  // Register this handler with the invalidation service if needed.
+  if (!registered_timestamp_) {
+    OnInvalidatorStateChange(invalidation_service_->GetInvalidatorState());
+    invalidation_service_->RegisterInvalidationHandler(this);
+  }
+
+  // Update internal state.
+  if (invalid_)
+    AcknowledgeInvalidation();
+  registered_timestamp_ = timestamp;
+  object_id_ = object_id;
+  UpdateInvalidationsEnabled();
+
+  // Update registration with the invalidation service.
+  syncer::ObjectIdSet ids;
+  ids.insert(object_id);
+  invalidation_service_->UpdateRegisteredInvalidationIds(this, ids);
+}
+
+void CloudPolicyInvalidator::Unregister() {
+  if (registered_timestamp_) {
+    if (invalid_)
+      AcknowledgeInvalidation();
+    invalidation_service_->UpdateRegisteredInvalidationIds(
+        this,
+        syncer::ObjectIdSet());
+    invalidation_service_->UnregisterInvalidationHandler(this);
+    registered_timestamp_ = 0;
+    UpdateInvalidationsEnabled();
+  }
+}
+
+void CloudPolicyInvalidator::UpdateMaxFetchDelay(const PolicyMap& policy_map) {
+  int delay;
+
+  // Try reading the delay from the policy.
+  const base::Value* delay_policy_value =
+      policy_map.GetValue(key::kMaxInvalidationFetchDelay);
+  if (delay_policy_value && delay_policy_value->GetAsInteger(&delay)) {
+    set_max_fetch_delay(delay);
+    return;
+  }
+
+  // Try reading the delay from the command line switch.
+  std::string delay_string =
+      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kCloudPolicyInvalidationDelay);
+  if (base::StringToInt(delay_string, &delay)) {
+    set_max_fetch_delay(delay);
+    return;
+  }
+
+  set_max_fetch_delay(kMaxFetchDelayDefault);
+}
+
+void CloudPolicyInvalidator::set_max_fetch_delay(int delay) {
+  if (delay < kMaxFetchDelayMin)
+    max_fetch_delay_ = kMaxFetchDelayMin;
+  else if (delay > kMaxFetchDelayMax)
+    max_fetch_delay_ = kMaxFetchDelayMax;
+  else
+    max_fetch_delay_ = delay;
+}
+
+void CloudPolicyInvalidator::UpdateInvalidationsEnabled() {
+  bool invalidations_enabled =
+      invalidation_service_enabled_ && registered_timestamp_;
+  if (invalidations_enabled_ != invalidations_enabled) {
+    invalidations_enabled_ = invalidations_enabled;
+    invalidation_handler_->OnInvalidatorStateChanged(invalidations_enabled);
+  }
+}
+
+void CloudPolicyInvalidator::RunInvalidateCallback(bool is_missing_payload) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // In the missing payload case, the invalidation version has not been set on
+  // the client yet, so set it now that the required time has elapsed.
+  if (is_missing_payload) {
+    invalidation_handler_->SetInvalidationInfo(
+        invalidation_version_,
+        std::string());
+  }
+  invalidation_handler_->InvalidatePolicy();
+}
+
+void CloudPolicyInvalidator::AcknowledgeInvalidation() {
+  DCHECK(invalid_);
+  invalid_ = false;
+  invalidation_handler_->SetInvalidationInfo(0, std::string());
+  invalidation_service_->AcknowledgeInvalidation(object_id_, ack_handle_);
+  // Cancel any scheduled invalidate callbacks.
+  weak_factory_.InvalidateWeakPtrs();
+}
+
+int CloudPolicyInvalidator::GetPolicyRefreshMetric() {
+  if (store_->policy_changed()) {
+    if (invalid_)
+      return METRIC_POLICY_REFRESH_INVALIDATED_CHANGED;
+    if (invalidations_enabled_)
+      return METRIC_POLICY_REFRESH_CHANGED;
+    return METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS;
+  }
+  if (invalid_)
+    return METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED;
+  return METRIC_POLICY_REFRESH_UNCHANGED;
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/cloud/cloud_policy_invalidator.h b/chrome/browser/policy/cloud/cloud_policy_invalidator.h
new file mode 100644
index 0000000..2575d1b
--- /dev/null
+++ b/chrome/browser/policy/cloud/cloud_policy_invalidator.h
@@ -0,0 +1,221 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_POLICY_CLOUD_CLOUD_POLICY_INVALIDATOR_H_
+#define CHROME_BROWSER_POLICY_CLOUD_CLOUD_POLICY_INVALIDATOR_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "chrome/browser/policy/cloud/cloud_policy_store.h"
+#include "sync/notifier/invalidation_handler.h"
+
+class Profile;
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace invalidation {
+class InvalidationService;
+}
+
+namespace policy {
+
+class CloudPolicyInvalidationHandler;
+
+// Listens for and provides policy invalidations.
+class CloudPolicyInvalidator : public syncer::InvalidationHandler,
+                               public CloudPolicyStore::Observer {
+ public:
+  // The number of minutes to delay a policy refresh after receiving an
+  // invalidation with no payload.
+  static const int kMissingPayloadDelay;
+
+  // The default, min and max values for max_fetch_delay_.
+  static const int kMaxFetchDelayDefault;
+  static const int kMaxFetchDelayMin;
+  static const int kMaxFetchDelayMax;
+
+  // |invalidation_handler| handles invalidations provided by this object and
+  // must remain valid until Shutdown is called.
+  // |store| is cloud policy store. It must remain valid until Shutdown is
+  // called.
+  // |task_runner| is used for scheduling delayed tasks. It must post tasks to
+  // the main policy thread.
+  CloudPolicyInvalidator(
+      CloudPolicyInvalidationHandler* invalidation_handler,
+      CloudPolicyStore* store,
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+  virtual ~CloudPolicyInvalidator();
+
+  // Initializes the invalidator with the given profile. The invalidator uses
+  // the profile to get a reference to the profile's invalidation service if
+  // needed. Both the profile and the invalidation service must remain valid
+  // until Shutdown is called. An Initialize method must only be called once.
+  void InitializeWithProfile(Profile* profile);
+
+  // Initializes the invalidator with the invalidation service. It must remain
+  // valid until Shutdown is called. An Initialize method must only be called
+  // once.
+  void InitializeWithService(
+      invalidation::InvalidationService* invalidation_service);
+
+  // Shuts down and disables invalidations. It must be called before the object
+  // is destroyed.
+  void Shutdown();
+
+  // Whether the invalidator currently has the ability to receive invalidations.
+  bool invalidations_enabled() {
+    return invalidations_enabled_;
+  }
+
+  // syncer::InvalidationHandler:
+  virtual void OnInvalidatorStateChange(
+      syncer::InvalidatorState state) OVERRIDE;
+  virtual void OnIncomingInvalidation(
+      const syncer::ObjectIdInvalidationMap& invalidation_map) OVERRIDE;
+
+  // CloudPolicyStore::Observer:
+  virtual void OnStoreLoaded(CloudPolicyStore* store) OVERRIDE;
+  virtual void OnStoreError(CloudPolicyStore* store) OVERRIDE;
+
+ protected:
+  // Allows subclasses to create a weak pointer to the object. The pointer
+  // should only be used to call one of the Initialize methods, as after the
+  // object is initialized weak pointers may be invalidated at any time.
+  base::WeakPtr<CloudPolicyInvalidator> GetWeakPtr();
+
+ private:
+  // Initialize the invalidator.
+  void Initialize();
+
+  // Returns whether an Initialize method has been called.
+  bool IsInitialized();
+
+  // Handle an invalidation to the policy.
+  void HandleInvalidation(const syncer::Invalidation& invalidation);
+
+  // Update object registration with the invalidation service based on the
+  // given policy data.
+  void UpdateRegistration(const enterprise_management::PolicyData* policy);
+
+  // Registers the given object with the invalidation service.
+  void Register(int64 timestamp, const invalidation::ObjectId& object_id);
+
+  // Unregisters the current object with the invalidation service.
+  void Unregister();
+
+  // Update |max_fetch_delay_| based on the given policy map.
+  void UpdateMaxFetchDelay(const PolicyMap& policy_map);
+  void set_max_fetch_delay(int delay);
+
+  // Updates invalidations_enabled_ and calls the invalidation handler if the
+  // value changed.
+  void UpdateInvalidationsEnabled();
+
+  // Run the invalidate callback on the invalidation handler. is_missing_payload
+  // is set to true if the callback is being invoked in response to an
+  // invalidation with a missing payload.
+  void RunInvalidateCallback(bool is_missing_payload);
+
+  // Acknowledge the latest invalidation.
+  void AcknowledgeInvalidation();
+
+  // Get the kMetricPolicyRefresh histogram metric which should be incremented
+  // when a policy is stored.
+  int GetPolicyRefreshMetric();
+
+  // The handler for invalidations provded by this object.
+  CloudPolicyInvalidationHandler* invalidation_handler_;
+
+  // The cloud policy store.
+  CloudPolicyStore* store_;
+
+  // Schedules delayed tasks.
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  // The profile which will be used to get a reference to an invalidation
+  // service.
+  Profile* profile_;
+
+  // The invalidation service.
+  invalidation::InvalidationService* invalidation_service_;
+
+  // Whether the invalidator currently has the ability to receive invalidations.
+  // This is true if the invalidation service is enabled and the invalidator
+  // has registered for a policy object.
+  bool invalidations_enabled_;
+
+  // Whether the invalidation service is currently enabled.
+  bool invalidation_service_enabled_;
+
+  // The timestamp of the PolicyData at which this object registered for policy
+  // invalidations. Set to zero if the object has not registered yet.
+  int64 registered_timestamp_;
+
+  // The object id representing the policy in the invalidation service.
+  invalidation::ObjectId object_id_;
+
+  // Whether the policy is current invalid. This is set to true when an
+  // invalidation is received and reset when the policy fetched due to the
+  // invalidation is stored.
+  bool invalid_;
+
+  // The version of the latest invalidation received. This is compared to
+  // the invalidation version of policy stored to determine when the
+  // invalidated policy is up-to-date.
+  int64 invalidation_version_;
+
+  // The number of invalidations with unknown version received. Since such
+  // invalidations do not provide a version number, this count is used to set
+  // invalidation_version_ when such invalidations occur.
+  int unknown_version_invalidation_count_;
+
+  // The acknowledgment handle for the current invalidation.
+  syncer::AckHandle ack_handle_;
+
+  // WeakPtrFactory used to create callbacks to this object.
+  base::WeakPtrFactory<CloudPolicyInvalidator> weak_factory_;
+
+  // The maximum random delay, in ms, between receiving an invalidation and
+  // fetching the new policy.
+  int max_fetch_delay_;
+
+  // A thread checker to make sure that callbacks are invoked on the correct
+  // thread.
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(CloudPolicyInvalidator);
+};
+
+// Handles invalidations to cloud policy objects.
+class CloudPolicyInvalidationHandler {
+ public:
+  virtual ~CloudPolicyInvalidationHandler() {}
+
+  // This method is called when the current invalidation info should be set
+  // on the cloud policy client.
+  virtual void SetInvalidationInfo(
+      int64 version,
+      const std::string& payload) = 0;
+
+  // This method is called when the policy should be refreshed due to an
+  // invalidation. A policy fetch should be scheduled in the near future.
+  virtual void InvalidatePolicy() = 0;
+
+  // This method is called when the invalidator determines that the ability to
+  // receive policy invalidations becomes enabled or disabled. The invalidator
+  // starts in a disabled state, so the first call to this method is always when
+  // the invalidator becomes enabled.
+  virtual void OnInvalidatorStateChanged(bool invalidations_enabled) = 0;
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_POLICY_CLOUD_CLOUD_POLICY_INVALIDATOR_H_
diff --git a/chrome/browser/policy/cloud/cloud_policy_invalidator_unittest.cc b/chrome/browser/policy/cloud/cloud_policy_invalidator_unittest.cc
new file mode 100644
index 0000000..eed8bfc
--- /dev/null
+++ b/chrome/browser/policy/cloud/cloud_policy_invalidator_unittest.cc
@@ -0,0 +1,724 @@
+// Copyright 2013 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.
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/sample_map.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/invalidation/fake_invalidation_service.h"
+#include "chrome/browser/policy/cloud/cloud_policy_core.h"
+#include "chrome/browser/policy/cloud/cloud_policy_invalidator.h"
+#include "chrome/browser/policy/cloud/cloud_policy_service.h"
+#include "chrome/browser/policy/cloud/enterprise_metrics.h"
+#include "chrome/browser/policy/cloud/mock_cloud_policy_client.h"
+#include "chrome/browser/policy/cloud/mock_cloud_policy_store.h"
+#include "chrome/browser/policy/policy_types.h"
+#include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h"
+#include "policy/policy_constants.h"
+#include "sync/notifier/invalidation_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+class CloudPolicyInvalidatorTest : public testing::Test,
+                                   public CloudPolicyInvalidationHandler {
+ protected:
+  // Policy objects which can be used in tests.
+  enum PolicyObject {
+    POLICY_OBJECT_NONE,
+    POLICY_OBJECT_A,
+    POLICY_OBJECT_B
+  };
+
+  CloudPolicyInvalidatorTest();
+
+  virtual void SetUp() OVERRIDE;
+
+  virtual void TearDown() OVERRIDE;
+
+  // Starts the invalidator which will be tested.
+  void StartInvalidator(bool initialize);
+  void StartInvalidator() {
+    StartInvalidator(true /* initialize */);
+  }
+
+  // Simulates storing a new policy to the policy store.
+  // |object| determines which policy object the store will report the
+  // invalidator should register for. May be POLICY_OBJECT_NONE for no object.
+  // |invalidation_version| determines what invalidation the store will report.
+  // |policy_changed| determines whether the store will report that the
+  // policy changed.
+  // |timestamp| determines the response timestamp the store will report.
+  void StorePolicy(
+      PolicyObject object,
+      int64 invalidation_version,
+      bool policy_changed,
+      int64 timestamp);
+  void StorePolicy(
+      PolicyObject object,
+      int64 invalidation_version,
+      bool policy_changed) {
+    StorePolicy(object, invalidation_version, policy_changed, ++timestamp_);
+  }
+  void StorePolicy(PolicyObject object, int64 invalidation_version) {
+    StorePolicy(object, invalidation_version, false);
+  }
+  void StorePolicy(PolicyObject object) {
+    StorePolicy(object, 0);
+  }
+
+  // Disables the invalidation service. It is enabled by default.
+  void DisableInvalidationService();
+
+  // Enables the invalidation service. It is enabled by default.
+  void EnableInvalidationService();
+
+  // Causes the invalidation service to fire an invalidation. Returns an ack
+  // handle which be used to verify that the invalidation was acknowledged.
+  syncer::AckHandle FireInvalidation(
+      PolicyObject object,
+      int64 version,
+      const std::string& payload);
+
+  // Causes the invalidation service to fire an invalidation with unknown
+  // version. Returns an ack handle which be used to verify that the
+  // invalidation was acknowledged.
+  syncer::AckHandle FireInvalidation(PolicyObject object);
+
+  // Checks the expected value of the currently set invalidation info.
+  bool CheckInvalidationInfo(int64 version, const std::string& payload);
+
+  // Checks that the invalidate callback was not called.
+  bool CheckInvalidateNotCalled();
+
+  // Checks that the invalidate callback was called within an appropriate
+  // timeframe depending on whether the invalidation had unknown version.
+  bool CheckInvalidateCalled(bool unknown_version);
+  bool CheckInvalidateCalled() {
+    return CheckInvalidateCalled(true);
+  }
+
+  // Checks that the state changed callback of the invalidation handler was not
+  // called.
+  bool CheckStateChangedNotCalled();
+
+  // Checks that the state changed callback of the invalidation handler was
+  // called with the given state.
+  bool CheckStateChangedCalled(bool invalidations_enabled);
+
+  // Determines if the invalidation with the given ack handle has been
+  // acknowledged.
+  bool IsInvalidationAcknowledged(const syncer::AckHandle& ack_handle);
+
+  // Get the current count for the given metric.
+  base::HistogramBase::Count GetCount(MetricPolicyRefresh metric);
+  base::HistogramBase::Count GetInvalidationCount(bool with_payload);
+
+  // CloudPolicyInvalidationHandler:
+  virtual void SetInvalidationInfo(
+      int64 version,
+      const std::string& payload) OVERRIDE;
+  virtual void InvalidatePolicy() OVERRIDE;
+  virtual void OnInvalidatorStateChanged(bool invalidations_enabled) OVERRIDE;
+
+ private:
+  // Returns the object id of the given policy object.
+  const invalidation::ObjectId& GetPolicyObjectId(PolicyObject object) const;
+
+  // Get histogram samples for the given histogram.
+  scoped_ptr<base::HistogramSamples> GetHistogramSamples(
+      const std::string& name) const;
+
+  // Objects the invalidator depends on.
+  invalidation::FakeInvalidationService invalidation_service_;
+  MockCloudPolicyStore store_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+
+  // The invalidator which will be tested.
+  scoped_ptr<CloudPolicyInvalidator> invalidator_;
+
+  // The latest invalidation info set by the invalidator.
+  int64 invalidation_version_;
+  std::string invalidation_payload_;
+
+  // Object ids for the test policy objects.
+  invalidation::ObjectId object_id_a_;
+  invalidation::ObjectId object_id_b_;
+
+  // Increasing policy timestamp.
+  int64 timestamp_;
+
+  // Fake policy values which are alternated to cause the store to report a
+  // changed policy.
+  const char* policy_value_a_;
+  const char* policy_value_b_;
+
+  // The currently used policy value.
+  const char* policy_value_cur_;
+
+  // Stores how many times the invalidate callback was called.
+  int invalidate_callback_count_;
+
+  // Stores how many times the state change callback was called for each state.
+  int state_change_enabled_callback_count_;
+  int state_change_disabled_callback_count_;
+
+  // Stores starting histogram counts for kMetricPolicyRefresh.
+  scoped_ptr<base::HistogramSamples> refresh_samples_;
+
+  // Stores starting histogram counts for kMetricPolicyInvalidations.
+  scoped_ptr<base::HistogramSamples> invalidations_samples_;
+};
+
+CloudPolicyInvalidatorTest::CloudPolicyInvalidatorTest()
+    : task_runner_(new base::TestSimpleTaskRunner()),
+      invalidation_version_(0),
+      object_id_a_(135, "asdf"),
+      object_id_b_(246, "zxcv"),
+      timestamp_(123456),
+      policy_value_a_("asdf"),
+      policy_value_b_("zxcv"),
+      policy_value_cur_(policy_value_a_),
+      invalidate_callback_count_(0),
+      state_change_enabled_callback_count_(0),
+      state_change_disabled_callback_count_(0) {}
+
+void CloudPolicyInvalidatorTest::SetUp() {
+  base::StatisticsRecorder::Initialize();
+  refresh_samples_ = GetHistogramSamples(kMetricPolicyRefresh);
+  invalidations_samples_ = GetHistogramSamples(kMetricPolicyInvalidations);
+}
+
+void CloudPolicyInvalidatorTest::TearDown() {
+  EXPECT_FALSE(invalidation_service_.ReceivedInvalidAcknowledgement());
+  if (invalidator_)
+    invalidator_->Shutdown();
+}
+
+void CloudPolicyInvalidatorTest::StartInvalidator(bool initialize) {
+  invalidator_.reset(new CloudPolicyInvalidator(
+      this /* invalidation_handler */,
+      &store_,
+      task_runner_));
+  if (initialize)
+    invalidator_->InitializeWithService(&invalidation_service_);
+}
+
+void CloudPolicyInvalidatorTest::StorePolicy(
+    PolicyObject object,
+    int64 invalidation_version,
+    bool policy_changed,
+    int64 timestamp) {
+  enterprise_management::PolicyData* data =
+      new enterprise_management::PolicyData();
+  if (object != POLICY_OBJECT_NONE) {
+    data->set_invalidation_source(GetPolicyObjectId(object).source());
+    data->set_invalidation_name(GetPolicyObjectId(object).name());
+  }
+  data->set_timestamp(timestamp);
+  // Swap the policy value if a policy change is desired.
+  if (policy_changed)
+    policy_value_cur_ = policy_value_cur_ == policy_value_a_ ?
+        policy_value_b_ : policy_value_a_;
+  data->set_policy_value(policy_value_cur_);
+  store_.invalidation_version_ = invalidation_version;
+  store_.policy_.reset(data);
+  base::DictionaryValue policies;
+  policies.SetInteger(
+      key::kMaxInvalidationFetchDelay,
+      CloudPolicyInvalidator::kMaxFetchDelayMin);
+  store_.policy_map_.LoadFrom(
+      &policies,
+      POLICY_LEVEL_MANDATORY,
+      POLICY_SCOPE_MACHINE);
+  store_.NotifyStoreLoaded();
+}
+
+void CloudPolicyInvalidatorTest::DisableInvalidationService() {
+  invalidation_service_.SetInvalidatorState(
+      syncer::TRANSIENT_INVALIDATION_ERROR);
+}
+
+void CloudPolicyInvalidatorTest::EnableInvalidationService() {
+  invalidation_service_.SetInvalidatorState(syncer::INVALIDATIONS_ENABLED);
+}
+
+syncer::AckHandle CloudPolicyInvalidatorTest::FireInvalidation(
+    PolicyObject object,
+    int64 version,
+    const std::string& payload) {
+  return invalidation_service_.EmitInvalidationForTest(
+      GetPolicyObjectId(object),
+      version,
+      payload);
+}
+
+syncer::AckHandle CloudPolicyInvalidatorTest::FireInvalidation(
+    PolicyObject object) {
+  return invalidation_service_.EmitInvalidationForTest(
+      GetPolicyObjectId(object),
+      syncer::Invalidation::kUnknownVersion,
+      std::string());
+}
+
+bool CloudPolicyInvalidatorTest::CheckInvalidationInfo(
+    int64 version,
+    const std::string& payload) {
+  return version == invalidation_version_ && payload == invalidation_payload_;
+}
+
+bool CloudPolicyInvalidatorTest::CheckInvalidateNotCalled() {
+  bool result = true;
+  if (invalidate_callback_count_ != 0)
+    result = false;
+  task_runner_->RunUntilIdle();
+  if (invalidate_callback_count_ != 0)
+    result = false;
+  return result;
+}
+
+bool CloudPolicyInvalidatorTest::CheckInvalidateCalled(bool unknown_version) {
+  base::TimeDelta min_delay;
+  base::TimeDelta max_delay = base::TimeDelta::FromMilliseconds(
+      CloudPolicyInvalidator::kMaxFetchDelayMin);
+  if (unknown_version) {
+    base::TimeDelta additional_delay = base::TimeDelta::FromMinutes(
+        CloudPolicyInvalidator::kMissingPayloadDelay);
+    min_delay += additional_delay;
+    max_delay += additional_delay;
+  }
+
+  if (task_runner_->GetPendingTasks().empty())
+    return false;
+  base::TimeDelta actual_delay = task_runner_->GetPendingTasks().back().delay;
+  EXPECT_GE(actual_delay, min_delay);
+  EXPECT_LE(actual_delay, max_delay);
+
+  bool result = true;
+  if (invalidate_callback_count_ != 0)
+    result = false;
+  task_runner_->RunUntilIdle();
+  if (invalidate_callback_count_ != 1)
+    result = false;
+  invalidate_callback_count_ = 0;
+  return result;
+}
+
+bool CloudPolicyInvalidatorTest::CheckStateChangedNotCalled() {
+  return state_change_enabled_callback_count_ == 0 &&
+      state_change_disabled_callback_count_ == 0;
+}
+
+bool CloudPolicyInvalidatorTest::CheckStateChangedCalled(
+    bool invalidations_enabled) {
+  int expected_enabled_count_ = invalidations_enabled ? 1 : 0;
+  int expected_disabled_count_ = invalidations_enabled ? 0 : 1;
+  bool result = state_change_enabled_callback_count_ == expected_enabled_count_
+      && state_change_disabled_callback_count_ == expected_disabled_count_;
+  state_change_enabled_callback_count_ = 0;
+  state_change_disabled_callback_count_ = 0;
+  return result;
+}
+
+bool CloudPolicyInvalidatorTest::IsInvalidationAcknowledged(
+    const syncer::AckHandle& ack_handle) {
+  return invalidation_service_.IsInvalidationAcknowledged(ack_handle);
+}
+
+base::HistogramBase::Count CloudPolicyInvalidatorTest::GetCount(
+    MetricPolicyRefresh metric) {
+  return GetHistogramSamples(kMetricPolicyRefresh)->GetCount(metric) -
+      refresh_samples_->GetCount(metric);
+}
+
+base::HistogramBase::Count CloudPolicyInvalidatorTest::GetInvalidationCount(
+    bool with_payload) {
+  int metric = with_payload ? 1 : 0;
+  return GetHistogramSamples(kMetricPolicyInvalidations)->GetCount(metric) -
+      invalidations_samples_->GetCount(metric);
+}
+
+void CloudPolicyInvalidatorTest::SetInvalidationInfo(
+    int64 version,
+    const std::string& payload) {
+  invalidation_version_ = version;
+  invalidation_payload_ = payload;
+}
+
+void CloudPolicyInvalidatorTest::InvalidatePolicy() {
+  ++invalidate_callback_count_;
+}
+
+void CloudPolicyInvalidatorTest::OnInvalidatorStateChanged(
+    bool invalidations_enabled) {
+  if (invalidator_.get())
+    EXPECT_EQ(invalidations_enabled, invalidator_->invalidations_enabled());
+  if (invalidations_enabled)
+    ++state_change_enabled_callback_count_;
+  else
+    ++state_change_disabled_callback_count_;
+}
+
+const invalidation::ObjectId& CloudPolicyInvalidatorTest::GetPolicyObjectId(
+    PolicyObject object) const {
+  EXPECT_TRUE(object == POLICY_OBJECT_A || object == POLICY_OBJECT_B);
+  return object == POLICY_OBJECT_A ? object_id_a_ : object_id_b_;
+}
+
+scoped_ptr<base::HistogramSamples>
+    CloudPolicyInvalidatorTest::GetHistogramSamples(
+        const std::string& name) const {
+  base::HistogramBase* histogram =
+      base::StatisticsRecorder::FindHistogram(name);
+  if (!histogram)
+    return scoped_ptr<base::HistogramSamples>(new base::SampleMap());
+  return histogram->SnapshotSamples();
+}
+
+TEST_F(CloudPolicyInvalidatorTest, Uninitialized) {
+  // No invalidations should be processed if the invalidator is not intialized.
+  StartInvalidator(false /* initialize */);
+  StorePolicy(POLICY_OBJECT_A);
+  FireInvalidation(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckInvalidateNotCalled());
+}
+
+TEST_F(CloudPolicyInvalidatorTest, RegisterOnStoreLoaded) {
+  // No registration when store is not loaded.
+  StartInvalidator();
+  EXPECT_TRUE(CheckStateChangedNotCalled());
+  FireInvalidation(POLICY_OBJECT_A);
+  FireInvalidation(POLICY_OBJECT_B);
+  EXPECT_TRUE(CheckInvalidateNotCalled());
+
+  // No registration when store is loaded with no invalidation object id.
+  StorePolicy(POLICY_OBJECT_NONE);
+  EXPECT_TRUE(CheckStateChangedNotCalled());
+  FireInvalidation(POLICY_OBJECT_A);
+  FireInvalidation(POLICY_OBJECT_B);
+  EXPECT_TRUE(CheckInvalidateNotCalled());
+
+  // Check registration when store is loaded for object A.
+  StorePolicy(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckStateChangedCalled(true));
+  FireInvalidation(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckInvalidateCalled());
+  FireInvalidation(POLICY_OBJECT_B);
+  EXPECT_TRUE(CheckInvalidateNotCalled());
+}
+
+TEST_F(CloudPolicyInvalidatorTest, ChangeRegistration) {
+  // Register for object A.
+  StartInvalidator();
+  StorePolicy(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckStateChangedCalled(true));
+  FireInvalidation(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckInvalidateCalled());
+  FireInvalidation(POLICY_OBJECT_B);
+  EXPECT_TRUE(CheckInvalidateNotCalled());
+  syncer::AckHandle ack = FireInvalidation(POLICY_OBJECT_A);
+
+  // Check re-registration for object B. Make sure the pending invalidation for
+  // object A is acknowledged without making the callback.
+  StorePolicy(POLICY_OBJECT_B);
+  EXPECT_TRUE(CheckStateChangedNotCalled());
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack));
+  EXPECT_TRUE(CheckInvalidateNotCalled());
+
+  // Make sure future invalidations for object A are ignored and for object B
+  // are processed.
+  FireInvalidation(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckInvalidateNotCalled());
+  FireInvalidation(POLICY_OBJECT_B);
+  EXPECT_TRUE(CheckInvalidateCalled());
+}
+
+TEST_F(CloudPolicyInvalidatorTest, UnregisterOnStoreLoaded) {
+  // Register for object A.
+  StartInvalidator();
+  StorePolicy(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckStateChangedCalled(true));
+  FireInvalidation(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckInvalidateCalled());
+
+  // Check unregistration when store is loaded with no invalidation object id.
+  syncer::AckHandle ack = FireInvalidation(POLICY_OBJECT_A);
+  EXPECT_FALSE(IsInvalidationAcknowledged(ack));
+  StorePolicy(POLICY_OBJECT_NONE);
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack));
+  EXPECT_TRUE(CheckStateChangedCalled(false));
+  FireInvalidation(POLICY_OBJECT_A);
+  FireInvalidation(POLICY_OBJECT_B);
+  EXPECT_TRUE(CheckInvalidateNotCalled());
+
+  // Check re-registration for object B.
+  StorePolicy(POLICY_OBJECT_B);
+  EXPECT_TRUE(CheckStateChangedCalled(true));
+  FireInvalidation(POLICY_OBJECT_B);
+  EXPECT_TRUE(CheckInvalidateCalled());
+}
+
+TEST_F(CloudPolicyInvalidatorTest, HandleInvalidation) {
+  // Register and fire invalidation
+  StorePolicy(POLICY_OBJECT_A);
+  StartInvalidator();
+  EXPECT_TRUE(CheckStateChangedCalled(true));
+  syncer::AckHandle ack = FireInvalidation(POLICY_OBJECT_A, 12, "test_payload");
+
+  // Make sure client info is set as soon as the invalidation is received.
+  EXPECT_TRUE(CheckInvalidationInfo(12, "test_payload"));
+  EXPECT_TRUE(CheckInvalidateCalled(false /* unknown_version */));
+
+  // Make sure invalidation is not acknowledged until the store is loaded.
+  EXPECT_FALSE(IsInvalidationAcknowledged(ack));
+  EXPECT_TRUE(CheckInvalidationInfo(12, "test_payload"));
+  StorePolicy(POLICY_OBJECT_A, 12);
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack));
+  EXPECT_TRUE(CheckInvalidationInfo(0, std::string()));
+}
+
+TEST_F(CloudPolicyInvalidatorTest, HandleInvalidationWithUnknownVersion) {
+  // Register and fire invalidation with unknown version.
+  StorePolicy(POLICY_OBJECT_A);
+  StartInvalidator();
+  syncer::AckHandle ack = FireInvalidation(POLICY_OBJECT_A);
+
+  // Make sure client info is not set until after the invalidation callback is
+  // made.
+  EXPECT_TRUE(CheckInvalidationInfo(0, std::string()));
+  EXPECT_TRUE(CheckInvalidateCalled());
+  EXPECT_TRUE(CheckInvalidationInfo(-1, std::string()));
+
+  // Make sure invalidation is not acknowledged until the store is loaded.
+  EXPECT_FALSE(IsInvalidationAcknowledged(ack));
+  StorePolicy(POLICY_OBJECT_A, -1);
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack));
+  EXPECT_TRUE(CheckInvalidationInfo(0, std::string()));
+}
+
+TEST_F(CloudPolicyInvalidatorTest, HandleMultipleInvalidations) {
+  // Generate multiple invalidations.
+  StorePolicy(POLICY_OBJECT_A);
+  StartInvalidator();
+  syncer::AckHandle ack1 = FireInvalidation(POLICY_OBJECT_A, 1, "test1");
+  EXPECT_TRUE(CheckInvalidationInfo(1, "test1"));
+  syncer::AckHandle ack2 = FireInvalidation(POLICY_OBJECT_A, 2, "test2");
+  EXPECT_TRUE(CheckInvalidationInfo(2, "test2"));
+  syncer::AckHandle ack3= FireInvalidation(POLICY_OBJECT_A, 3, "test3");
+  EXPECT_TRUE(CheckInvalidationInfo(3, "test3"));
+
+  // Make sure the replaced invalidations are acknowledged.
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack1));
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack2));
+
+  // Make sure the invalidate callback is called once.
+  EXPECT_TRUE(CheckInvalidateCalled(false /* unknown_version */));
+
+  // Make sure that the last invalidation is only acknowledged after the store
+  // is loaded with the latest version.
+  StorePolicy(POLICY_OBJECT_A, 1);
+  EXPECT_FALSE(IsInvalidationAcknowledged(ack3));
+  StorePolicy(POLICY_OBJECT_A, 2);
+  EXPECT_FALSE(IsInvalidationAcknowledged(ack3));
+  StorePolicy(POLICY_OBJECT_A, 3);
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack3));
+}
+
+TEST_F(CloudPolicyInvalidatorTest,
+       HandleMultipleInvalidationsWithUnknownVersion) {
+  // Validate that multiple invalidations with unknown version each generate
+  // unique invalidation version numbers.
+  StorePolicy(POLICY_OBJECT_A);
+  StartInvalidator();
+  syncer::AckHandle ack1 = FireInvalidation(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckInvalidationInfo(0, std::string()));
+  EXPECT_TRUE(CheckInvalidateCalled());
+  EXPECT_TRUE(CheckInvalidationInfo(-1, std::string()));
+  syncer::AckHandle ack2 = FireInvalidation(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckInvalidationInfo(0, std::string()));
+  EXPECT_TRUE(CheckInvalidateCalled());
+  EXPECT_TRUE(CheckInvalidationInfo(-2, std::string()));
+  syncer::AckHandle ack3 = FireInvalidation(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckInvalidationInfo(0, std::string()));
+  EXPECT_TRUE(CheckInvalidateCalled());
+  EXPECT_TRUE(CheckInvalidationInfo(-3, std::string()));
+
+  // Make sure the replaced invalidations are acknowledged.
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack1));
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack2));
+
+  // Make sure that the last invalidation is only acknowledged after the store
+  // is loaded with the last unknown version.
+  StorePolicy(POLICY_OBJECT_A, -1);
+  EXPECT_FALSE(IsInvalidationAcknowledged(ack3));
+  StorePolicy(POLICY_OBJECT_A, -2);
+  EXPECT_FALSE(IsInvalidationAcknowledged(ack3));
+  StorePolicy(POLICY_OBJECT_A, -3);
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack3));
+}
+
+TEST_F(CloudPolicyInvalidatorTest, AcknowledgeBeforeInvalidateCallback) {
+  // Generate an invalidation.
+  StorePolicy(POLICY_OBJECT_A);
+  StartInvalidator();
+  syncer::AckHandle ack = FireInvalidation(POLICY_OBJECT_A, 3, "test");
+
+  // Ensure that the invalidate callback is not made and the invalidation is
+  // acknowledged if the store is loaded with the latest version before the
+  // callback is invoked.
+  StorePolicy(POLICY_OBJECT_A, 3);
+  EXPECT_TRUE(IsInvalidationAcknowledged(ack));
+  EXPECT_TRUE(CheckInvalidateNotCalled());
+}
+
+TEST_F(CloudPolicyInvalidatorTest, StateChanged) {
+  // Before registration, changes to the invalidation service state should not
+  // generate change state notifications.
+  StartInvalidator();
+  DisableInvalidationService();
+  EnableInvalidationService();
+  EXPECT_TRUE(CheckStateChangedNotCalled());
+
+  // After registration, changes to the invalidation service state should
+  // generate notifications.
+  StorePolicy(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckStateChangedCalled(true));
+  DisableInvalidationService();
+  EXPECT_TRUE(CheckStateChangedCalled(false));
+  DisableInvalidationService();
+  EXPECT_TRUE(CheckStateChangedNotCalled());
+  EnableInvalidationService();
+  EXPECT_TRUE(CheckStateChangedCalled(true));
+  EnableInvalidationService();
+  EXPECT_TRUE(CheckStateChangedNotCalled());
+
+  // When the invalidation service is enabled, changes to the registration
+  // state should generate notifications.
+  StorePolicy(POLICY_OBJECT_NONE);
+  EXPECT_TRUE(CheckStateChangedCalled(false));
+  StorePolicy(POLICY_OBJECT_NONE);
+  EXPECT_TRUE(CheckStateChangedNotCalled());
+  StorePolicy(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckStateChangedCalled(true));
+  StorePolicy(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckStateChangedNotCalled());
+
+  // When the invalidation service is disabled, changes to the registration
+  // state should not generate notifications.
+  DisableInvalidationService();
+  EXPECT_TRUE(CheckStateChangedCalled(false));
+  StorePolicy(POLICY_OBJECT_NONE);
+  StorePolicy(POLICY_OBJECT_A);
+  EXPECT_TRUE(CheckStateChangedNotCalled());
+}
+
+TEST_F(CloudPolicyInvalidatorTest, RefreshMetricsUnregistered) {
+  // Store loads occurring before invalidation registration are not counted.
+  StartInvalidator();
+  StorePolicy(POLICY_OBJECT_NONE, 0, false /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_NONE, 0, true /* policy_changed */);
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_CHANGED));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_UNCHANGED));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_CHANGED));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED));
+}
+
+TEST_F(CloudPolicyInvalidatorTest, RefreshMetricsNoInvalidations) {
+  // Store loads occurring while registered should be differentiated depending
+  // on whether the invalidation service was enabled or not.
+  StorePolicy(POLICY_OBJECT_A);
+  StartInvalidator();
+  StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */);
+  DisableInvalidationService();
+  StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */);
+  EXPECT_EQ(1, GetCount(METRIC_POLICY_REFRESH_CHANGED));
+  EXPECT_EQ(2, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS));
+  EXPECT_EQ(3, GetCount(METRIC_POLICY_REFRESH_UNCHANGED));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_CHANGED));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED));
+}
+
+TEST_F(CloudPolicyInvalidatorTest, RefreshMetricsStoreSameTimestamp) {
+  // Store loads with the same timestamp as the load which causes registration
+  // are not counted.
+  StartInvalidator();
+  StorePolicy(
+      POLICY_OBJECT_A, 0, false /* policy_changed */, 12 /* timestamp */);
+  StorePolicy(
+      POLICY_OBJECT_A, 0, false /* policy_changed */, 12 /* timestamp */);
+  StorePolicy(
+      POLICY_OBJECT_A, 0, true /* policy_changed */, 12 /* timestamp */);
+
+  // The next load with a different timestamp counts.
+  StorePolicy(
+      POLICY_OBJECT_A, 0, true /* policy_changed */, 13 /* timestamp */);
+
+  EXPECT_EQ(1, GetCount(METRIC_POLICY_REFRESH_CHANGED));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_UNCHANGED));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_CHANGED));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED));
+}
+
+TEST_F(CloudPolicyInvalidatorTest, RefreshMetricsInvalidation) {
+  // Store loads after an invalidation are counted as invalidated, even if
+  // the loads do not result in the invalidation being acknowledged.
+  StartInvalidator();
+  StorePolicy(POLICY_OBJECT_A);
+  FireInvalidation(POLICY_OBJECT_A, 5, "test");
+  StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 5, true /* policy_changed */);
+
+  // Store loads after the invalidation is complete are not counted as
+  // invalidated.
+  StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */);
+  StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */);
+
+  EXPECT_EQ(3, GetCount(METRIC_POLICY_REFRESH_CHANGED));
+  EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS));
+  EXPECT_EQ(4, GetCount(METRIC_POLICY_REFRESH_UNCHANGED));
+  EXPECT_EQ(2, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_CHANGED));
+  EXPECT_EQ(1, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED));
+}
+
+TEST_F(CloudPolicyInvalidatorTest, InvalidationMetrics) {
+  // Generate a mix of versioned and unknown-version invalidations.
+  StorePolicy(POLICY_OBJECT_A);
+  StartInvalidator();
+  FireInvalidation(POLICY_OBJECT_B);
+  FireInvalidation(POLICY_OBJECT_A);
+  FireInvalidation(POLICY_OBJECT_B, 1, "test");
+  FireInvalidation(POLICY_OBJECT_A, 1, "test");
+  FireInvalidation(POLICY_OBJECT_A, 2, "test");
+  FireInvalidation(POLICY_OBJECT_A);
+  FireInvalidation(POLICY_OBJECT_A);
+  FireInvalidation(POLICY_OBJECT_A, 3, "test");
+  FireInvalidation(POLICY_OBJECT_A, 4, "test");
+
+  // Verify that received invalidations metrics are correct.
+  EXPECT_EQ(3, GetInvalidationCount(false /* with_payload */));
+  EXPECT_EQ(4, GetInvalidationCount(true /* with_payload */));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/cloud/cloud_policy_manager.cc b/chrome/browser/policy/cloud/cloud_policy_manager.cc
index 59fcc73..4c704ba 100644
--- a/chrome/browser/policy/cloud/cloud_policy_manager.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_manager.cc
@@ -8,6 +8,7 @@
 #include "base/bind_helpers.h"
 #include "base/logging.h"
 #include "base/prefs/pref_service.h"
+#include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h"
 #include "chrome/browser/policy/cloud/cloud_policy_service.h"
 #include "chrome/browser/policy/policy_bundle.h"
 #include "chrome/browser/policy/policy_map.h"
@@ -30,6 +31,19 @@
 
 CloudPolicyManager::~CloudPolicyManager() {}
 
+void CloudPolicyManager::EnableInvalidations(
+    const base::Closure& initialize_invalidator) {
+  DCHECK(!initialize_invalidator.is_null());
+  DCHECK(initialize_invalidator_.is_null());
+  // If the refresh scheduler is already running, initialize the invalidator
+  // right away. Otherwise, store the closure so it can be invoked when the
+  // refresh scheduler starts.
+  if (core()->refresh_scheduler())
+    initialize_invalidator.Run();
+  else
+    initialize_invalidator_ = initialize_invalidator;
+}
+
 void CloudPolicyManager::Shutdown() {
   core_.Disconnect();
   store()->RemoveObserver(this);
@@ -66,6 +80,24 @@
   CheckAndPublishPolicy();
 }
 
+void CloudPolicyManager::SetInvalidationInfo(
+    int64 version,
+    const std::string& payload) {
+  DCHECK(core()->client());
+  core()->client()->SetInvalidationInfo(version, payload);
+}
+
+void CloudPolicyManager::InvalidatePolicy() {
+  DCHECK(core()->refresh_scheduler());
+  core()->refresh_scheduler()->RefreshSoon();
+}
+
+void CloudPolicyManager::OnInvalidatorStateChanged(bool invalidations_enabled) {
+  DCHECK(core()->refresh_scheduler());
+  core()->refresh_scheduler()->SetInvalidationServiceAvailability(
+      invalidations_enabled);
+}
+
 void CloudPolicyManager::CheckAndPublishPolicy() {
   if (IsInitializationComplete(POLICY_DOMAIN_CHROME) &&
       !waiting_for_policy_refresh_) {
@@ -73,6 +105,14 @@
   }
 }
 
+void CloudPolicyManager::StartRefreshScheduler() {
+  DCHECK(!core()->refresh_scheduler());
+  core()->StartRefreshScheduler();
+  // Initialize the invalidator if EnableInvalidations has been called.
+  if (!initialize_invalidator_.is_null())
+    initialize_invalidator_.Run();
+}
+
 scoped_ptr<PolicyBundle> CloudPolicyManager::CreatePolicyBundle() {
   scoped_ptr<PolicyBundle> bundle(new PolicyBundle);
   bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
diff --git a/chrome/browser/policy/cloud/cloud_policy_manager.h b/chrome/browser/policy/cloud/cloud_policy_manager.h
index 34be652..6d63e87 100644
--- a/chrome/browser/policy/cloud/cloud_policy_manager.h
+++ b/chrome/browser/policy/cloud/cloud_policy_manager.h
@@ -8,10 +8,12 @@
 #include <string>
 
 #include "base/basictypes.h"
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/prefs/pref_member.h"
 #include "chrome/browser/policy/cloud/cloud_policy_constants.h"
 #include "chrome/browser/policy/cloud/cloud_policy_core.h"
+#include "chrome/browser/policy/cloud/cloud_policy_invalidator.h"
 #include "chrome/browser/policy/cloud/cloud_policy_store.h"
 #include "chrome/browser/policy/configuration_policy_provider.h"
 
@@ -27,7 +29,8 @@
 // functionality specific to user-level and device-level cloud policy, such as
 // blocking on initial user policy fetch or device enrollment.
 class CloudPolicyManager : public ConfigurationPolicyProvider,
-                           public CloudPolicyStore::Observer {
+                           public CloudPolicyStore::Observer,
+                           public CloudPolicyInvalidationHandler {
  public:
   CloudPolicyManager(const PolicyNamespaceKey& policy_ns_key,
                      CloudPolicyStore* cloud_policy_store);
@@ -36,6 +39,13 @@
   CloudPolicyCore* core() { return &core_; }
   const CloudPolicyCore* core() const { return &core_; }
 
+  // Enables invalidations to the cloud policy. This method must only be
+  // called once.
+  // |initialize_invalidator| should initialize the invalidator when invoked.
+  // If the manager is ready to receive invalidations, it will be invoked
+  // immediately; otherwise, it will be invoked upon becoming ready.
+  void EnableInvalidations(const base::Closure& initialize_invalidator);
+
   // ConfigurationPolicyProvider:
   virtual void Shutdown() OVERRIDE;
   virtual bool IsInitializationComplete(PolicyDomain domain) const OVERRIDE;
@@ -45,11 +55,23 @@
   virtual void OnStoreLoaded(CloudPolicyStore* cloud_policy_store) OVERRIDE;
   virtual void OnStoreError(CloudPolicyStore* cloud_policy_store) OVERRIDE;
 
+  // CloudPolicyInvalidationHandler:
+  virtual void SetInvalidationInfo(
+      int64 version,
+      const std::string& payload) OVERRIDE;
+  virtual void InvalidatePolicy() OVERRIDE;
+  virtual void OnInvalidatorStateChanged(bool invalidations_enabled) OVERRIDE;
+
  protected:
   // Check whether fully initialized and if so, publish policy by calling
   // ConfigurationPolicyStore::UpdatePolicy().
   void CheckAndPublishPolicy();
 
+  // Starts the policy refresh scheduler. This must be called instead of calling
+  // enabling the scheduler on core() directly so that invalidations are
+  // enabled correctly.
+  void StartRefreshScheduler();
+
   // Called by CheckAndPublishPolicy() to create a bundle with the current
   // policies.
   virtual scoped_ptr<PolicyBundle> CreatePolicyBundle();
@@ -72,6 +94,10 @@
   // policy update notifications are deferred until after it completes.
   bool waiting_for_policy_refresh_;
 
+  // Used to initialize the policy invalidator once the refresh scheduler
+  // starts.
+  base::Closure initialize_invalidator_;
+
   DISALLOW_COPY_AND_ASSIGN(CloudPolicyManager);
 };
 
diff --git a/chrome/browser/policy/cloud/cloud_policy_manager_unittest.cc b/chrome/browser/policy/cloud/cloud_policy_manager_unittest.cc
index cbcb4d4..ee3422d 100644
--- a/chrome/browser/policy/cloud/cloud_policy_manager_unittest.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_manager_unittest.cc
@@ -5,17 +5,22 @@
 #include "chrome/browser/policy/cloud/cloud_policy_manager.h"
 
 #include "base/basictypes.h"
+#include "base/bind.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
+#include "base/test/test_simple_task_runner.h"
+#include "chrome/browser/invalidation/fake_invalidation_service.h"
 #include "chrome/browser/policy/cloud/cloud_policy_constants.h"
+#include "chrome/browser/policy/cloud/cloud_policy_invalidator.h"
 #include "chrome/browser/policy/cloud/mock_cloud_policy_client.h"
 #include "chrome/browser/policy/cloud/mock_cloud_policy_store.h"
 #include "chrome/browser/policy/cloud/policy_builder.h"
 #include "chrome/browser/policy/configuration_policy_provider_test.h"
 #include "chrome/browser/policy/external_data_fetcher.h"
 #include "chrome/browser/policy/mock_configuration_policy_provider.h"
+#include "sync/notifier/invalidation_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -143,6 +148,7 @@
   using CloudPolicyManager::store;
   using CloudPolicyManager::service;
   using CloudPolicyManager::CheckAndPublishPolicy;
+  using CloudPolicyManager::StartRefreshScheduler;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestCloudPolicyManager);
@@ -179,6 +185,57 @@
     manager_->Shutdown();
   }
 
+  // Sets up for an invalidations test.
+  void CreateInvalidator() {
+    // Add the invalidation registration info to the policy data.
+    em::PolicyData* policy_data = new em::PolicyData(policy_.policy_data());
+    policy_data->set_invalidation_source(12345);
+    policy_data->set_invalidation_name("12345");
+    store_.policy_.reset(policy_data);
+
+    // Connect the core.
+    MockCloudPolicyClient* client = new MockCloudPolicyClient();
+    EXPECT_CALL(*client, SetupRegistration(_, _));
+    manager_->core()->Connect(scoped_ptr<CloudPolicyClient>(client));
+
+    // Create invalidation objects.
+    task_runner_ = new base::TestSimpleTaskRunner();
+    invalidation_service_.reset(new invalidation::FakeInvalidationService());
+    invalidator_.reset(new CloudPolicyInvalidator(
+        manager_.get(),
+        manager_->core()->store(),
+        task_runner_));
+  }
+
+  void ShutdownInvalidator() {
+    invalidator_->Shutdown();
+  }
+
+  // Call EnableInvalidations on the manager.
+  void EnableInvalidations() {
+    manager_->EnableInvalidations(
+        base::Bind(
+            &CloudPolicyInvalidator::InitializeWithService,
+            base::Unretained(invalidator_.get()),
+            base::Unretained(invalidation_service_.get())));
+  }
+
+  // Determine if the invalidator has registered with the invalidation service.
+  bool IsInvalidatorRegistered() {
+    syncer::ObjectIdSet object_ids =
+        invalidation_service_->invalidator_registrar().GetAllRegisteredIds();
+    return object_ids.size() == 1 &&
+        object_ids.begin()->source() == 12345 &&
+        object_ids.begin()->name() == "12345";
+  }
+
+  // Determine if the invalidator is unregistered with the invalidation service.
+  bool IsInvalidatorUnregistered() {
+    syncer::ObjectIdSet object_ids =
+        invalidation_service_->invalidator_registrar().GetAllRegisteredIds();
+    return object_ids.empty();
+  }
+
   // Required by the refresh scheduler that's created by the manager.
   base::MessageLoop loop_;
 
@@ -193,6 +250,11 @@
   MockCloudPolicyStore store_;
   scoped_ptr<TestCloudPolicyManager> manager_;
 
+  // Invalidation objects.
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  scoped_ptr<invalidation::FakeInvalidationService> invalidation_service_;
+  scoped_ptr<CloudPolicyInvalidator> invalidator_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(CloudPolicyManagerTest);
 };
@@ -337,5 +399,25 @@
   EXPECT_TRUE(manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 }
 
+TEST_F(CloudPolicyManagerTest, EnableInvalidationsBeforeRefreshScheduler) {
+  CreateInvalidator();
+  EXPECT_TRUE(IsInvalidatorUnregistered());
+  EnableInvalidations();
+  EXPECT_TRUE(IsInvalidatorUnregistered());
+  manager_->StartRefreshScheduler();
+  EXPECT_TRUE(IsInvalidatorRegistered());
+  ShutdownInvalidator();
+}
+
+TEST_F(CloudPolicyManagerTest, EnableInvalidationsAfterRefreshScheduler) {
+  CreateInvalidator();
+  EXPECT_TRUE(IsInvalidatorUnregistered());
+  manager_->StartRefreshScheduler();
+  EXPECT_TRUE(IsInvalidatorUnregistered());
+  EnableInvalidations();
+  EXPECT_TRUE(IsInvalidatorRegistered());
+  ShutdownInvalidator();
+}
+
 }  // namespace
 }  // namespace policy
diff --git a/chrome/browser/policy/cloud/cloud_policy_service.cc b/chrome/browser/policy/cloud/cloud_policy_service.cc
index 1317d6a..fd71677 100644
--- a/chrome/browser/policy/cloud/cloud_policy_service.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_service.cc
@@ -68,7 +68,7 @@
   if (policy) {
     if (refresh_state_ != REFRESH_NONE)
       refresh_state_ = REFRESH_POLICY_STORE;
-    store_->Store(*policy);
+    store_->Store(*policy, client->fetched_invalidation_version());
   } else {
     RefreshCompleted(false);
   }
diff --git a/chrome/browser/policy/cloud/cloud_policy_service_unittest.cc b/chrome/browser/policy/cloud/cloud_policy_service_unittest.cc
index 495937e..68c22ee 100644
--- a/chrome/browser/policy/cloud/cloud_policy_service_unittest.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_service_unittest.cc
@@ -105,8 +105,11 @@
   em::PolicyFetchResponse policy;
   policy.set_policy_data("fake policy");
   client_.SetPolicy(policy_ns_key_, policy);
+  client_.fetched_invalidation_version_ = 12345;
   EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1);
+  EXPECT_EQ(0, store_.invalidation_version());
   client_.NotifyPolicyFetched();
+  EXPECT_EQ(12345, store_.invalidation_version());
 
   // Store reloads policy, callback gets triggered.
   store_.policy_.reset(new em::PolicyData());
diff --git a/chrome/browser/policy/cloud/cloud_policy_store.cc b/chrome/browser/policy/cloud/cloud_policy_store.cc
index 2205fb4..472c97d 100644
--- a/chrome/browser/policy/cloud/cloud_policy_store.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_store.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/policy/cloud/cloud_policy_store.h"
 
+#include "base/hash.h"
+
 namespace policy {
 
 CloudPolicyStore::Observer::~Observer() {}
@@ -11,10 +13,20 @@
 CloudPolicyStore::CloudPolicyStore()
     : status_(STATUS_OK),
       validation_status_(CloudPolicyValidatorBase::VALIDATION_OK),
-      is_initialized_(false) {}
+      invalidation_version_(0),
+      is_initialized_(false),
+      policy_changed_(false),
+      hash_value_(0) {}
 
 CloudPolicyStore::~CloudPolicyStore() {}
 
+void CloudPolicyStore::Store(
+    const enterprise_management::PolicyFetchResponse& policy,
+    int64 invalidation_version) {
+  invalidation_version_ = invalidation_version;
+  Store(policy);
+}
+
 void CloudPolicyStore::AddObserver(CloudPolicyStore::Observer* observer) {
   observers_.AddObserver(observer);
 }
@@ -24,6 +36,14 @@
 }
 
 void CloudPolicyStore::NotifyStoreLoaded() {
+  // Determine if the policy changed by comparing the new policy's hash value
+  // to the previous.
+  uint32 new_hash_value = 0;
+  if (policy_ && policy_->has_policy_value())
+    new_hash_value = base::Hash(policy_->policy_value());
+  policy_changed_ = new_hash_value != hash_value_;
+  hash_value_ = new_hash_value;
+
   is_initialized_ = true;
   FOR_EACH_OBSERVER(Observer, observers_, OnStoreLoaded(this));
 }
@@ -33,4 +53,4 @@
   FOR_EACH_OBSERVER(Observer, observers_, OnStoreError(this));
 }
 
-}  // namespace
+}  // namespace policy
diff --git a/chrome/browser/policy/cloud/cloud_policy_store.h b/chrome/browser/policy/cloud/cloud_policy_store.h
index fee92b7..75cab26 100644
--- a/chrome/browser/policy/cloud/cloud_policy_store.h
+++ b/chrome/browser/policy/cloud/cloud_policy_store.h
@@ -73,11 +73,23 @@
     return validation_status_;
   }
 
+  // Returns true if the latest policy loaded was different from the previous
+  // policy.
+  bool policy_changed() const {
+    return policy_changed_;
+  }
+
   // Store a new policy blob. Pending load/store operations will be canceled.
   // The store operation may proceed asynchronously and observers are notified
   // once the operation finishes. If successful, OnStoreLoaded() will be invoked
   // on the observers and the updated policy can be read through policy().
   // Errors generate OnStoreError() notifications.
+  // |invalidation_version| is the invalidation version of the policy to be
+  // stored.
+  void Store(
+      const enterprise_management::PolicyFetchResponse& policy,
+      int64 invalidation_version);
+
   virtual void Store(
       const enterprise_management::PolicyFetchResponse& policy) = 0;
 
@@ -93,6 +105,12 @@
   // Removes the specified observer.
   void RemoveObserver(Observer* observer);
 
+  // The invalidation version of the last policy stored. This value can be read
+  // by observers to determine which version of the policy is now available.
+  int64 invalidation_version() {
+    return invalidation_version_;
+  }
+
  protected:
   // Invokes the corresponding callback on all registered observers.
   void NotifyStoreLoaded();
@@ -110,11 +128,21 @@
   // Latest validation status.
   CloudPolicyValidatorBase::Status validation_status_;
 
+  // The invalidation version of the last policy stored.
+  int64 invalidation_version_;
+
  private:
   // Whether the store has completed asynchronous initialization, which is
   // triggered by calling Load().
   bool is_initialized_;
 
+  // Whether latest policy loaded was different from the previous policy.
+  bool policy_changed_;
+
+  // The hash value of the current policy. This is used to determine when the
+  // policy changes.
+  uint32 hash_value_;
+
   ObserverList<Observer, true> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(CloudPolicyStore);
diff --git a/chrome/browser/policy/cloud/enterprise_metrics.cc b/chrome/browser/policy/cloud/enterprise_metrics.cc
index e2bbb0e..b9d4eda 100644
--- a/chrome/browser/policy/cloud/enterprise_metrics.cc
+++ b/chrome/browser/policy/cloud/enterprise_metrics.cc
@@ -6,8 +6,10 @@
 
 namespace policy {
 
-const char* kMetricToken = "Enterprise.DMToken";
-const char* kMetricPolicy = "Enterprise.Policy";
-const char* kMetricEnrollment = "Enterprise.Enrollment";
+const char kMetricToken[] = "Enterprise.DMToken";
+const char kMetricPolicy[] = "Enterprise.Policy";
+const char kMetricEnrollment[] = "Enterprise.Enrollment";
+const char kMetricPolicyRefresh[] = "Enterprise.PolicyRefresh";
+const char kMetricPolicyInvalidations[] = "Enterprise.PolicyInvalidations";
 
 }  // namespace policy
diff --git a/chrome/browser/policy/cloud/enterprise_metrics.h b/chrome/browser/policy/cloud/enterprise_metrics.h
index 6d262e0..bb33453 100644
--- a/chrome/browser/policy/cloud/enterprise_metrics.h
+++ b/chrome/browser/policy/cloud/enterprise_metrics.h
@@ -159,12 +159,35 @@
   kMetricEnrollmentSize  // Must be the last.
 };
 
+// Events related to policy refresh.
+enum MetricPolicyRefresh {
+  // A refresh occurred while the policy was not invalidated and the policy was
+  // changed. Invalidations were enabled.
+  METRIC_POLICY_REFRESH_CHANGED,
+  // A refresh occurred while the policy was not invalidated and the policy was
+  // changed. Invalidations were disabled.
+  METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS,
+  // A refresh occurred while the policy was not invalidated and the policy was
+  // unchanged.
+  METRIC_POLICY_REFRESH_UNCHANGED,
+  // A refresh occurred while the policy was invalidated and the policy was
+  // changed.
+  METRIC_POLICY_REFRESH_INVALIDATED_CHANGED,
+  // A refresh occurred while the policy was invalidated and the policy was
+  // unchanged.
+  METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED,
+
+  METRIC_POLICY_REFRESH_SIZE  // Must be the last.
+};
+
 // Names for the UMA counters. They are shared from here since the events
 // from the same enum above can be triggered in different files, and must use
 // the same UMA histogram name.
-extern const char* kMetricToken;
-extern const char* kMetricPolicy;
-extern const char* kMetricEnrollment;
+extern const char kMetricToken[];
+extern const char kMetricPolicy[];
+extern const char kMetricEnrollment[];
+extern const char kMetricPolicyRefresh[];
+extern const char kMetricPolicyInvalidations[];
 
 }  // namespace policy
 
diff --git a/chrome/browser/policy/cloud/mock_cloud_policy_client.h b/chrome/browser/policy/cloud/mock_cloud_policy_client.h
index e19e4f9..db9aca9 100644
--- a/chrome/browser/policy/cloud/mock_cloud_policy_client.h
+++ b/chrome/browser/policy/cloud/mock_cloud_policy_client.h
@@ -50,6 +50,9 @@
   using CloudPolicyClient::public_key_version_;
   using CloudPolicyClient::public_key_version_valid_;
   using CloudPolicyClient::namespaces_to_fetch_;
+  using CloudPolicyClient::invalidation_version_;
+  using CloudPolicyClient::invalidation_payload_;
+  using CloudPolicyClient::fetched_invalidation_version_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockCloudPolicyClient);
diff --git a/chrome/browser/policy/cloud/mock_cloud_policy_store.h b/chrome/browser/policy/cloud/mock_cloud_policy_store.h
index 3908f51..684f37c 100644
--- a/chrome/browser/policy/cloud/mock_cloud_policy_store.h
+++ b/chrome/browser/policy/cloud/mock_cloud_policy_store.h
@@ -25,6 +25,7 @@
   using CloudPolicyStore::policy_map_;
   using CloudPolicyStore::policy_;
   using CloudPolicyStore::status_;
+  using CloudPolicyStore::invalidation_version_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockCloudPolicyStore);
diff --git a/chrome/browser/policy/cloud/user_cloud_policy_invalidator.cc b/chrome/browser/policy/cloud/user_cloud_policy_invalidator.cc
new file mode 100644
index 0000000..26e001a
--- /dev/null
+++ b/chrome/browser/policy/cloud/user_cloud_policy_invalidator.cc
@@ -0,0 +1,50 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/policy/cloud/user_cloud_policy_invalidator.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/policy/cloud/cloud_policy_core.h"
+#include "chrome/browser/policy/cloud/cloud_policy_manager.h"
+#include "content/public/browser/notification_source.h"
+
+namespace policy {
+
+UserCloudPolicyInvalidator::UserCloudPolicyInvalidator(
+    Profile* profile,
+    CloudPolicyManager* policy_manager)
+    : CloudPolicyInvalidator(
+          policy_manager,
+          policy_manager->core()->store(),
+          base::MessageLoopProxy::current()),
+      profile_(profile),
+      policy_manager_(policy_manager) {
+  DCHECK(profile);
+
+  // Register for notification that profile creation is complete.
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_PROFILE_ADDED,
+                 content::Source<Profile>(profile));
+}
+
+void UserCloudPolicyInvalidator::Shutdown() {
+  CloudPolicyInvalidator::Shutdown();
+}
+
+void UserCloudPolicyInvalidator::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  // Enable invalidations now that profile creation is complete.
+  DCHECK(type == chrome::NOTIFICATION_PROFILE_ADDED);
+  policy_manager_->EnableInvalidations(
+      base::Bind(
+          &CloudPolicyInvalidator::InitializeWithProfile,
+          GetWeakPtr(),
+          base::Unretained(profile_)));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/cloud/user_cloud_policy_invalidator.h b/chrome/browser/policy/cloud/user_cloud_policy_invalidator.h
new file mode 100644
index 0000000..96988c1
--- /dev/null
+++ b/chrome/browser/policy/cloud/user_cloud_policy_invalidator.h
@@ -0,0 +1,57 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_POLICY_CLOUD_USER_CLOUD_POLICY_INVALIDATOR_H_
+#define CHROME_BROWSER_POLICY_CLOUD_USER_CLOUD_POLICY_INVALIDATOR_H_
+
+#include "chrome/browser/policy/cloud/cloud_policy_invalidator.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+class Profile;
+
+namespace policy {
+
+class CloudPolicyManager;
+
+// Provides invalidations to user policy. Implemented as a
+// BrowserContextKeyedService to allow profile-based lifetime management.
+class UserCloudPolicyInvalidator : public CloudPolicyInvalidator,
+                                   public BrowserContextKeyedService,
+                                   public content::NotificationObserver {
+ public:
+  // |profile| is profile associated with the invalidator. It is used to get
+  // a reference to the profile's invalidation service. Both the profile and
+  // invalidation service must remain valid until Shutdown is called.
+  // |policy_manager| is the policy manager for the user policy and must remain
+  // valid until Shutdown is called.
+  UserCloudPolicyInvalidator(
+      Profile* profile,
+      CloudPolicyManager* policy_manager);
+
+  // BrowserContextKeyedService:
+  virtual void Shutdown() OVERRIDE;
+
+  // content::NotificationObserver implementation:
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
+ private:
+  // The profile associated with the invalidator.
+  Profile* profile_;
+
+  // The policy manager for the user policy.
+  CloudPolicyManager* policy_manager_;
+
+  // Used to register for notification that profile creation is complete.
+  content::NotificationRegistrar registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyInvalidator);
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_POLICY_CLOUD_USER_CLOUD_POLICY_INVALIDATOR_H_
diff --git a/chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.cc b/chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.cc
new file mode 100644
index 0000000..42ec772
--- /dev/null
+++ b/chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.cc
@@ -0,0 +1,76 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.h"
+
+#include "base/command_line.h"
+#include "chrome/browser/invalidation/invalidation_service_factory.h"
+#include "chrome/browser/policy/cloud/user_cloud_policy_invalidator.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_switches.h"
+#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
+#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h"
+#else
+#include "chrome/browser/policy/cloud/user_cloud_policy_manager.h"
+#include "chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h"
+#endif
+
+namespace policy {
+
+// static
+UserCloudPolicyInvalidatorFactory*
+    UserCloudPolicyInvalidatorFactory::GetInstance() {
+  return Singleton<UserCloudPolicyInvalidatorFactory>::get();
+}
+
+UserCloudPolicyInvalidatorFactory::UserCloudPolicyInvalidatorFactory()
+    : BrowserContextKeyedServiceFactory(
+          "UserCloudPolicyInvalidator",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(invalidation::InvalidationServiceFactory::GetInstance());
+#if defined(OS_CHROMEOS)
+  DependsOn(UserCloudPolicyManagerFactoryChromeOS::GetInstance());
+#else
+  DependsOn(UserCloudPolicyManagerFactory::GetInstance());
+#endif
+}
+
+UserCloudPolicyInvalidatorFactory::~UserCloudPolicyInvalidatorFactory() {}
+
+BrowserContextKeyedService*
+    UserCloudPolicyInvalidatorFactory::BuildServiceInstanceFor(
+        content::BrowserContext* context) const {
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableCloudPolicyPush)) {
+    return NULL;
+  }
+
+  Profile* profile = static_cast<Profile*>(context);
+#if defined(OS_CHROMEOS)
+  CloudPolicyManager* policy_manager =
+      UserCloudPolicyManagerFactoryChromeOS::GetForProfile(profile);
+#else
+  CloudPolicyManager* policy_manager =
+      UserCloudPolicyManagerFactory::GetForProfile(profile);
+#endif
+  if (!policy_manager)
+    return NULL;
+
+  return new UserCloudPolicyInvalidator(profile, policy_manager);
+}
+
+bool UserCloudPolicyInvalidatorFactory::
+ServiceIsCreatedWithBrowserContext() const {
+  // Must be automatically created to enable user policy invalidations.
+  return true;
+}
+
+bool UserCloudPolicyInvalidatorFactory::ServiceIsNULLWhileTesting() const {
+  // Not used in tests.
+  return true;
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.h b/chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.h
new file mode 100644
index 0000000..354801b
--- /dev/null
+++ b/chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.h
@@ -0,0 +1,36 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_POLICY_CLOUD_USER_CLOUD_POLICY_INVALIDATOR_FACTORY_H_
+#define CHROME_BROWSER_POLICY_CLOUD_USER_CLOUD_POLICY_INVALIDATOR_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
+
+namespace policy {
+
+// Creates an instance of UserCloudPolicyInvalidator for each profile.
+class UserCloudPolicyInvalidatorFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static UserCloudPolicyInvalidatorFactory* GetInstance();
+
+ private:
+  friend struct DefaultSingletonTraits<UserCloudPolicyInvalidatorFactory>;
+
+  UserCloudPolicyInvalidatorFactory();
+  virtual ~UserCloudPolicyInvalidatorFactory();
+
+  // BrowserContextKeyedServiceFactory:
+  virtual BrowserContextKeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const OVERRIDE;
+  virtual bool ServiceIsCreatedWithBrowserContext() const OVERRIDE;
+  virtual bool ServiceIsNULLWhileTesting() const OVERRIDE;
+
+  DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyInvalidatorFactory);
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_POLICY_CLOUD_USER_CLOUD_POLICY_INVALIDATOR_FACTORY_H_
diff --git a/chrome/browser/policy/cloud/user_cloud_policy_manager.cc b/chrome/browser/policy/cloud/user_cloud_policy_manager.cc
index 09db701..1772f03 100644
--- a/chrome/browser/policy/cloud/user_cloud_policy_manager.cc
+++ b/chrome/browser/policy/cloud/user_cloud_policy_manager.cc
@@ -35,7 +35,7 @@
 void UserCloudPolicyManager::Connect(
     PrefService* local_state, scoped_ptr<CloudPolicyClient> client) {
   core()->Connect(client.Pass());
-  core()->StartRefreshScheduler();
+  StartRefreshScheduler();
   core()->TrackRefreshDelayPref(local_state, prefs::kUserPolicyRefreshRate);
 }
 
diff --git a/chrome/browser/policy/cloud/user_cloud_policy_store_unittest.cc b/chrome/browser/policy/cloud/user_cloud_policy_store_unittest.cc
index 38ec96a..1144ae9 100644
--- a/chrome/browser/policy/cloud/user_cloud_policy_store_unittest.cc
+++ b/chrome/browser/policy/cloud/user_cloud_policy_store_unittest.cc
@@ -115,6 +115,7 @@
 
   EXPECT_FALSE(store_->policy());
   EXPECT_TRUE(store_->policy_map().empty());
+  EXPECT_FALSE(store_->policy_changed());
 }
 
 TEST_F(UserCloudPolicyStoreTest, LoadWithInvalidFile) {
@@ -135,6 +136,7 @@
 
   EXPECT_FALSE(store_->policy());
   EXPECT_TRUE(store_->policy_map().empty());
+  EXPECT_FALSE(store_->policy_changed());
 }
 
 TEST_F(UserCloudPolicyStoreTest, LoadImmediatelyWithNoFile) {
@@ -146,6 +148,7 @@
 
   EXPECT_FALSE(store_->policy());
   EXPECT_TRUE(store_->policy_map().empty());
+  EXPECT_FALSE(store_->policy_changed());
 }
 
 TEST_F(UserCloudPolicyStoreTest, LoadImmediatelyWithInvalidFile) {
@@ -165,6 +168,7 @@
 
   EXPECT_FALSE(store_->policy());
   EXPECT_TRUE(store_->policy_map().empty());
+  EXPECT_FALSE(store_->policy_changed());
 }
 
 TEST_F(UserCloudPolicyStoreTest, Store) {
@@ -283,6 +287,7 @@
   VerifyPolicyMap(store2.get());
   EXPECT_EQ(CloudPolicyStore::STATUS_OK, store2->status());
   store2->RemoveObserver(&observer_);
+  EXPECT_TRUE(store2->policy_changed());
 }
 
 TEST_F(UserCloudPolicyStoreTest, StoreValidationError) {
@@ -295,6 +300,7 @@
   store_->Store(policy_.policy());
   RunUntilIdle();
   ASSERT_FALSE(store_->policy());
+  EXPECT_FALSE(store_->policy_changed());
 }
 
 TEST_F(UserCloudPolicyStoreTest, LoadValidationError) {
@@ -348,6 +354,46 @@
   store4->RemoveObserver(&observer_);
 }
 
+TEST_F(UserCloudPolicyStoreTest, PolicyChanged) {
+  EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).Times(7);
+  EXPECT_FALSE(store_->policy_changed());
+
+  // Clearing before storing should not result in a change.
+  store_->Clear();
+  EXPECT_FALSE(store_->policy_changed());
+
+  // Storing an initial policy should result in a change.
+  store_->Store(policy_.policy());
+  RunUntilIdle();
+  EXPECT_TRUE(store_->policy_changed());
+
+  // Storing the same policy should not result in a change.
+  store_->Store(policy_.policy());
+  RunUntilIdle();
+  EXPECT_FALSE(store_->policy_changed());
+
+  // Storing a modified policy should result in a change.
+  policy_.payload().mutable_urlblacklist()->mutable_value()->add_entries(
+      "build.chromium.org");
+  policy_.Build();
+  store_->Store(policy_.policy());
+  RunUntilIdle();
+  EXPECT_TRUE(store_->policy_changed());
+
+  // Storing the same policy should not result in a change.
+  store_->Store(policy_.policy());
+  RunUntilIdle();
+  EXPECT_FALSE(store_->policy_changed());
+
+  // Clearing the policy should result in a change.
+  store_->Clear();
+  EXPECT_TRUE(store_->policy_changed());
+
+  // Clearing the policy again shouldn't result in a change.
+  store_->Clear();
+  EXPECT_FALSE(store_->policy_changed());
+}
+
 }  // namespace
 
 }  // namespace policy
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service.cc b/chrome/browser/policy/cloud/user_policy_signin_service.cc
index ba593a9..eafb1d3 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service.cc
@@ -23,10 +23,6 @@
 #include "content/public/browser/notification_source.h"
 #include "google_apis/gaia/gaia_constants.h"
 
-#if defined(ENABLE_MANAGED_USERS)
-#include "chrome/browser/managed_mode/managed_user_service.h"
-#endif
-
 namespace policy {
 
 UserPolicySigninService::UserPolicySigninService(
@@ -102,12 +98,10 @@
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
 
-#if defined(ENABLE_MANAGED_USERS)
-  if (ManagedUserService::ProfileIsManaged(profile())) {
+  if (profile()->IsManaged()) {
     registrar()->RemoveAll();
     return;
   }
-#endif
 
   // If using a TestingProfile with no SigninManager or UserCloudPolicyManager,
   // skip initialization.
diff --git a/chrome/browser/policy/configuration_policy_handler.cc b/chrome/browser/policy/configuration_policy_handler.cc
index a1ba5bb..b31f689 100644
--- a/chrome/browser/policy/configuration_policy_handler.cc
+++ b/chrome/browser/policy/configuration_policy_handler.cc
@@ -100,6 +100,21 @@
   { key::kDefaultSearchProviderSearchTermsReplacementKey,
     prefs::kDefaultSearchProviderSearchTermsReplacementKey,
     Value::TYPE_STRING },
+  { key::kDefaultSearchProviderImageURL,
+    prefs::kDefaultSearchProviderImageURL,
+    Value::TYPE_STRING },
+  { key::kDefaultSearchProviderSearchURLPostParams,
+    prefs::kDefaultSearchProviderSearchURLPostParams,
+    Value::TYPE_STRING },
+  { key::kDefaultSearchProviderSuggestURLPostParams,
+    prefs::kDefaultSearchProviderSuggestURLPostParams,
+    Value::TYPE_STRING },
+  { key::kDefaultSearchProviderInstantURLPostParams,
+    prefs::kDefaultSearchProviderInstantURLPostParams,
+    Value::TYPE_STRING },
+  { key::kDefaultSearchProviderImageURLPostParams,
+    prefs::kDefaultSearchProviderImageURLPostParams,
+    Value::TYPE_STRING },
 };
 
 // List of entries determining which proxy policies can be specified, depending
@@ -956,6 +971,15 @@
                     new ListValue());
     prefs->SetString(prefs::kDefaultSearchProviderSearchTermsReplacementKey,
                      std::string());
+    prefs->SetString(prefs::kDefaultSearchProviderImageURL, std::string());
+    prefs->SetString(prefs::kDefaultSearchProviderSearchURLPostParams,
+                     std::string());
+    prefs->SetString(prefs::kDefaultSearchProviderSuggestURLPostParams,
+                     std::string());
+    prefs->SetString(prefs::kDefaultSearchProviderInstantURLPostParams,
+                     std::string());
+    prefs->SetString(prefs::kDefaultSearchProviderImageURLPostParams,
+                     std::string());
   } else {
     // The search URL is required.  The other entries are optional.  Just make
     // sure that they are all specified via policy, so that the regular prefs
@@ -975,6 +999,16 @@
       EnsureListPrefExists(prefs, prefs::kDefaultSearchProviderAlternateURLs);
       EnsureStringPrefExists(prefs,
           prefs::kDefaultSearchProviderSearchTermsReplacementKey);
+      EnsureStringPrefExists(prefs,
+          prefs::kDefaultSearchProviderImageURL);
+      EnsureStringPrefExists(prefs,
+          prefs::kDefaultSearchProviderSearchURLPostParams);
+      EnsureStringPrefExists(prefs,
+          prefs::kDefaultSearchProviderSuggestURLPostParams);
+      EnsureStringPrefExists(prefs,
+          prefs::kDefaultSearchProviderInstantURLPostParams);
+      EnsureStringPrefExists(prefs,
+          prefs::kDefaultSearchProviderImageURLPostParams);
 
       // For the name and keyword, default to the host if not specified.  If
       // there is no host (file: URLs?  Not sure), use "_" to guarantee that the
@@ -1000,7 +1034,6 @@
       chrome::NOTIFICATION_DEFAULT_SEARCH_POLICY_CHANGED,
       content::NotificationService::AllSources(),
       content::NotificationService::NoDetails());
-
 }
 
 bool DefaultSearchPolicyHandler::CheckIndividualPolicies(
diff --git a/chrome/browser/policy/configuration_policy_handler_list.cc b/chrome/browser/policy/configuration_policy_handler_list.cc
index 47f3a7b..08a1a38 100644
--- a/chrome/browser/policy/configuration_policy_handler_list.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list.cc
@@ -246,6 +246,9 @@
   { key::kRemoteAccessHostRequireCurtain,
     prefs::kRemoteAccessHostRequireCurtain,
     Value::TYPE_BOOLEAN },
+  { key::kRemoteAccessHostAllowClientPairing,
+    prefs::kRemoteAccessHostAllowClientPairing,
+    Value::TYPE_BOOLEAN },
   { key::kCloudPrintProxyEnabled,
     prefs::kCloudPrintProxyEnabled,
     Value::TYPE_BOOLEAN },
diff --git a/chrome/browser/policy/configuration_policy_pref_store.cc b/chrome/browser/policy/configuration_policy_pref_store.cc
index c054471..8b7d10a 100644
--- a/chrome/browser/policy/configuration_policy_pref_store.cc
+++ b/chrome/browser/policy/configuration_policy_pref_store.cc
@@ -88,16 +88,6 @@
 void ConfigurationPolicyPrefStore::OnPolicyServiceInitialized(
     PolicyDomain domain) {
   if (domain == POLICY_DOMAIN_CHROME) {
-    // Subtle: the current implementation of PolicyServiceImpl sends out this
-    // notification before invoking OnPolicyUpdated(), which gets posted to
-    // execute "soon" after on the current message loop.
-    // But the PrefService will start reading the preferences published by
-    // this store once OnInitializationCompleted() is sent out, so make sure
-    // this is reading the current policies before doing so.
-    // TODO(joaodasilva): remove this, and fix the order of notifications from
-    // the PolicyService instead. http://crbug.com/263728
-    Refresh();
-
     FOR_EACH_OBSERVER(PrefStore::Observer, observers_,
                       OnInitializationCompleted(true));
   }
diff --git a/chrome/browser/policy/configuration_policy_pref_store_unittest.cc b/chrome/browser/policy/configuration_policy_pref_store_unittest.cc
index d5981d6..d628d15 100644
--- a/chrome/browser/policy/configuration_policy_pref_store_unittest.cc
+++ b/chrome/browser/policy/configuration_policy_pref_store_unittest.cc
@@ -634,6 +634,8 @@
   static const char* const kName;
   static const char* const kKeyword;
   static const char* const kReplacementKey;
+  static const char* const kImageURL;
+  static const char* const kImageParams;
 
   // Build a default search policy by setting search-related keys in |policy| to
   // reasonable values. You can update any of the keys after calling this
@@ -655,6 +657,10 @@
     "MyKeyword";
 const char* const
     ConfigurationPolicyPrefStoreDefaultSearchTest::kReplacementKey = "espv";
+const char* const ConfigurationPolicyPrefStoreDefaultSearchTest::kImageURL =
+    "http://test.com/searchbyimage/upload";
+const char* const ConfigurationPolicyPrefStoreDefaultSearchTest::kImageParams =
+    "image_content=content,image_url=http://test.com/test.png";
 
 void ConfigurationPolicyPrefStoreDefaultSearchTest::
     BuildDefaultSearchPolicy(PolicyMap* policy) {
@@ -684,6 +690,12 @@
   policy->Set(key::kDefaultSearchProviderSearchTermsReplacementKey,
               POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
               base::Value::CreateStringValue(kReplacementKey), NULL);
+  policy->Set(key::kDefaultSearchProviderImageURL,
+              POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+              base::Value::CreateStringValue(kImageURL), NULL);
+  policy->Set(key::kDefaultSearchProviderImageURLPostParams,
+              POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+              base::Value::CreateStringValue(kImageParams), NULL);
 }
 
 // Checks that if the policy for default search is valid, i.e. there's a
@@ -729,6 +741,25 @@
       store_->GetValue(prefs::kDefaultSearchProviderSearchTermsReplacementKey,
       &value));
   EXPECT_TRUE(base::StringValue(std::string()).Equals(value));
+
+  EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, &value));
+  EXPECT_TRUE(base::StringValue(std::string()).Equals(value));
+
+  EXPECT_TRUE(store_->GetValue(
+      prefs::kDefaultSearchProviderSearchURLPostParams, &value));
+  EXPECT_TRUE(base::StringValue(std::string()).Equals(value));
+
+  EXPECT_TRUE(store_->GetValue(
+      prefs::kDefaultSearchProviderSuggestURLPostParams, &value));
+  EXPECT_TRUE(base::StringValue(std::string()).Equals(value));
+
+  EXPECT_TRUE(store_->GetValue(
+      prefs::kDefaultSearchProviderInstantURLPostParams, &value));
+  EXPECT_TRUE(base::StringValue(std::string()).Equals(value));
+
+  EXPECT_TRUE(store_->GetValue(
+      prefs::kDefaultSearchProviderImageURLPostParams, &value));
+  EXPECT_TRUE(base::StringValue(std::string()).Equals(value));
 }
 
 // Checks that for a fully defined search policy, all elements have been
@@ -766,6 +797,25 @@
       store_->GetValue(prefs::kDefaultSearchProviderSearchTermsReplacementKey,
       &value));
   EXPECT_TRUE(base::StringValue(kReplacementKey).Equals(value));
+
+  EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, &value));
+  EXPECT_TRUE(base::StringValue(std::string(kImageURL)).Equals(value));
+
+  EXPECT_TRUE(store_->GetValue(prefs::kDefaultSearchProviderImageURLPostParams,
+                               &value));
+  EXPECT_TRUE(base::StringValue(std::string(kImageParams)).Equals(value));
+
+  EXPECT_TRUE(store_->GetValue(
+      prefs::kDefaultSearchProviderSearchURLPostParams, &value));
+  EXPECT_TRUE(base::StringValue(std::string()).Equals(value));
+
+  EXPECT_TRUE(store_->GetValue(
+      prefs::kDefaultSearchProviderSuggestURLPostParams, &value));
+  EXPECT_TRUE(base::StringValue(std::string()).Equals(value));
+
+  EXPECT_TRUE(store_->GetValue(
+      prefs::kDefaultSearchProviderInstantURLPostParams, &value));
+  EXPECT_TRUE(base::StringValue(std::string()).Equals(value));
 }
 
 // Checks that if the default search policy is missing, that no elements of the
@@ -786,6 +836,9 @@
                                 NULL));
   EXPECT_FALSE(store_->GetValue(
       prefs::kDefaultSearchProviderSearchTermsReplacementKey, NULL));
+  EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, NULL));
+  EXPECT_FALSE(store_->GetValue(
+      prefs::kDefaultSearchProviderImageURLPostParams, NULL));
 }
 
 // Checks that if the default search policy is invalid, that no elements of the
@@ -809,6 +862,9 @@
                                 NULL));
   EXPECT_FALSE(store_->GetValue(
       prefs::kDefaultSearchProviderSearchTermsReplacementKey, NULL));
+  EXPECT_FALSE(store_->GetValue(prefs::kDefaultSearchProviderImageURL, NULL));
+  EXPECT_FALSE(store_->GetValue(
+      prefs::kDefaultSearchProviderImageURLPostParams, NULL));
 }
 
 // Checks that if the default search policy is invalid, that no elements of the
diff --git a/chrome/browser/policy/mock_configuration_policy_provider.cc b/chrome/browser/policy/mock_configuration_policy_provider.cc
index 06657cd..364e9ab 100644
--- a/chrome/browser/policy/mock_configuration_policy_provider.cc
+++ b/chrome/browser/policy/mock_configuration_policy_provider.cc
@@ -7,6 +7,8 @@
 #include <string>
 
 #include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
 #include "chrome/browser/policy/policy_bundle.h"
 
 namespace policy {
@@ -21,6 +23,8 @@
   bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
       .CopyFrom(policy);
   UpdatePolicy(bundle.Pass());
+  if (base::MessageLoop::current())
+    base::RunLoop().RunUntilIdle();
 }
 
 MockConfigurationPolicyObserver::MockConfigurationPolicyObserver() {}
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 94d158c..bfb8cfe 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -50,6 +50,8 @@
 #include "chrome/browser/policy/policy_map.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/instant_service.h"
+#include "chrome/browser/search/instant_service_factory.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/search_engines/template_url.h"
 #include "chrome/browser/search_engines/template_url_service.h"
@@ -76,6 +78,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/browser_child_process_host_iterator.h"
 #include "content/public/browser/browser_context.h"
@@ -127,12 +130,7 @@
 #include "ash/shell_delegate.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
-#include "chrome/browser/chromeos/audio/audio_handler.h"
-#include "chromeos/audio/audio_pref_handler.h"
-#endif
-
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
+#include "chromeos/audio/cras_audio_handler.h"
 #endif
 
 using content::BrowserThread;
@@ -427,17 +425,27 @@
 }
 
 #if defined(OS_CHROMEOS)
-// Volume observer mock used by the audio policy tests.
-class TestVolumeObserver : public chromeos::AudioHandler::VolumeObserver {
+class TestAudioObserver : public chromeos::CrasAudioHandler::AudioObserver {
  public:
-  TestVolumeObserver() {}
-  virtual ~TestVolumeObserver() {}
+  TestAudioObserver() : output_mute_changed_count_(0) {
+  }
 
-  MOCK_METHOD0(OnVolumeChanged, void());
-  MOCK_METHOD0(OnMuteToggled, void());
+  int output_mute_changed_count() const {
+    return output_mute_changed_count_;
+  }
+
+  virtual ~TestAudioObserver() {}
+
+ protected:
+  // chromeos::CrasAudioHandler::AudioObserver overrides.
+  virtual void OnOutputMuteChanged() OVERRIDE {
+    ++output_mute_changed_count_;
+  }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(TestVolumeObserver);
+  int output_mute_changed_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestAudioObserver);
 };
 #endif
 
@@ -482,53 +490,6 @@
     UpdateProviderPolicy(policies);
   }
 
-  void TestScreenshotFeedback(bool enabled) {
-    SetScreenshotPolicy(enabled);
-
-    // Wait for feedback page to load.
-    content::WindowedNotificationObserver observer(
-        content::NOTIFICATION_LOAD_STOP,
-        content::NotificationService::AllSources());
-    EXPECT_TRUE(chrome::ExecuteCommand(browser(), IDC_FEEDBACK));
-    observer.Wait();
-    content::WebContents* web_contents =
-        static_cast<content::Source<content::NavigationController> >(
-            observer.source())->GetWebContents();
-
-    // Wait for feedback page to fully initialize.
-    // setupCurrentScreenshot is called when feedback page loads and (among
-    // other things) adds current-screenshots-thumbnailDiv-0-image element.
-    // The code below executes either before setupCurrentScreenshot was called
-    // (setupCurrentScreenshot is replaced with our hook) or after it has
-    // completed (in that case send result immediately).
-    bool result = false;
-    EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
-        web_contents,
-        "function btest_initCompleted(url) {"
-        "  var img = new Image();"
-        "  img.src = url;"
-        "  img.onload = function() {"
-        "    domAutomationController.send(img.width * img.height > 0);"
-        "  };"
-        "  img.onerror = function() {"
-        "    domAutomationController.send(false);"
-        "  };"
-        "}"
-        "function setupCurrentScreenshot(url) {"
-        "  btest_initCompleted(url);"
-        "}"
-        "var img = document.getElementById("
-        "    'current-screenshots-thumbnailDiv-0-image');"
-        "if (img)"
-        "  btest_initCompleted(img.src);",
-        &result));
-    EXPECT_EQ(enabled, result);
-
-    // Feedback page is a singleton page, so close so future calls to this
-    // function work as expected.
-    web_contents->Close();
-  }
-
 #if defined(OS_CHROMEOS)
   void TestScreenshotFile(bool enabled) {
     SetScreenshotPolicy(enabled);
@@ -662,7 +623,7 @@
 IN_PROC_BROWSER_TEST_F(PolicyTest, BookmarkBarEnabled) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -743,6 +704,9 @@
       "http://search.example/search#q={searchTerms}");
   const std::string kAlternateURL1("http://search.example/#q={searchTerms}");
   const std::string kSearchTermsReplacementKey("zekey");
+  const std::string kImageURL("http://test.com/searchbyimage/upload");
+  const std::string kImageURLPostParams(
+      "image_content=content,image_url=http://test.com/test.png");
 
   TemplateURLService* service = TemplateURLServiceFactory::GetForProfile(
       browser()->profile());
@@ -756,7 +720,9 @@
     default_search->alternate_urls()[0] == kAlternateURL0 &&
     default_search->alternate_urls()[1] == kAlternateURL1 &&
     default_search->search_terms_replacement_key() ==
-        kSearchTermsReplacementKey);
+        kSearchTermsReplacementKey &&
+    default_search->image_url() == kImageURL &&
+    default_search->image_url_post_params() == kImageURLPostParams);
 
   // Override the default search provider using policies.
   PolicyMap policies;
@@ -777,6 +743,14 @@
                POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
                base::Value::CreateStringValue(kSearchTermsReplacementKey),
                NULL);
+  policies.Set(key::kDefaultSearchProviderImageURL,
+               POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               base::Value::CreateStringValue(kImageURL),
+               NULL);
+  policies.Set(key::kDefaultSearchProviderImageURLPostParams,
+               POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               base::Value::CreateStringValue(kImageURLPostParams),
+               NULL);
   UpdateProviderPolicy(policies);
   default_search = service->GetDefaultSearchProvider();
   ASSERT_TRUE(default_search);
@@ -787,6 +761,8 @@
   EXPECT_EQ(kAlternateURL1, default_search->alternate_urls()[1]);
   EXPECT_EQ(kSearchTermsReplacementKey,
             default_search->search_terms_replacement_key());
+  EXPECT_EQ(kImageURL, default_search->image_url());
+  EXPECT_EQ(kImageURLPostParams, default_search->image_url_post_params());
 
   // Verify that searching from the omnibox uses kSearchURL.
   chrome::FocusLocationBar(browser());
@@ -929,6 +905,13 @@
   EXPECT_EQ(kAlternateURL0, default_search->alternate_urls()[0]);
   EXPECT_EQ(kAlternateURL1, default_search->alternate_urls()[1]);
 
+  // Query terms replacement requires that the renderer process be a recognized
+  // Instant renderer. Fake it.
+  InstantService* instant_service =
+      InstantServiceFactory::GetForProfile(browser()->profile());
+  instant_service->AddInstantProcess(browser()->tab_strip_model()->
+      GetActiveWebContents()->GetRenderProcessHost()->GetID());
+
   // Verify that searching from the omnibox does search term replacement with
   // first URL pattern.
   chrome::FocusLocationBar(browser());
@@ -1200,7 +1183,7 @@
 IN_PROC_BROWSER_TEST_F(PolicyTest, WebStoreIconHidden) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -1267,9 +1250,13 @@
 }
 #endif
 
-// See crbug.com/248464
+// There's a bug filed for flakiness on windows: http://crbug.com/248464.
+// Unfortunately, the bug doesn't contain any actionable information, so this
+// test is temporarily marked FLAKY to get some cycles on the builders in order
+// to gather data on the nature of the flakes.
+// TODO(mnissler): Flip back to DISABLED after obtaining logs from flaky runs.
 #if defined(OS_WIN)
-#define MAYBE_ExtensionInstallBlacklist DISABLED_ExtensionInstallBlacklist
+#define MAYBE_ExtensionInstallBlacklist FLAKY_ExtensionInstallBlacklist
 #else
 #define MAYBE_ExtensionInstallBlacklist ExtensionInstallBlacklist
 #endif
@@ -1489,7 +1476,7 @@
 IN_PROC_BROWSER_TEST_F(PolicyTest, HomepageLocation) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -1802,27 +1789,6 @@
   CheckURLIsBlocked(browser(), file_path2.c_str());
 }
 
-// Flaky on Linux. http://crbug.com/155459
-#if defined(OS_LINUX)
-#define MAYBE_DisableScreenshotsFeedback DISABLED_DisableScreenshotsFeedback
-#else
-#define MAYBE_DisableScreenshotsFeedback DisableScreenshotsFeedback
-#endif
-IN_PROC_BROWSER_TEST_F(PolicyTest, MAYBE_DisableScreenshotsFeedback) {
-#if defined(OS_WIN) && defined(USE_ASH)
-  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
-    return;
-#endif
-
-  // Make sure current screenshot can be taken and displayed on feedback page.
-  TestScreenshotFeedback(true);
-
-  // Check if banning screenshots disabled feedback page's ability to grab a
-  // screenshot.
-  TestScreenshotFeedback(false);
-}
-
 #if defined(OS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(PolicyTest, DisableScreenshotsFile) {
   int screenshot_count = CountScreenshots();
@@ -1836,44 +1802,41 @@
   ASSERT_EQ(CountScreenshots(), screenshot_count + 1);
 }
 
-// TODO(rkc,jennyz): Fix this once we remove the old Audio Handler completely.
-IN_PROC_BROWSER_TEST_F(PolicyTest, DISABLED_DisableAudioOutput) {
+IN_PROC_BROWSER_TEST_F(PolicyTest, DisableAudioOutput) {
   // Set up the mock observer.
-  chromeos::AudioHandler::Initialize(
-      chromeos::AudioPrefHandler::Create(g_browser_process->local_state()));
-  chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
-  scoped_ptr<TestVolumeObserver> mock(new TestVolumeObserver());
-  audio_handler->AddVolumeObserver(mock.get());
+  chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
+  scoped_ptr<TestAudioObserver> test_observer(new TestAudioObserver);
+  audio_handler->AddAudioObserver(test_observer.get());
 
-  bool prior_state = audio_handler->IsMuted();
-  // Make sure we are not muted and then toggle the policy and observe if the
-  // trigger was successful.
-  audio_handler->SetMuted(false);
-  EXPECT_FALSE(audio_handler->IsMuted());
-  EXPECT_CALL(*mock, OnMuteToggled()).Times(1);
+  bool prior_state = audio_handler->IsOutputMuted();
+  // Make sure the audio is not muted and then toggle the policy and observe
+  // if the output mute changed event is fired.
+  audio_handler->SetOutputMute(false);
+  EXPECT_FALSE(audio_handler->IsOutputMuted());
+  EXPECT_EQ(1, test_observer->output_mute_changed_count());
   PolicyMap policies;
   policies.Set(key::kAudioOutputAllowed, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, base::Value::CreateBooleanValue(false), NULL);
   UpdateProviderPolicy(policies);
-  EXPECT_TRUE(audio_handler->IsMuted());
-  // This should not change the state now and should not trigger OnMuteToggled.
-  audio_handler->SetMuted(false);
-  EXPECT_TRUE(audio_handler->IsMuted());
+  EXPECT_TRUE(audio_handler->IsOutputMuted());
+  // This should not change the state now and should not trigger output mute
+  // changed event.
+  audio_handler->SetOutputMute(false);
+  EXPECT_TRUE(audio_handler->IsOutputMuted());
+  EXPECT_EQ(1, test_observer->output_mute_changed_count());
 
-  // Toggle back and observe if the trigger was successful.
-  EXPECT_CALL(*mock, OnMuteToggled()).Times(1);
+  // Toggle back and observe if the output mute changed event is fired.
   policies.Set(key::kAudioOutputAllowed, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, base::Value::CreateBooleanValue(true), NULL);
   UpdateProviderPolicy(policies);
-  EXPECT_FALSE(audio_handler->IsMuted());
-  EXPECT_CALL(*mock, OnMuteToggled()).Times(1);
-  audio_handler->SetMuted(true);
-  EXPECT_TRUE(audio_handler->IsMuted());
+  EXPECT_FALSE(audio_handler->IsOutputMuted());
+  EXPECT_EQ(1, test_observer->output_mute_changed_count());
+  audio_handler->SetOutputMute(true);
+  EXPECT_TRUE(audio_handler->IsOutputMuted());
+  EXPECT_EQ(2, test_observer->output_mute_changed_count());
   // Revert the prior state.
-  EXPECT_CALL(*mock, OnMuteToggled()).Times(1);
-  audio_handler->SetMuted(prior_state);
-  audio_handler->RemoveVolumeObserver(mock.get());
-  chromeos::AudioHandler::Shutdown();
+  audio_handler->SetOutputMute(prior_state);
+  audio_handler->RemoveAudioObserver(test_observer.get());
 }
 
 IN_PROC_BROWSER_TEST_F(PolicyTest, PRE_SessionLengthLimit) {
@@ -2186,7 +2149,7 @@
 IN_PROC_BROWSER_TEST_P(RestoreOnStartupPolicyTest, RunTest) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/policy/policy_loader_win.cc b/chrome/browser/policy/policy_loader_win.cc
index e52a866..10f9231 100644
--- a/chrome/browser/policy/policy_loader_win.cc
+++ b/chrome/browser/policy/policy_loader_win.cc
@@ -25,31 +25,23 @@
 #include "base/scoped_native_library.h"
 #include "base/stl_util.h"
 #include "base/strings/string16.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/sys_byteorder.h"
-#include "base/win/registry.h"
 #include "chrome/browser/policy/policy_bundle.h"
 #include "chrome/browser/policy/policy_load_status.h"
 #include "chrome/browser/policy/policy_map.h"
 #include "chrome/browser/policy/preg_parser_win.h"
+#include "chrome/browser/policy/registry_dict_win.h"
 #include "chrome/common/json_schema/json_schema_constants.h"
 #include "policy/policy_constants.h"
 
 namespace schema = json_schema_constants;
 
-using base::win::RegKey;
-using base::win::RegistryKeyIterator;
-using base::win::RegistryValueIterator;
-
 namespace policy {
 
 namespace {
 
 const char kKeyMandatory[] = "policy";
 const char kKeyRecommended[] = "recommended";
-const char kKeyRecommendedChrome[] = "Recommended";
 const char kKeySchema[] = "schema";
 const char kKeyThirdParty[] = "3rdparty";
 
@@ -170,29 +162,6 @@
 static base::LazyInstance<WinGPOListProvider> g_win_gpo_list_provider =
     LAZY_INSTANCE_INITIALIZER;
 
-// Returns the entry with key |name| in |dictionary| (can be NULL), or NULL.
-const base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary,
-                                      const std::string& name) {
-  if (!dictionary)
-    return NULL;
-  const base::DictionaryValue* entry = NULL;
-  dictionary->GetDictionaryWithoutPathExpansion(name, &entry);
-  return entry;
-}
-
-// Tries to extract the dictionary at |key| in |dict| and returns it.
-scoped_ptr<base::DictionaryValue> RemoveDict(base::DictionaryValue* dict,
-                                             const std::string& key) {
-  base::Value* entry = NULL;
-  base::DictionaryValue* result_dict = NULL;
-  if (dict && dict->RemoveWithoutPathExpansion(key, &entry) && entry) {
-    if (!entry->GetAsDictionary(&result_dict))
-      delete entry;
-  }
-
-  return make_scoped_ptr(result_dict);
-}
-
 std::string GetSchemaTypeForValueType(base::Value::Type value_type) {
   switch (value_type) {
     case base::Value::TYPE_DICTIONARY:
@@ -213,220 +182,9 @@
   return json_schema_constants::kNull;
 }
 
-// Returns the Value type described in |schema|, or |default_type| if not found.
-base::Value::Type GetValueTypeForSchema(const base::DictionaryValue* schema,
-                                        base::Value::Type default_type) {
-  // JSON-schema types to base::Value::Type mapping.
-  static const struct {
-    // JSON schema type.
-    const char* schema_type;
-    // Correspondent value type.
-    base::Value::Type value_type;
-  } kSchemaToValueTypeMap[] = {
-    { schema::kArray,        base::Value::TYPE_LIST        },
-    { schema::kBoolean,      base::Value::TYPE_BOOLEAN     },
-    { schema::kInteger,      base::Value::TYPE_INTEGER     },
-    { schema::kNull,         base::Value::TYPE_NULL        },
-    { schema::kNumber,       base::Value::TYPE_DOUBLE      },
-    { schema::kObject,       base::Value::TYPE_DICTIONARY  },
-    { schema::kString,       base::Value::TYPE_STRING      },
-  };
-
-  if (!schema)
-    return default_type;
-  std::string type;
-  if (!schema->GetStringWithoutPathExpansion(schema::kType, &type))
-    return default_type;
-  for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) {
-    if (type == kSchemaToValueTypeMap[i].schema_type)
-      return kSchemaToValueTypeMap[i].value_type;
-  }
-  return default_type;
-}
-
-// Returns the schema for property |name| given the |schema| of an object.
-// Returns the "additionalProperties" schema if no specific schema for
-// |name| is present. Returns NULL if no schema is found.
-const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema,
-                                          const std::string& name) {
-  const base::DictionaryValue* properties =
-      GetEntry(schema, schema::kProperties);
-  const base::DictionaryValue* sub_schema = GetEntry(properties, name);
-  if (sub_schema)
-    return sub_schema;
-  // "additionalProperties" can be a boolean, but that case is ignored.
-  return GetEntry(schema, schema::kAdditionalProperties);
-}
-
-// Reads the subtree of the Windows registry at |root| into the passed |dict|.
-void ReadRegistry(HKEY hive,
-                  const string16& root,
-                  base::DictionaryValue* dict) {
-  // First, read all the values of the key.
-  for (RegistryValueIterator it(hive, root.c_str()); it.Valid(); ++it) {
-    const std::string name = UTF16ToUTF8(it.Name());
-    switch (it.Type()) {
-      case REG_SZ:
-      case REG_EXPAND_SZ:
-        dict->SetStringWithoutPathExpansion(name, UTF16ToUTF8(it.Value()));
-        continue;
-      case REG_DWORD_LITTLE_ENDIAN:
-      case REG_DWORD_BIG_ENDIAN:
-        if (it.ValueSize() == sizeof(DWORD)) {
-          DWORD dword_value = *(reinterpret_cast<const DWORD*>(it.Value()));
-          if (it.Type() == REG_DWORD_BIG_ENDIAN)
-            dword_value = base::NetToHost32(dword_value);
-          else
-            dword_value = base::ByteSwapToLE32(dword_value);
-          dict->SetIntegerWithoutPathExpansion(name, dword_value);
-          continue;
-        }
-      case REG_NONE:
-      case REG_LINK:
-      case REG_MULTI_SZ:
-      case REG_RESOURCE_LIST:
-      case REG_FULL_RESOURCE_DESCRIPTOR:
-      case REG_RESOURCE_REQUIREMENTS_LIST:
-      case REG_QWORD_LITTLE_ENDIAN:
-        // Unsupported type, message gets logged below.
-        break;
-    }
-
-    LOG(WARNING) << "Failed to read hive " << hive << " at "
-                 << root << "\\" << name
-                 << " type " << it.Type();
-  }
-
-  // Recurse for all subkeys.
-  for (RegistryKeyIterator it(hive, root.c_str()); it.Valid(); ++it) {
-    std::string name(UTF16ToUTF8(it.Name()));
-    if (dict->HasKey(name)) {
-      DLOG(WARNING) << "Ignoring registry key because a value exists with the "
-                       "same name: " << root << "\\" << name;
-    } else {
-      scoped_ptr<base::DictionaryValue> subdict(new base::DictionaryValue());
-      ReadRegistry(hive, root + L"\\" + it.Name(), subdict.get());
-      dict->SetWithoutPathExpansion(name, subdict.release());
-    }
-  }
-}
-
-// Converts |value| in raw GPO representation to the internal policy value, as
-// described by |schema|. This maps the ambiguous GPO data types to the
-// internal policy value representations.
-scoped_ptr<base::Value> ConvertPolicyValue(
-    const base::Value& value,
-    const base::DictionaryValue* schema) {
-  // Figure out the type to convert to from the schema.
-  const base::Value::Type result_type(
-      GetValueTypeForSchema(schema, value.GetType()));
-
-  // If the type is good already, go with it.
-  if (value.IsType(result_type)) {
-    // Recurse for complex types if there is a schema.
-    if (schema) {
-      const base::DictionaryValue* dict = NULL;
-      const base::ListValue* list = NULL;
-      if (value.GetAsDictionary(&dict)) {
-        scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
-        for (base::DictionaryValue::Iterator entry(*dict); !entry.IsAtEnd();
-             entry.Advance()) {
-          scoped_ptr<base::Value> converted_value(
-              ConvertPolicyValue(entry.value(),
-                                 GetSchemaFor(schema, entry.key())));
-          result->SetWithoutPathExpansion(entry.key(),
-                                          converted_value.release());
-        }
-        return result.Pass();
-      } else if (value.GetAsList(&list)) {
-        scoped_ptr<base::ListValue> result(new base::ListValue());
-        const base::DictionaryValue* item_schema =
-            GetEntry(schema, schema::kItems);
-        for (base::ListValue::const_iterator entry(list->begin());
-             entry != list->end(); ++entry) {
-          result->Append(ConvertPolicyValue(**entry, item_schema).release());
-        }
-        return result.Pass();
-      }
-    }
-    return make_scoped_ptr(value.DeepCopy());
-  }
-
-  // Else, do some conversions to map windows registry data types to JSON types.
-  std::string string_value;
-  int int_value = 0;
-  switch (result_type) {
-    case base::Value::TYPE_NULL: {
-      return make_scoped_ptr(base::Value::CreateNullValue());
-    }
-    case base::Value::TYPE_BOOLEAN: {
-      // Accept booleans encoded as either string or integer.
-      if (value.GetAsInteger(&int_value) ||
-          (value.GetAsString(&string_value) &&
-           base::StringToInt(string_value, &int_value))) {
-        return make_scoped_ptr(Value::CreateBooleanValue(int_value != 0));
-      }
-      break;
-    }
-    case base::Value::TYPE_INTEGER: {
-      // Integers may be string-encoded.
-      if (value.GetAsString(&string_value) &&
-          base::StringToInt(string_value, &int_value)) {
-        return make_scoped_ptr(base::Value::CreateIntegerValue(int_value));
-      }
-      break;
-    }
-    case base::Value::TYPE_DOUBLE: {
-      // Doubles may be string-encoded or integer-encoded.
-      double double_value = 0;
-      if (value.GetAsInteger(&int_value)) {
-        return make_scoped_ptr(base::Value::CreateDoubleValue(int_value));
-      } else if (value.GetAsString(&string_value) &&
-                 base::StringToDouble(string_value, &double_value)) {
-        return make_scoped_ptr(base::Value::CreateDoubleValue(double_value));
-      }
-      break;
-    }
-    case base::Value::TYPE_LIST: {
-      // Lists are encoded as subkeys with numbered value in the registry.
-      const base::DictionaryValue* dict = NULL;
-      if (value.GetAsDictionary(&dict)) {
-        scoped_ptr<base::ListValue> result(new base::ListValue());
-        const base::DictionaryValue* item_schema =
-            GetEntry(schema, schema::kItems);
-        for (int i = 1; ; ++i) {
-          const base::Value* entry = NULL;
-          if (!dict->Get(base::IntToString(i), &entry))
-            break;
-          result->Append(ConvertPolicyValue(*entry, item_schema).release());
-        }
-        return result.Pass();
-      }
-      // Fall through in order to accept lists encoded as JSON strings.
-    }
-    case base::Value::TYPE_DICTIONARY: {
-      // Dictionaries may be encoded as JSON strings.
-      if (value.GetAsString(&string_value)) {
-        scoped_ptr<base::Value> result(base::JSONReader::Read(string_value));
-        if (result && result->IsType(result_type))
-          return result.Pass();
-      }
-      break;
-    }
-    case base::Value::TYPE_STRING:
-    case base::Value::TYPE_BINARY:
-      // No conversion possible.
-      break;
-  }
-
-  LOG(WARNING) << "Failed to convert " << value.GetType()
-               << " to " << result_type;
-  return make_scoped_ptr(base::Value::CreateNullValue());
-}
-
 // Parses |gpo_dict| according to |schema| and writes the resulting policy
 // settings to |policy| for the given |scope| and |level|.
-void ParsePolicy(const base::DictionaryValue* gpo_dict,
+void ParsePolicy(const RegistryDict* gpo_dict,
                  PolicyLevel level,
                  PolicyScope scope,
                  const base::DictionaryValue* schema,
@@ -434,7 +192,7 @@
   if (!gpo_dict)
     return;
 
-  scoped_ptr<base::Value> policy_value(ConvertPolicyValue(*gpo_dict, schema));
+  scoped_ptr<base::Value> policy_value(gpo_dict->ConvertToJSON(schema));
   const base::DictionaryValue* policy_dict = NULL;
   if (!policy_value->GetAsDictionary(&policy_dict) || !policy_dict) {
     LOG(WARNING) << "Root policy object is not a dictionary!";
@@ -513,28 +271,37 @@
   for (size_t i = 0; i < arraysize(kScopes); ++i) {
     PolicyScope scope = kScopes[i].scope;
     PolicyLoadStatusSample status;
-    base::DictionaryValue gpo_dict;
+    RegistryDict gpo_dict;
 
-    HANDLE policy_lock =
-        EnterCriticalPolicySection(scope == POLICY_SCOPE_MACHINE);
-    if (policy_lock == NULL)
-      PLOG(ERROR) << "EnterCriticalPolicySection";
+    // Note: GPO rules mandate a call to EnterCriticalPolicySection() here, and
+    // a matching LeaveCriticalPolicySection() call below after the
+    // ReadPolicyFromGPO() block. Unfortunately, the policy mutex may be
+    // unavailable for extended periods of time, and there are reports of this
+    // happening in the wild: http://crbug.com/265862.
+    //
+    // Blocking for minutes is neither acceptable for Chrome startup, nor on
+    // the FILE thread on which this code runs in steady state. Given that
+    // there have never been any reports of issues due to partially-applied /
+    // corrupt group policy, this code intentionally omits the
+    // EnterCriticalPolicySection() call.
+    //
+    // If there's ever reason to revisit this decision, one option could be to
+    // make the EnterCriticalPolicySection() call on a dedicated thread and
+    // timeout on it more aggressively. For now, there's no justification for
+    // the additional effort this would introduce.
 
     if (!ReadPolicyFromGPO(scope, &gpo_dict, &status)) {
       VLOG(1) << "Failed to read GPO files for " << scope
               << " falling back to registry.";
-      ReadRegistry(kScopes[i].hive, chrome_policy_key_, &gpo_dict);
+      gpo_dict.ReadRegistry(kScopes[i].hive, chrome_policy_key_);
     }
 
-    if (!LeaveCriticalPolicySection(policy_lock))
-      PLOG(ERROR) << "LeaveCriticalPolicySection";
-
     // Remove special-cased entries from the GPO dictionary.
     base::DictionaryValue* temp_dict = NULL;
-    scoped_ptr<base::DictionaryValue> recommended_dict(
-        RemoveDict(&gpo_dict, kKeyRecommendedChrome));
-    scoped_ptr<base::DictionaryValue> third_party_dict(
-        RemoveDict(&gpo_dict, kKeyThirdParty));
+    scoped_ptr<RegistryDict> recommended_dict(
+        gpo_dict.RemoveKey(kKeyRecommended));
+    scoped_ptr<RegistryDict> third_party_dict(
+        gpo_dict.RemoveKey(kKeyThirdParty));
 
     // Load Chrome policy.
     LoadChromePolicy(&gpo_dict, POLICY_LEVEL_MANDATORY, scope, chrome_policy);
@@ -575,7 +342,7 @@
 }
 
 bool PolicyLoaderWin::ReadPRegFile(const base::FilePath& preg_file,
-                                   base::DictionaryValue* policy,
+                                   RegistryDict* policy,
                                    PolicyLoadStatusSample* status) {
   // The following deals with the minor annoyance that Wow64 FS redirection
   // might need to be turned off: This is the case if running as a 32-bit
@@ -603,10 +370,10 @@
 
 bool PolicyLoaderWin::LoadGPOPolicy(PolicyScope scope,
                                     PGROUP_POLICY_OBJECT policy_object_list,
-                                    base::DictionaryValue* policy,
+                                    RegistryDict* policy,
                                     PolicyLoadStatusSample* status) {
-  base::DictionaryValue parsed_policy;
-  base::DictionaryValue forced_policy;
+  RegistryDict parsed_policy;
+  RegistryDict forced_policy;
   for (GROUP_POLICY_OBJECT* policy_object = policy_object_list;
        policy_object; policy_object = policy_object->pNext) {
     if (policy_object->dwOptions & GPO_FLAG_DISABLE)
@@ -623,13 +390,13 @@
     base::FilePath preg_file_path(
         base::FilePath(policy_object->lpFileSysPath).Append(kPRegFileName));
     if (policy_object->dwOptions & GPO_FLAG_FORCE) {
-      base::DictionaryValue new_forced_policy;
+      RegistryDict new_forced_policy;
       if (!ReadPRegFile(preg_file_path, &new_forced_policy, status))
         return false;
 
       // Merge with existing forced policy, giving precedence to the existing
       // forced policy.
-      new_forced_policy.MergeDictionary(&forced_policy);
+      new_forced_policy.Merge(forced_policy);
       forced_policy.Swap(&new_forced_policy);
     } else {
       if (!ReadPRegFile(preg_file_path, &parsed_policy, status))
@@ -638,15 +405,14 @@
   }
 
   // Merge, give precedence to forced policy.
-  parsed_policy.MergeDictionary(&forced_policy);
+  parsed_policy.Merge(forced_policy);
   policy->Swap(&parsed_policy);
 
   return true;
 }
 
-
 bool PolicyLoaderWin::ReadPolicyFromGPO(PolicyScope scope,
-                                        base::DictionaryValue* policy,
+                                        RegistryDict* policy,
                                         PolicyLoadStatusSample* status) {
   PGROUP_POLICY_OBJECT policy_object_list = NULL;
   DWORD flags = scope == POLICY_SCOPE_MACHINE ? GPO_LIST_FLAG_MACHINE : 0;
@@ -670,7 +436,7 @@
   return result;
 }
 
-void PolicyLoaderWin::LoadChromePolicy(const base::DictionaryValue* gpo_dict,
+void PolicyLoaderWin::LoadChromePolicy(const RegistryDict* gpo_dict,
                                        PolicyLevel level,
                                        PolicyScope scope,
                                        PolicyMap* chrome_policy_map) {
@@ -679,19 +445,15 @@
   chrome_policy_map->MergeFrom(policy);
 }
 
-void PolicyLoaderWin::Load3rdPartyPolicy(
-    const DictionaryValue* gpo_dict,
-    PolicyScope scope,
-    PolicyBundle* bundle) {
+void PolicyLoaderWin::Load3rdPartyPolicy(const RegistryDict* gpo_dict,
+                                         PolicyScope scope,
+                                         PolicyBundle* bundle) {
   // Map of known 3rd party policy domain name to their enum values.
   static const struct {
     const char* name;
     PolicyDomain domain;
   } k3rdPartyDomains[] = {
     { "extensions", POLICY_DOMAIN_EXTENSIONS },
-    // Support a common misspelling. The correct spelling is first, so it takes
-    // precedence in case of collisions.
-    { "Extensions", POLICY_DOMAIN_EXTENSIONS },
   };
 
   // Policy level and corresponding path.
@@ -706,45 +468,37 @@
   for (size_t i = 0; i < arraysize(k3rdPartyDomains); i++) {
     const char* name = k3rdPartyDomains[i].name;
     const PolicyDomain domain = k3rdPartyDomains[i].domain;
-    const base::DictionaryValue* domain_dict = NULL;
-    if (!gpo_dict->GetDictionaryWithoutPathExpansion(name, &domain_dict) ||
-        !domain_dict) {
+    const RegistryDict* domain_dict = gpo_dict->GetKey(name);
+    if (!domain_dict)
       continue;
-    }
 
-    for (base::DictionaryValue::Iterator component(*domain_dict);
-         !component.IsAtEnd(); component.Advance()) {
-      const base::DictionaryValue* component_dict = NULL;
-      if (!component.value().GetAsDictionary(&component_dict) ||
-          !component_dict) {
-        continue;
-      }
-
+    for (RegistryDict::KeyMap::const_iterator component(
+             domain_dict->keys().begin());
+         component != domain_dict->keys().end();
+         ++component) {
       // Load the schema.
-      scoped_ptr<base::Value> schema;
       const base::DictionaryValue* schema_dict = NULL;
+      scoped_ptr<base::Value> schema;
       std::string schema_json;
-      if (component_dict->GetStringWithoutPathExpansion(kKeySchema,
-                                                        &schema_json)) {
+      const base::Value* schema_value = component->second->GetValue(kKeySchema);
+      if (schema_value && schema_value->GetAsString(&schema_json)) {
         schema.reset(base::JSONReader::Read(schema_json));
         if (!schema || !schema->GetAsDictionary(&schema_dict)) {
           LOG(WARNING) << "Failed to parse 3rd-part policy schema for "
-                       << domain << "/" << component.key();
+                       << domain << "/" << component->first;
         }
       }
 
       // Parse policy.
       for (size_t j = 0; j < arraysize(kLevels); j++) {
-        const base::DictionaryValue* policy_dict = NULL;
-        if (!component_dict->GetDictionaryWithoutPathExpansion(
-                kLevels[j].path, &policy_dict) ||
-            !policy_dict) {
+        const RegistryDict* policy_dict =
+            component->second->GetKey(kLevels[j].path);
+        if (!policy_dict)
           continue;
-        }
 
         PolicyMap policy;
         ParsePolicy(policy_dict, kLevels[j].level, scope, schema_dict, &policy);
-        PolicyNamespace policy_namespace(domain, component.key());
+        PolicyNamespace policy_namespace(domain, component->first);
         bundle->Get(policy_namespace).MergeFrom(policy);
       }
     }
diff --git a/chrome/browser/policy/policy_loader_win.h b/chrome/browser/policy/policy_loader_win.h
index e9c0658..baf25ef 100644
--- a/chrome/browser/policy/policy_loader_win.h
+++ b/chrome/browser/policy/policy_loader_win.h
@@ -22,6 +22,7 @@
 class AppliedGPOListProvider;
 class PolicyLoadStatusSample;
 class PolicyMap;
+class RegistryDict;
 struct PolicyDefinitionList;
 
 // Interface for mocking out GPO enumeration in tests.
@@ -64,7 +65,7 @@
   // Reads Chrome Policy from a PReg file at the given path and stores the
   // result in |policy|.
   bool ReadPRegFile(const base::FilePath& preg_file,
-                    base::DictionaryValue* policy,
+                    RegistryDict* policy,
                     PolicyLoadStatusSample *status);
 
   // Loads and parses GPO policy in |policy_object_list| for scope |scope|. If
@@ -73,25 +74,25 @@
   // back to reading the registry.
   bool LoadGPOPolicy(PolicyScope scope,
                      PGROUP_POLICY_OBJECT policy_object_list,
-                     base::DictionaryValue* policy,
+                     RegistryDict* policy,
                      PolicyLoadStatusSample *status);
 
   // Queries Windows for applied group policy and writes the result to |policy|.
   // This is the preferred way to obtain GPO data, there are reports of abuse
   // of the registry GPO keys by 3rd-party software.
   bool ReadPolicyFromGPO(PolicyScope scope,
-                         base::DictionaryValue* policy,
+                         RegistryDict* policy,
                          PolicyLoadStatusSample *status);
 
   // Parses Chrome policy from |gpo_dict| for the given |scope| and |level| and
   // merges it into |chrome_policy_map|.
-  void LoadChromePolicy(const base::DictionaryValue* gpo_dict,
+  void LoadChromePolicy(const RegistryDict* gpo_dict,
                         PolicyLevel level,
                         PolicyScope scope,
                         PolicyMap* chrome_policy_map);
 
   // Loads 3rd-party policy from |gpo_dict| and merges it into |bundle|.
-  void Load3rdPartyPolicy(const base::DictionaryValue* gpo_dict,
+  void Load3rdPartyPolicy(const RegistryDict* gpo_dict,
                           PolicyScope scope,
                           PolicyBundle* bundle);
 
diff --git a/chrome/browser/policy/policy_loader_win_unittest.cc b/chrome/browser/policy/policy_loader_win_unittest.cc
index 3417d9f..a2aff0e 100644
--- a/chrome/browser/policy/policy_loader_win_unittest.cc
+++ b/chrome/browser/policy/policy_loader_win_unittest.cc
@@ -1182,7 +1182,7 @@
 
   PolicyBundle expected;
   base::DictionaryValue expected_a;
-  expected_a.SetInteger("policy 1", 1);
+  expected_a.SetInteger("policy 1", 3);
   expected_a.SetInteger("policy 2", 3);
   expected.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
                                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
diff --git a/chrome/browser/policy/policy_service_impl.cc b/chrome/browser/policy/policy_service_impl.cc
index 5a25400..6af7605 100644
--- a/chrome/browser/policy/policy_service_impl.cc
+++ b/chrome/browser/policy/policy_service_impl.cc
@@ -7,7 +7,6 @@
 #include <algorithm>
 
 #include "base/bind.h"
-#include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/stl_util.h"
 #include "chrome/browser/policy/policy_domain_descriptor.h"
@@ -15,22 +14,10 @@
 
 namespace policy {
 
-PolicyServiceImpl::PolicyChangeInfo::PolicyChangeInfo(
-    const PolicyNamespace& policy_namespace,
-    const PolicyMap& previous,
-    const PolicyMap& current)
-    : policy_namespace_(policy_namespace) {
-  previous_.CopyFrom(previous);
-  current_.CopyFrom(current);
-}
-
-PolicyServiceImpl::PolicyChangeInfo::~PolicyChangeInfo() {
-}
-
 typedef PolicyServiceImpl::Providers::const_iterator Iterator;
 
 PolicyServiceImpl::PolicyServiceImpl(const Providers& providers)
-    : weak_ptr_factory_(this) {
+    : update_task_ptr_factory_(this) {
   for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain)
     initialization_complete_[domain] = true;
   providers_ = providers;
@@ -102,8 +89,13 @@
     refresh_callbacks_.push_back(callback);
 
   if (providers_.empty()) {
-    // Refresh is immediately complete if there are no providers.
-    MergeAndTriggerUpdates();
+    // Refresh is immediately complete if there are no providers. See the note
+    // on OnUpdatePolicy() about why this is a posted task.
+    update_task_ptr_factory_.InvalidateWeakPtrs();
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates,
+                   update_task_ptr_factory_.GetWeakPtr()));
   } else {
     // Some providers might invoke OnUpdatePolicy synchronously while handling
     // RefreshPolicies. Mark all as pending before refreshing.
@@ -117,45 +109,31 @@
 void PolicyServiceImpl::OnUpdatePolicy(ConfigurationPolicyProvider* provider) {
   DCHECK_EQ(1, std::count(providers_.begin(), providers_.end(), provider));
   refresh_pending_.erase(provider);
-  MergeAndTriggerUpdates();
+
+  // Note: a policy change may trigger further policy changes in some providers.
+  // For example, disabling SigninAllowed would cause the CloudPolicyManager to
+  // drop all its policies, which makes this method enter again for that
+  // provider.
+  //
+  // Therefore this update is posted asynchronously, to prevent reentrancy in
+  // MergeAndTriggerUpdates. Also, cancel a pending update if there is any,
+  // since both will produce the same PolicyBundle.
+  update_task_ptr_factory_.InvalidateWeakPtrs();
+  base::MessageLoop::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates,
+                 update_task_ptr_factory_.GetWeakPtr()));
 }
 
 void PolicyServiceImpl::NotifyNamespaceUpdated(
     const PolicyNamespace& ns,
     const PolicyMap& previous,
     const PolicyMap& current) {
-  // If running a unit test that hasn't setup a MessageLoop, don't send any
-  // notifications.
-  if (!base::MessageLoop::current())
-    return;
-
-  // Don't queue up a task if we have no observers - that way Observers added
-  // later don't get notified of changes that happened during construction time.
-  if (observers_.find(ns.domain) == observers_.end())
-    return;
-
-  // Notify Observers via a queued task, so Observers can't trigger a re-entrant
-  // call to MergeAndTriggerUpdates() by modifying policy.
-  scoped_ptr<PolicyChangeInfo> changes(
-      new PolicyChangeInfo(ns, previous, current));
-  base::MessageLoop::current()->PostTask(
-      FROM_HERE,
-      base::Bind(&PolicyServiceImpl::NotifyNamespaceUpdatedTask,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 base::Passed(&changes)));
-}
-
-void PolicyServiceImpl::NotifyNamespaceUpdatedTask(
-    scoped_ptr<PolicyChangeInfo> changes) {
-  ObserverMap::iterator iterator = observers_.find(
-      changes->policy_namespace_.domain);
+  ObserverMap::iterator iterator = observers_.find(ns.domain);
   if (iterator != observers_.end()) {
-    FOR_EACH_OBSERVER(
-        PolicyService::Observer,
-        *iterator->second,
-        OnPolicyUpdated(changes->policy_namespace_,
-                        changes->previous_,
-                        changes->current_));
+    FOR_EACH_OBSERVER(PolicyService::Observer,
+                      *iterator->second,
+                      OnPolicyUpdated(ns, previous, current));
   }
 }
 
diff --git a/chrome/browser/policy/policy_service_impl.h b/chrome/browser/policy/policy_service_impl.h
index 642d979..51716f2 100644
--- a/chrome/browser/policy/policy_service_impl.h
+++ b/chrome/browser/policy/policy_service_impl.h
@@ -51,19 +51,6 @@
   typedef ObserverList<PolicyService::Observer, true> Observers;
   typedef std::map<PolicyDomain, Observers*> ObserverMap;
 
-  // Information about policy changes sent to observers.
-  class PolicyChangeInfo {
-   public:
-    PolicyChangeInfo(const PolicyNamespace& policy_namespace,
-                     const PolicyMap& previous,
-                     const PolicyMap& current);
-    ~PolicyChangeInfo();
-
-    PolicyNamespace policy_namespace_;
-    PolicyMap previous_;
-    PolicyMap current_;
-  };
-
   // ConfigurationPolicyProvider::Observer overrides:
   virtual void OnUpdatePolicy(ConfigurationPolicyProvider* provider) OVERRIDE;
 
@@ -73,11 +60,6 @@
                               const PolicyMap& previous,
                               const PolicyMap& current);
 
-  // Helper function invoked by NotifyNamespaceUpdated() to notify observers
-  // via a queued task, to deal with reentrancy issues caused by observers
-  // generating policy changes.
-  void NotifyNamespaceUpdatedTask(scoped_ptr<PolicyChangeInfo> info);
-
   // Combines the policies from all the providers, and notifies the observers
   // of namespaces whose policies have been modified.
   void MergeAndTriggerUpdates();
@@ -115,7 +97,7 @@
 
   // Used to create tasks to delay new policy updates while we may be already
   // processing previous policy updates.
-  base::WeakPtrFactory<PolicyServiceImpl> weak_ptr_factory_;
+  base::WeakPtrFactory<PolicyServiceImpl> update_task_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PolicyServiceImpl);
 };
diff --git a/chrome/browser/policy/policy_service_impl_unittest.cc b/chrome/browser/policy/policy_service_impl_unittest.cc
index 508ae44..364e3c4 100644
--- a/chrome/browser/policy/policy_service_impl_unittest.cc
+++ b/chrome/browser/policy/policy_service_impl_unittest.cc
@@ -136,11 +136,6 @@
     return policy_service_->GetPolicies(ns).Equals(expected);
   }
 
-  void UpdateProviderPolicy(const PolicyMap& policy) {
-    provider0_.UpdateChromePolicy(policy);
-    RunUntilIdle();
-  }
-
   void RunUntilIdle() {
     base::RunLoop loop;
     loop.RunUntilIdle();
@@ -186,12 +181,12 @@
                                                         std::string()),
                                         PolicyEquals(&expectedPrevious),
                                         PolicyEquals(&expectedCurrent)));
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(&observer);
 
   // No changes.
   EXPECT_CALL(observer, OnPolicyUpdated(_, _, _)).Times(0);
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(&observer);
   EXPECT_TRUE(VerifyPolicies(
       PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()), expectedCurrent));
@@ -206,7 +201,7 @@
                                                         std::string()),
                                         PolicyEquals(&expectedPrevious),
                                         PolicyEquals(&expectedCurrent)));
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(&observer);
 
   // Removed policy.
@@ -217,7 +212,7 @@
                                                         std::string()),
                                         PolicyEquals(&expectedPrevious),
                                         PolicyEquals(&expectedCurrent)));
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(&observer);
 
   // Changed policy.
@@ -231,12 +226,12 @@
                                                         std::string()),
                                         PolicyEquals(&expectedPrevious),
                                         PolicyEquals(&expectedCurrent)));
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(&observer);
 
   // No changes again.
   EXPECT_CALL(observer, OnPolicyUpdated(_, _, _)).Times(0);
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(&observer);
   EXPECT_TRUE(VerifyPolicies(
       PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()), expectedCurrent));
@@ -338,7 +333,7 @@
   policy0_.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
                base::Value::CreateIntegerValue(1234), NULL);
   // Should not crash.
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, &observer);
   EXPECT_TRUE(observer.observer_invoked());
 }
@@ -398,14 +393,14 @@
   EXPECT_CALL(*this, OnPolicyValueUpdated(NULL, ValueEquals(&kValue0)));
   policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
                kValue0.DeepCopy(), NULL);
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(this);
 
   // Changing other values doesn't trigger a notification.
   EXPECT_CALL(*this, OnPolicyValueUpdated(_, _)).Times(0);
   policy0_.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
                kValue0.DeepCopy(), NULL);
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(this);
 
   // Modifying the value triggers a notification.
@@ -414,13 +409,13 @@
                                           ValueEquals(&kValue1)));
   policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
                kValue1.DeepCopy(), NULL);
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(this);
 
   // Removing the value triggers a notification.
   EXPECT_CALL(*this, OnPolicyValueUpdated(ValueEquals(&kValue1), NULL));
   policy0_.Erase("aaa");
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(this);
 
   // No more notifications after destroying the registrar.
@@ -430,7 +425,7 @@
                kValue1.DeepCopy(), NULL);
   policy0_.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
                kValue1.DeepCopy(), NULL);
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(this);
 }
 
@@ -451,7 +446,7 @@
   base::FundamentalValue kValue0(0);
   policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
                kValue0.DeepCopy(), NULL);
-  UpdateProviderPolicy(policy0_);
+  provider0_.UpdateChromePolicy(policy0_);
   Mock::VerifyAndClearExpectations(this);
 
   EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
@@ -459,7 +454,6 @@
   policy1_.Set("aaa", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
                kValue1.DeepCopy(), NULL);
   provider1_.UpdateChromePolicy(policy1_);
-  RunUntilIdle();
   Mock::VerifyAndClearExpectations(this);
 
   // A provider can refresh more than once after a RefreshPolicies call, but
@@ -469,7 +463,6 @@
   policy1_.Set("bbb", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
                kValue1.DeepCopy(), NULL);
   provider1_.UpdateChromePolicy(policy1_);
-  RunUntilIdle();
   Mock::VerifyAndClearExpectations(this);
 
   // If another RefreshPolicies() call happens while waiting for a previous
@@ -485,7 +478,6 @@
   policy2_.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
                kValue0.DeepCopy(), NULL);
   provider2_.UpdateChromePolicy(policy2_);
-  RunUntilIdle();
   Mock::VerifyAndClearExpectations(this);
 
   // Providers 0 and 1 must reload again.
@@ -495,7 +487,6 @@
                kValue2.DeepCopy(), NULL);
   provider0_.UpdateChromePolicy(policy0_);
   provider1_.UpdateChromePolicy(policy1_);
-  RunUntilIdle();
   Mock::VerifyAndClearExpectations(this);
 
   const PolicyMap& policies = policy_service_->GetPolicies(
@@ -519,6 +510,7 @@
   provider0_.UpdatePolicy(bundle0.Pass());
   provider1_.UpdatePolicy(bundle1.Pass());
   provider2_.UpdatePolicy(bundle2.Pass());
+  RunUntilIdle();
 
   PolicyMap expected;
   // For policies of the same level and scope, the first provider takes
@@ -565,7 +557,6 @@
       .WillRepeatedly(Return(false));
   const PolicyMap kPolicyMap;
   provider1_.UpdateChromePolicy(kPolicyMap);
-  RunUntilIdle();
   Mock::VerifyAndClearExpectations(&observer);
   EXPECT_FALSE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
   EXPECT_FALSE(
@@ -579,7 +570,6 @@
   EXPECT_CALL(provider2_, IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS))
       .WillRepeatedly(Return(true));
   provider2_.UpdateChromePolicy(kPolicyMap);
-  RunUntilIdle();
   Mock::VerifyAndClearExpectations(&observer);
   EXPECT_FALSE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
   EXPECT_FALSE(
@@ -593,7 +583,6 @@
   EXPECT_CALL(provider2_, IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS))
       .WillRepeatedly(Return(true));
   provider2_.UpdateChromePolicy(kPolicyMap);
-  RunUntilIdle();
   Mock::VerifyAndClearExpectations(&observer);
   EXPECT_TRUE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
   // Other domains are still not initialized.
@@ -608,7 +597,6 @@
   EXPECT_CALL(provider1_, IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS))
       .WillRepeatedly(Return(true));
   provider1_.UpdateChromePolicy(kPolicyMap);
-  RunUntilIdle();
   Mock::VerifyAndClearExpectations(&observer);
   EXPECT_TRUE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
   EXPECT_TRUE(
diff --git a/chrome/browser/policy/preg_parser_win.cc b/chrome/browser/policy/preg_parser_win.cc
index f1755c7..5f16233 100644
--- a/chrome/browser/policy/preg_parser_win.cc
+++ b/chrome/browser/policy/preg_parser_win.cc
@@ -21,6 +21,7 @@
 #include "base/sys_byteorder.h"
 #include "base/values.h"
 #include "chrome/browser/policy/policy_load_status.h"
+#include "chrome/browser/policy/registry_dict_win.h"
 
 namespace policy {
 namespace preg_parser {
@@ -147,7 +148,7 @@
                   const string16& value,
                   uint32 type,
                   const std::vector<uint8>& data,
-                  base::DictionaryValue* dict) {
+                  RegistryDict* dict) {
   // Locate/create the dictionary to place the value in.
   std::vector<string16> path;
 
@@ -156,12 +157,11 @@
        entry != path.end(); ++entry) {
     if (entry->empty())
       continue;
-    base::DictionaryValue* subdict = NULL;
     const std::string name = UTF16ToUTF8(*entry);
-    if (!dict->GetDictionaryWithoutPathExpansion(name, &subdict) ||
-        !subdict) {
-      subdict = new DictionaryValue();
-      dict->SetWithoutPathExpansion(name, subdict);
+    RegistryDict* subdict = dict->GetKey(name);
+    if (!subdict) {
+      subdict = new RegistryDict();
+      dict->SetKey(name, make_scoped_ptr(subdict));
     }
     dict = subdict;
   }
@@ -173,39 +173,33 @@
   if (!StartsWithASCII(value_name, kActionTriggerPrefix, true)) {
     scoped_ptr<base::Value> value;
     if (DecodePRegValue(type, data, &value))
-      dict->Set(value_name, value.release());
+      dict->SetValue(value_name, value.Pass());
     return;
   }
 
   std::string action_trigger(StringToLowerASCII(value_name.substr(
       arraysize(kActionTriggerPrefix) - 1)));
-  if (action_trigger == kActionTriggerDeleteValues ||
-      StartsWithASCII(action_trigger, kActionTriggerDeleteKeys, true)) {
+  if (action_trigger == kActionTriggerDeleteValues) {
+    std::vector<std::string> values;
+    Tokenize(DecodePRegStringValue(data), ";", &values);
+    for (std::vector<std::string>::const_iterator value(values.begin());
+         value != values.end(); ++value) {
+      dict->RemoveValue(*value);
+    }
+  } else if (StartsWithASCII(action_trigger, kActionTriggerDeleteKeys, true)) {
     std::vector<std::string> keys;
     Tokenize(DecodePRegStringValue(data), ";", &keys);
     for (std::vector<std::string>::const_iterator key(keys.begin());
          key != keys.end(); ++key) {
-      dict->RemoveWithoutPathExpansion(*key, NULL);
+      dict->RemoveKey(*key);
     }
   } else if (StartsWithASCII(action_trigger, kActionTriggerDel, true)) {
-    dict->RemoveWithoutPathExpansion(
+    dict->RemoveValue(
         value_name.substr(arraysize(kActionTriggerPrefix) - 1 +
-                          arraysize(kActionTriggerDel) - 1),
-        NULL);
+                          arraysize(kActionTriggerDel) - 1));
   } else if (StartsWithASCII(action_trigger, kActionTriggerDelVals, true)) {
-    // Delete all values, but keep keys (i.e. retain dictionary entries).
-    base::DictionaryValue new_dict;
-    for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd();
-         it.Advance()) {
-      base::DictionaryValue* subdict = NULL;
-      if (dict->GetDictionaryWithoutPathExpansion(it.key(), &subdict)) {
-        scoped_ptr<base::DictionaryValue> new_subdict(
-            new base::DictionaryValue());
-        new_subdict->Swap(subdict);
-        new_dict.Set(it.key(), new_subdict.release());
-      }
-    }
-    dict->Swap(&new_dict);
+    // Delete all values.
+    dict->ClearValues();
   } else if (StartsWithASCII(action_trigger, kActionTriggerSecureKey, true) ||
              StartsWithASCII(action_trigger, kActionTriggerSoft, true)) {
     // Doesn't affect values.
@@ -216,7 +210,7 @@
 
 bool ReadFile(const base::FilePath& file_path,
               const string16& root,
-              base::DictionaryValue* dict,
+              RegistryDict* dict,
               PolicyLoadStatusSample* status) {
   base::MemoryMappedFile mapped_file;
   if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) {
diff --git a/chrome/browser/policy/preg_parser_win.h b/chrome/browser/policy/preg_parser_win.h
index 2a5c28e..dbd54c7 100644
--- a/chrome/browser/policy/preg_parser_win.h
+++ b/chrome/browser/policy/preg_parser_win.h
@@ -16,13 +16,13 @@
 #include "base/strings/string16.h"
 
 namespace base {
-class DictionaryValue;
 class FilePath;
 }
 
 namespace policy {
 
 class PolicyLoadStatusSample;
+class RegistryDict;
 
 namespace preg_parser {
 
@@ -34,7 +34,7 @@
 // everything else gets ignored.
 bool ReadFile(const base::FilePath& file_path,
               const string16& root,
-              base::DictionaryValue* dict,
+              RegistryDict* dict,
               PolicyLoadStatusSample* status);
 
 }  // namespace preg_parser
diff --git a/chrome/browser/policy/preg_parser_win_unittest.cc b/chrome/browser/policy/preg_parser_win_unittest.cc
index efe32c6..0c39e06 100644
--- a/chrome/browser/policy/preg_parser_win_unittest.cc
+++ b/chrome/browser/policy/preg_parser_win_unittest.cc
@@ -10,11 +10,62 @@
 #include "base/path_service.h"
 #include "base/values.h"
 #include "chrome/browser/policy/policy_load_status.h"
+#include "chrome/browser/policy/registry_dict_win.h"
 #include "chrome/common/chrome_paths.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace policy {
 namespace preg_parser {
+namespace {
+
+// Check whether two RegistryDicts equal each other.
+testing::AssertionResult RegistryDictEquals(const RegistryDict& a,
+                                            const RegistryDict& b) {
+  RegistryDict::KeyMap::const_iterator iter_key_a(a.keys().begin());
+  RegistryDict::KeyMap::const_iterator iter_key_b(b.keys().begin());
+  for (; iter_key_a != a.keys().end() && iter_key_b != b.keys().end();
+       ++iter_key_a, ++iter_key_b) {
+    if (iter_key_a->first != iter_key_b->first) {
+      return testing::AssertionFailure()
+          << "Key mismatch " << iter_key_a->first
+          << " vs. " << iter_key_b->first;
+    }
+    testing::AssertionResult result = RegistryDictEquals(*iter_key_a->second,
+                                                         *iter_key_b->second);
+    if (!result)
+      return result;
+  }
+
+  RegistryDict::ValueMap::const_iterator iter_value_a(a.values().begin());
+  RegistryDict::ValueMap::const_iterator iter_value_b(b.values().begin());
+  for (; iter_value_a != a.values().end() && iter_value_b != b.values().end();
+       ++iter_value_a, ++iter_value_b) {
+    if (iter_value_a->first != iter_value_b->first ||
+        !base::Value::Equals(iter_value_a->second, iter_value_b->second)) {
+      return testing::AssertionFailure()
+          << "Value mismatch " << iter_value_a->first
+          << " vs. " << iter_value_b->first;
+    }
+  }
+
+  return testing::AssertionSuccess();
+}
+
+void SetInteger(RegistryDict* dict,
+                const std::string& name,
+                int value) {
+  dict->SetValue(
+      name,
+      make_scoped_ptr<base::Value>(new base::FundamentalValue(value)));
+}
+
+void SetString(RegistryDict* dict,
+               const std::string& name,
+               const std::string&  value) {
+  dict->SetValue(
+      name,
+      make_scoped_ptr<base::Value>(new base::StringValue(value)));
+}
 
 TEST(PRegParserWinTest, TestParseFile) {
   base::FilePath test_data_dir;
@@ -22,17 +73,19 @@
 
   // Prepare the test dictionary with some data so the test can check that the
   // PReg action triggers work, i.e. remove these items.
-  base::DictionaryValue dict;
-  dict.SetInteger("DeleteValuesTest1", 1);
-  dict.SetString("DeleteValuesTest2", "2");
-  dict.SetInteger("DeleteKeysTest1", 1);
-  dict.SetString("DeleteKeysTest2", "2");
-  dict.SetInteger("DelTest", 1);
-  base::DictionaryValue* subdict = new base::DictionaryValue();
-  subdict->SetInteger("DelValsTest1", 1);
-  subdict->SetString("DelValsTest2", "2");
-  subdict->Set("DelValsTest3", new base::DictionaryValue());
-  dict.Set("DelValsTest", subdict);
+  RegistryDict dict;
+  SetInteger(&dict, "DeleteValuesTest1", 1);
+  SetString(&dict, "DeleteValuesTest2", "2");
+  dict.SetKey("DeleteKeysTest1", make_scoped_ptr(new RegistryDict()));
+  scoped_ptr<RegistryDict> delete_keys_test(new RegistryDict());
+  SetInteger(delete_keys_test.get(), "DeleteKeysTest2Entry", 1);
+  dict.SetKey("DeleteKeysTest2", delete_keys_test.Pass());
+  SetInteger(&dict, "DelTest", 1);
+  scoped_ptr<RegistryDict> subdict(new RegistryDict());
+  SetInteger(subdict.get(), "DelValsTest1", 1);
+  SetString(subdict.get(), "DelValsTest2", "2");
+  subdict->SetKey("DelValsTest3", make_scoped_ptr(new RegistryDict()));
+  dict.SetKey("DelValsTest", subdict.Pass());
 
   // Run the parser.
   base::FilePath test_file(test_data_dir.AppendASCII("policy/registry.pol"));
@@ -41,22 +94,23 @@
       test_file, L"SOFTWARE\\Policies\\Chromium", &dict, &status));
 
   // Build the expected output dictionary.
-  base::DictionaryValue expected;
-  base::DictionaryValue* del_vals_dict = new base::DictionaryValue();
-  del_vals_dict->Set("DelValsTest3", new base::DictionaryValue());
-  expected.Set("DelValsTest", del_vals_dict);
-  expected.SetInteger("HomepageIsNewTabPage", 1);
-  expected.SetString("HomepageLocation", "http://www.example.com");
-  expected.SetInteger("RestoreOnStartup", 4);
-  base::DictionaryValue* startup_urls = new DictionaryValue();
-  startup_urls->SetString("1", "http://www.chromium.org");
-  startup_urls->SetString("2", "http://www.example.com");
-  expected.Set("RestoreOnStartupURLs", startup_urls);
-  expected.SetInteger("ShowHomeButton", 1);
-  expected.SetString("Snowman", "\xE2\x98\x83");
+  RegistryDict expected;
+  scoped_ptr<RegistryDict> del_vals_dict(new RegistryDict());
+  del_vals_dict->SetKey("DelValsTest3", make_scoped_ptr(new RegistryDict()));
+  expected.SetKey("DelValsTest", del_vals_dict.Pass());
+  SetInteger(&expected, "HomepageIsNewTabPage", 1);
+  SetString(&expected, "HomepageLocation", "http://www.example.com");
+  SetInteger(&expected, "RestoreOnStartup", 4);
+  scoped_ptr<RegistryDict> startup_urls(new RegistryDict());
+  SetString(startup_urls.get(), "1", "http://www.chromium.org");
+  SetString(startup_urls.get(), "2", "http://www.example.com");
+  expected.SetKey("RestoreOnStartupURLs", startup_urls.Pass());
+  SetInteger(&expected, "ShowHomeButton", 1);
+  SetString(&expected, "Snowman", "\xE2\x98\x83");
 
-  EXPECT_TRUE(base::Value::Equals(&expected, &dict));
+  EXPECT_TRUE(RegistryDictEquals(dict, expected));
 }
 
-}  // namespace policy
+}  // namespace
 }  // namespace preg_parser
+}  // namespace policy
diff --git a/chrome/browser/policy/proto/cloud/device_management_backend.proto b/chrome/browser/policy/proto/cloud/device_management_backend.proto
index c92152f..17fcd64 100644
--- a/chrome/browser/policy/proto/cloud/device_management_backend.proto
+++ b/chrome/browser/policy/proto/cloud/device_management_backend.proto
@@ -169,6 +169,16 @@
   // policy_type represents the type of settings (e.g. public account,
   // extension) devices request to fetch.
   optional string settings_entity_id = 6;
+
+  // If this fetch is due to a policy invalidation, this field contains the
+  // version provided with the invalidation. The server interprets this value
+  // and the value of invalidation_payload to fetch the up-to-date policy.
+  optional int64 invalidation_version = 7;
+
+  // If this fetch is due to a policy invalidation, this field contains the
+  // payload delivered with the invalidation. The server interprets this value
+  // and the value of invalidation_version to fetch the up-to-date policy.
+  optional bytes invalidation_payload = 8;
 }
 
 // This message is included in serialized form in PolicyFetchResponse
@@ -242,6 +252,16 @@
   // Indicates the identity the device service account is associated with.
   // This is only sent as part of device policy fetch.
   optional string service_account_identity = 12;
+
+  // The object source which hosts policy objects within the invalidation
+  // service. This value is combined with invalidation_name to form the object
+  // id used to register for invalidations to this policy.
+  optional int32 invalidation_source = 13;
+
+  // The name which uniquely identifies this policy within the invalidation
+  // service object source. This value is combined with invalidation_source to
+  // form the object id used to register for invalidations to this policy.
+  optional bytes invalidation_name = 14;
 }
 
 message PolicyFetchResponse {
diff --git a/chrome/browser/policy/registry_dict_win.cc b/chrome/browser/policy/registry_dict_win.cc
new file mode 100644
index 0000000..b99cca3
--- /dev/null
+++ b/chrome/browser/policy/registry_dict_win.cc
@@ -0,0 +1,400 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/policy/registry_dict_win.h"
+
+#include "base/json/json_reader.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/sys_byteorder.h"
+#include "base/values.h"
+#include "base/win/registry.h"
+#include "chrome/common/json_schema/json_schema_constants.h"
+
+namespace schema = json_schema_constants;
+
+using base::win::RegistryKeyIterator;
+using base::win::RegistryValueIterator;
+
+namespace policy {
+
+namespace {
+
+// Returns the entry with key |name| in |dictionary| (can be NULL), or NULL.
+const base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary,
+                                      const std::string& name) {
+  if (!dictionary)
+    return NULL;
+  const base::DictionaryValue* entry = NULL;
+  dictionary->GetDictionaryWithoutPathExpansion(name, &entry);
+  return entry;
+}
+
+// Returns the Value type described in |schema|, or |default_type| if not found.
+base::Value::Type GetValueTypeForSchema(const base::DictionaryValue* schema,
+                                        base::Value::Type default_type) {
+  // JSON-schema types to base::Value::Type mapping.
+  static const struct {
+    // JSON schema type.
+    const char* schema_type;
+    // Correspondent value type.
+    base::Value::Type value_type;
+  } kSchemaToValueTypeMap[] = {
+    { schema::kArray,        base::Value::TYPE_LIST        },
+    { schema::kBoolean,      base::Value::TYPE_BOOLEAN     },
+    { schema::kInteger,      base::Value::TYPE_INTEGER     },
+    { schema::kNull,         base::Value::TYPE_NULL        },
+    { schema::kNumber,       base::Value::TYPE_DOUBLE      },
+    { schema::kObject,       base::Value::TYPE_DICTIONARY  },
+    { schema::kString,       base::Value::TYPE_STRING      },
+  };
+
+  if (!schema)
+    return default_type;
+  std::string type;
+  if (!schema->GetStringWithoutPathExpansion(schema::kType, &type))
+    return default_type;
+  for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) {
+    if (type == kSchemaToValueTypeMap[i].schema_type)
+      return kSchemaToValueTypeMap[i].value_type;
+  }
+  return default_type;
+}
+
+// Returns the schema for property |name| given the |schema| of an object.
+// Returns the "additionalProperties" schema if no specific schema for
+// |name| is present. Returns NULL if no schema is found.
+const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema,
+                                          const std::string& name) {
+  const base::DictionaryValue* properties =
+      GetEntry(schema, schema::kProperties);
+  const base::DictionaryValue* sub_schema = GetEntry(properties, name);
+  if (sub_schema)
+    return sub_schema;
+  // "additionalProperties" can be a boolean, but that case is ignored.
+  return GetEntry(schema, schema::kAdditionalProperties);
+}
+
+// Converts a value (as read from the registry) to meet |schema|, converting
+// types as necessary. Unconvertible types will show up as NULL values in the
+// result.
+scoped_ptr<base::Value> ConvertValue(const base::Value& value,
+                                     const base::DictionaryValue* schema) {
+  // Figure out the type to convert to from the schema.
+  const base::Value::Type result_type(
+      GetValueTypeForSchema(schema, value.GetType()));
+
+  // If the type is good already, go with it.
+  if (value.IsType(result_type)) {
+    // Recurse for complex types if there is a schema.
+    if (schema) {
+      const base::DictionaryValue* dict = NULL;
+      const base::ListValue* list = NULL;
+      if (value.GetAsDictionary(&dict)) {
+        scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+        for (base::DictionaryValue::Iterator entry(*dict); !entry.IsAtEnd();
+             entry.Advance()) {
+          scoped_ptr<base::Value> converted_value(
+              ConvertValue(entry.value(), GetSchemaFor(schema, entry.key())));
+          result->SetWithoutPathExpansion(entry.key(),
+                                          converted_value.release());
+        }
+        return result.Pass();
+      } else if (value.GetAsList(&list)) {
+        scoped_ptr<base::ListValue> result(new base::ListValue());
+        const base::DictionaryValue* item_schema =
+            GetEntry(schema, schema::kItems);
+        for (base::ListValue::const_iterator entry(list->begin());
+             entry != list->end(); ++entry) {
+          result->Append(ConvertValue(**entry, item_schema).release());
+        }
+        return result.Pass();
+      }
+    }
+    return make_scoped_ptr(value.DeepCopy());
+  }
+
+  // Else, do some conversions to map windows registry data types to JSON types.
+  std::string string_value;
+  int int_value = 0;
+  switch (result_type) {
+    case base::Value::TYPE_NULL: {
+      return make_scoped_ptr(base::Value::CreateNullValue());
+    }
+    case base::Value::TYPE_BOOLEAN: {
+      // Accept booleans encoded as either string or integer.
+      if (value.GetAsInteger(&int_value) ||
+          (value.GetAsString(&string_value) &&
+           base::StringToInt(string_value, &int_value))) {
+        return make_scoped_ptr(Value::CreateBooleanValue(int_value != 0));
+      }
+      break;
+    }
+    case base::Value::TYPE_INTEGER: {
+      // Integers may be string-encoded.
+      if (value.GetAsString(&string_value) &&
+          base::StringToInt(string_value, &int_value)) {
+        return make_scoped_ptr(base::Value::CreateIntegerValue(int_value));
+      }
+      break;
+    }
+    case base::Value::TYPE_DOUBLE: {
+      // Doubles may be string-encoded or integer-encoded.
+      double double_value = 0;
+      if (value.GetAsInteger(&int_value)) {
+        return make_scoped_ptr(base::Value::CreateDoubleValue(int_value));
+      } else if (value.GetAsString(&string_value) &&
+                 base::StringToDouble(string_value, &double_value)) {
+        return make_scoped_ptr(base::Value::CreateDoubleValue(double_value));
+      }
+      break;
+    }
+    case base::Value::TYPE_LIST: {
+      // Lists are encoded as subkeys with numbered value in the registry.
+      const base::DictionaryValue* dict = NULL;
+      if (value.GetAsDictionary(&dict)) {
+        scoped_ptr<base::ListValue> result(new base::ListValue());
+        const base::DictionaryValue* item_schema =
+            GetEntry(schema, schema::kItems);
+        for (int i = 1; ; ++i) {
+          const base::Value* entry = NULL;
+          if (!dict->Get(base::IntToString(i), &entry))
+            break;
+          result->Append(ConvertValue(*entry, item_schema).release());
+        }
+        return result.Pass();
+      }
+      // Fall through in order to accept lists encoded as JSON strings.
+    }
+    case base::Value::TYPE_DICTIONARY: {
+      // Dictionaries may be encoded as JSON strings.
+      if (value.GetAsString(&string_value)) {
+        scoped_ptr<base::Value> result(base::JSONReader::Read(string_value));
+        if (result && result->IsType(result_type))
+          return result.Pass();
+      }
+      break;
+    }
+    case base::Value::TYPE_STRING:
+    case base::Value::TYPE_BINARY:
+      // No conversion possible.
+      break;
+  }
+
+  LOG(WARNING) << "Failed to convert " << value.GetType()
+               << " to " << result_type;
+  return make_scoped_ptr(base::Value::CreateNullValue());
+}
+
+}  // namespace
+
+bool CaseInsensitiveStringCompare::operator()(const std::string& a,
+                                              const std::string& b) const {
+  return base::strcasecmp(a.c_str(), b.c_str()) < 0;
+}
+
+RegistryDict::RegistryDict() {}
+
+RegistryDict::~RegistryDict() {
+  ClearKeys();
+  ClearValues();
+}
+
+RegistryDict* RegistryDict::GetKey(const std::string& name) {
+  KeyMap::iterator entry = keys_.find(name);
+  return entry != keys_.end() ? entry->second : NULL;
+}
+
+const RegistryDict* RegistryDict::GetKey(const std::string& name) const {
+  KeyMap::const_iterator entry = keys_.find(name);
+  return entry != keys_.end() ? entry->second : NULL;
+}
+
+void RegistryDict::SetKey(const std::string& name,
+                          scoped_ptr<RegistryDict> dict) {
+  if (!dict) {
+    RemoveKey(name);
+    return;
+  }
+
+  RegistryDict*& entry = keys_[name];
+  delete entry;
+  entry = dict.release();
+}
+
+scoped_ptr<RegistryDict> RegistryDict::RemoveKey(const std::string& name) {
+  scoped_ptr<RegistryDict> result;
+  KeyMap::iterator entry = keys_.find(name);
+  if (entry != keys_.end()) {
+    result.reset(entry->second);
+    keys_.erase(entry);
+  }
+  return result.Pass();
+}
+
+void RegistryDict::ClearKeys() {
+  STLDeleteValues(&keys_);
+}
+
+base::Value* RegistryDict::GetValue(const std::string& name) {
+  ValueMap::iterator entry = values_.find(name);
+  return entry != values_.end() ? entry->second : NULL;
+}
+
+const base::Value* RegistryDict::GetValue(const std::string& name) const {
+  ValueMap::const_iterator entry = values_.find(name);
+  return entry != values_.end() ? entry->second : NULL;
+}
+
+void RegistryDict::SetValue(const std::string& name,
+                            scoped_ptr<base::Value> dict) {
+  if (!dict) {
+    RemoveValue(name);
+    return;
+  }
+
+  Value*& entry = values_[name];
+  delete entry;
+  entry = dict.release();
+}
+
+scoped_ptr<base::Value> RegistryDict::RemoveValue(const std::string& name) {
+  scoped_ptr<base::Value> result;
+  ValueMap::iterator entry = values_.find(name);
+  if (entry != values_.end()) {
+    result.reset(entry->second);
+    values_.erase(entry);
+  }
+  return result.Pass();
+}
+
+void RegistryDict::ClearValues() {
+  STLDeleteValues(&values_);
+}
+
+void RegistryDict::Merge(const RegistryDict& other) {
+  for (KeyMap::const_iterator entry(other.keys_.begin());
+       entry != other.keys_.end(); ++entry) {
+    RegistryDict*& subdict = keys_[entry->first];
+    if (!subdict)
+      subdict = new RegistryDict();
+    subdict->Merge(*entry->second);
+  }
+
+  for (ValueMap::const_iterator entry(other.values_.begin());
+       entry != other.values_.end(); ++entry) {
+    SetValue(entry->first, make_scoped_ptr(entry->second->DeepCopy()));
+  }
+}
+
+void RegistryDict::Swap(RegistryDict* other) {
+  keys_.swap(other->keys_);
+  values_.swap(other->values_);
+}
+
+void RegistryDict::ReadRegistry(HKEY hive, const string16& root) {
+  ClearKeys();
+  ClearValues();
+
+  // First, read all the values of the key.
+  for (RegistryValueIterator it(hive, root.c_str()); it.Valid(); ++it) {
+    const std::string name = UTF16ToUTF8(it.Name());
+    switch (it.Type()) {
+      case REG_SZ:
+      case REG_EXPAND_SZ:
+        SetValue(
+            name,
+            make_scoped_ptr(new base::StringValue(UTF16ToUTF8(it.Value()))));
+        continue;
+      case REG_DWORD_LITTLE_ENDIAN:
+      case REG_DWORD_BIG_ENDIAN:
+        if (it.ValueSize() == sizeof(DWORD)) {
+          DWORD dword_value = *(reinterpret_cast<const DWORD*>(it.Value()));
+          if (it.Type() == REG_DWORD_BIG_ENDIAN)
+            dword_value = base::NetToHost32(dword_value);
+          else
+            dword_value = base::ByteSwapToLE32(dword_value);
+          SetValue(
+              name,
+              make_scoped_ptr(base::Value::CreateIntegerValue(dword_value)));
+          continue;
+        }
+      case REG_NONE:
+      case REG_LINK:
+      case REG_MULTI_SZ:
+      case REG_RESOURCE_LIST:
+      case REG_FULL_RESOURCE_DESCRIPTOR:
+      case REG_RESOURCE_REQUIREMENTS_LIST:
+      case REG_QWORD_LITTLE_ENDIAN:
+        // Unsupported type, message gets logged below.
+        break;
+    }
+
+    LOG(WARNING) << "Failed to read hive " << hive << " at "
+                 << root << "\\" << name
+                 << " type " << it.Type();
+  }
+
+  // Recurse for all subkeys.
+  for (RegistryKeyIterator it(hive, root.c_str()); it.Valid(); ++it) {
+    std::string name(UTF16ToUTF8(it.Name()));
+    scoped_ptr<RegistryDict> subdict(new RegistryDict());
+    subdict->ReadRegistry(hive, root + L"\\" + it.Name());
+    SetKey(name, subdict.Pass());
+  }
+}
+
+scoped_ptr<base::Value> RegistryDict::ConvertToJSON(
+    const base::DictionaryValue* schema) const {
+  base::Value::Type type =
+      GetValueTypeForSchema(schema, base::Value::TYPE_DICTIONARY);
+  switch (type) {
+    case base::Value::TYPE_DICTIONARY: {
+      scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+      for (RegistryDict::ValueMap::const_iterator entry(values_.begin());
+           entry != values_.end(); ++entry) {
+        result->SetWithoutPathExpansion(
+            entry->first,
+            ConvertValue(*entry->second,
+                         GetSchemaFor(schema, entry->first)).release());
+      }
+      for (RegistryDict::KeyMap::const_iterator entry(keys_.begin());
+           entry != keys_.end(); ++entry) {
+        result->SetWithoutPathExpansion(
+            entry->first,
+            entry->second->ConvertToJSON(
+                GetSchemaFor(schema, entry->first)).release());
+      }
+      return result.Pass();
+    }
+    case base::Value::TYPE_LIST: {
+      scoped_ptr<base::ListValue> result(new base::ListValue());
+      const base::DictionaryValue* item_schema =
+          GetEntry(schema, schema::kItems);
+      for (int i = 1; ; ++i) {
+        const std::string name(base::IntToString(i));
+        const RegistryDict* key = GetKey(name);
+        if (key) {
+          result->Append(key->ConvertToJSON(item_schema).release());
+          continue;
+        }
+        const base::Value* value = GetValue(name);
+        if (value) {
+          result->Append(ConvertValue(*value, item_schema).release());
+          continue;
+        }
+        break;
+      }
+      return result.Pass();
+    }
+    default:
+      LOG(WARNING) << "Can't convert registry key to schema type " << type;
+  }
+
+  return make_scoped_ptr(base::Value::CreateNullValue());
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/registry_dict_win.h b/chrome/browser/policy/registry_dict_win.h
new file mode 100644
index 0000000..43abf26
--- /dev/null
+++ b/chrome/browser/policy/registry_dict_win.h
@@ -0,0 +1,90 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_POLICY_REGISTRY_DICT_WIN_H_
+#define CHROME_BROWSER_POLICY_REGISTRY_DICT_WIN_H_
+
+#include <windows.h>
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+
+namespace base {
+class DictionaryValue;
+class Value;
+}
+
+namespace policy {
+
+// A case-insensitive string comparison functor.
+struct CaseInsensitiveStringCompare {
+  bool operator()(const std::string& a, const std::string& b) const;
+};
+
+// In-memory representation of a registry subtree. Using a
+// base::DictionaryValue directly seems tempting, but that doesn't handle the
+// registry's case-insensitive-but-case-preserving semantics properly.
+class RegistryDict {
+ public:
+  typedef std::map<std::string, RegistryDict*,
+      CaseInsensitiveStringCompare> KeyMap;
+  typedef std::map<std::string, base::Value*,
+      CaseInsensitiveStringCompare> ValueMap;
+
+  RegistryDict();
+  ~RegistryDict();
+
+  // Returns a pointer to an existing key, NULL if not present.
+  RegistryDict* GetKey(const std::string& name);
+  const RegistryDict* GetKey(const std::string& name) const;
+  // Sets a key. If |dict| is NULL, clears that key.
+  void SetKey(const std::string& name, scoped_ptr<RegistryDict> dict);
+  // Removes a key. If the key doesn't exist, NULL is returned.
+  scoped_ptr<RegistryDict> RemoveKey(const std::string& name);
+  // Clears all keys.
+  void ClearKeys();
+
+  // Returns a pointer to a value, NULL if not present.
+  base::Value* GetValue(const std::string& name);
+  const base::Value* GetValue(const std::string& name) const;
+  // Sets a value. If |value| is NULL, removes the value.
+  void SetValue(const std::string& name, scoped_ptr<base::Value> value);
+  // Removes a value. If the value doesn't exist, NULL is returned.
+  scoped_ptr<base::Value> RemoveValue(const std::string& name);
+  // Clears all values.
+  void ClearValues();
+
+  // Merge keys and values from |other|, giving precedence to |other|.
+  void Merge(const RegistryDict& other);
+
+  // Swap with |other|.
+  void Swap(RegistryDict* other);
+
+  // Read a Windows registry subtree into this registry dictionary object.
+  void ReadRegistry(HKEY hive, const string16& root);
+
+  // Converts the dictionary to base::Value representation. For key/value name
+  // collisions, the key wins. |schema| supplies an optional JSON schema that
+  // will be used to map types to base::Value types. The returned object is
+  // either a base::DictionaryValue or a base::ListValue.
+  scoped_ptr<base::Value> ConvertToJSON(
+      const base::DictionaryValue* schema) const;
+
+  const KeyMap& keys() const { return keys_; }
+  const ValueMap& values() const { return values_; }
+
+ private:
+  KeyMap keys_;
+  ValueMap values_;
+
+  DISALLOW_COPY_AND_ASSIGN(RegistryDict);
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_POLICY_REGISTRY_DICT_WIN_H_
diff --git a/chrome/browser/policy/registry_dict_win_unittest.cc b/chrome/browser/policy/registry_dict_win_unittest.cc
new file mode 100644
index 0000000..59aa69f
--- /dev/null
+++ b/chrome/browser/policy/registry_dict_win_unittest.cc
@@ -0,0 +1,241 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/policy/registry_dict_win.h"
+
+#include "base/values.h"
+#include "chrome/common/json_schema/json_schema_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace schema = json_schema_constants;
+
+namespace policy {
+namespace {
+
+TEST(RegistryDictTest, SetAndGetValue) {
+  RegistryDict test_dict;
+
+  base::FundamentalValue int_value(42);
+  base::StringValue string_value("fortytwo");
+
+  test_dict.SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+  EXPECT_EQ(1, test_dict.values().size());
+  EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("one")));
+  EXPECT_FALSE(test_dict.GetValue("two"));
+
+  test_dict.SetValue("two", make_scoped_ptr(string_value.DeepCopy()));
+  EXPECT_EQ(2, test_dict.values().size());
+  EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("one")));
+  EXPECT_TRUE(base::Value::Equals(&string_value, test_dict.GetValue("two")));
+
+  scoped_ptr<base::Value> one(test_dict.RemoveValue("one"));
+  EXPECT_EQ(1, test_dict.values().size());
+  EXPECT_TRUE(base::Value::Equals(&int_value, one.get()));
+  EXPECT_FALSE(test_dict.GetValue("one"));
+  EXPECT_TRUE(base::Value::Equals(&string_value, test_dict.GetValue("two")));
+
+  test_dict.ClearValues();
+  EXPECT_FALSE(test_dict.GetValue("one"));
+  EXPECT_FALSE(test_dict.GetValue("two"));
+  EXPECT_TRUE(test_dict.values().empty());
+}
+
+TEST(RegistryDictTest, CaseInsensitiveButPreservingValueNames) {
+  RegistryDict test_dict;
+
+  base::FundamentalValue int_value(42);
+  base::StringValue string_value("fortytwo");
+
+  test_dict.SetValue("One", make_scoped_ptr(int_value.DeepCopy()));
+  EXPECT_EQ(1, test_dict.values().size());
+  EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("oNe")));
+
+  RegistryDict::ValueMap::const_iterator entry = test_dict.values().begin();
+  ASSERT_NE(entry, test_dict.values().end());
+  EXPECT_EQ("One", entry->first);
+
+  test_dict.SetValue("ONE", make_scoped_ptr(string_value.DeepCopy()));
+  EXPECT_EQ(1, test_dict.values().size());
+  EXPECT_TRUE(base::Value::Equals(&string_value, test_dict.GetValue("one")));
+
+  scoped_ptr<base::Value> removed_value(test_dict.RemoveValue("onE"));
+  EXPECT_TRUE(base::Value::Equals(&string_value, removed_value.get()));
+  EXPECT_TRUE(test_dict.values().empty());
+}
+
+TEST(RegistryDictTest, SetAndGetKeys) {
+  RegistryDict test_dict;
+
+  base::FundamentalValue int_value(42);
+  base::StringValue string_value("fortytwo");
+
+  scoped_ptr<RegistryDict> subdict(new RegistryDict());
+  subdict->SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+  test_dict.SetKey("two", subdict.Pass());
+  EXPECT_EQ(1, test_dict.keys().size());
+  RegistryDict* actual_subdict = test_dict.GetKey("two");
+  ASSERT_TRUE(actual_subdict);
+  EXPECT_TRUE(base::Value::Equals(&int_value, actual_subdict->GetValue("one")));
+
+  subdict.reset(new RegistryDict());
+  subdict->SetValue("three", make_scoped_ptr(string_value.DeepCopy()));
+  test_dict.SetKey("four", subdict.Pass());
+  EXPECT_EQ(2, test_dict.keys().size());
+  actual_subdict = test_dict.GetKey("two");
+  ASSERT_TRUE(actual_subdict);
+  EXPECT_TRUE(base::Value::Equals(&int_value, actual_subdict->GetValue("one")));
+  actual_subdict = test_dict.GetKey("four");
+  ASSERT_TRUE(actual_subdict);
+  EXPECT_TRUE(base::Value::Equals(&string_value,
+                                  actual_subdict->GetValue("three")));
+
+  test_dict.ClearKeys();
+  EXPECT_FALSE(test_dict.GetKey("one"));
+  EXPECT_FALSE(test_dict.GetKey("three"));
+  EXPECT_TRUE(test_dict.keys().empty());
+}
+
+TEST(RegistryDictTest, CaseInsensitiveButPreservingKeyNames) {
+  RegistryDict test_dict;
+
+  base::FundamentalValue int_value(42);
+
+  test_dict.SetKey("One", make_scoped_ptr(new RegistryDict()));
+  EXPECT_EQ(1, test_dict.keys().size());
+  RegistryDict* actual_subdict = test_dict.GetKey("One");
+  ASSERT_TRUE(actual_subdict);
+  EXPECT_TRUE(actual_subdict->values().empty());
+
+  RegistryDict::KeyMap::const_iterator entry = test_dict.keys().begin();
+  ASSERT_NE(entry, test_dict.keys().end());
+  EXPECT_EQ("One", entry->first);
+
+  scoped_ptr<RegistryDict> subdict(new RegistryDict());
+  subdict->SetValue("two", make_scoped_ptr(int_value.DeepCopy()));
+  test_dict.SetKey("ONE", subdict.Pass());
+  EXPECT_EQ(1, test_dict.keys().size());
+  actual_subdict = test_dict.GetKey("One");
+  ASSERT_TRUE(actual_subdict);
+  EXPECT_TRUE(base::Value::Equals(&int_value,
+                                  actual_subdict->GetValue("two")));
+
+  scoped_ptr<RegistryDict> removed_key(test_dict.RemoveKey("one"));
+  ASSERT_TRUE(removed_key);
+  EXPECT_TRUE(base::Value::Equals(&int_value,
+                                  removed_key->GetValue("two")));
+  EXPECT_TRUE(test_dict.keys().empty());
+}
+
+TEST(RegistryDictTest, Merge) {
+  RegistryDict dict_a;
+  RegistryDict dict_b;
+
+  base::FundamentalValue int_value(42);
+  base::StringValue string_value("fortytwo");
+
+  dict_a.SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+  scoped_ptr<RegistryDict> subdict(new RegistryDict());
+  subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy()));
+  dict_a.SetKey("three", subdict.Pass());
+
+  dict_b.SetValue("four", make_scoped_ptr(string_value.DeepCopy()));
+  subdict.reset(new RegistryDict());
+  subdict->SetValue("two", make_scoped_ptr(int_value.DeepCopy()));
+  dict_b.SetKey("three", subdict.Pass());
+  subdict.reset(new RegistryDict());
+  subdict->SetValue("five", make_scoped_ptr(int_value.DeepCopy()));
+  dict_b.SetKey("six", subdict.Pass());
+
+  dict_a.Merge(dict_b);
+
+  EXPECT_TRUE(base::Value::Equals(&int_value, dict_a.GetValue("one")));
+  EXPECT_TRUE(base::Value::Equals(&string_value, dict_b.GetValue("four")));
+  RegistryDict* actual_subdict = dict_a.GetKey("three");
+  ASSERT_TRUE(actual_subdict);
+  EXPECT_TRUE(base::Value::Equals(&int_value, actual_subdict->GetValue("two")));
+  actual_subdict = dict_a.GetKey("six");
+  ASSERT_TRUE(actual_subdict);
+  EXPECT_TRUE(base::Value::Equals(&int_value,
+                                  actual_subdict->GetValue("five")));
+}
+
+TEST(RegistryDictTest, Swap) {
+  RegistryDict dict_a;
+  RegistryDict dict_b;
+
+  base::FundamentalValue int_value(42);
+  base::StringValue string_value("fortytwo");
+
+  dict_a.SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+  dict_a.SetKey("two", make_scoped_ptr(new RegistryDict()));
+  dict_b.SetValue("three", make_scoped_ptr(string_value.DeepCopy()));
+
+  dict_a.Swap(&dict_b);
+
+  EXPECT_TRUE(base::Value::Equals(&int_value, dict_b.GetValue("one")));
+  EXPECT_TRUE(dict_b.GetKey("two"));
+  EXPECT_FALSE(dict_b.GetValue("two"));
+
+  EXPECT_TRUE(base::Value::Equals(&string_value, dict_a.GetValue("three")));
+  EXPECT_FALSE(dict_a.GetValue("one"));
+  EXPECT_FALSE(dict_a.GetKey("two"));
+}
+
+TEST(RegistryDictTest, ConvertToJSON) {
+  RegistryDict test_dict;
+
+  base::FundamentalValue int_value(42);
+  base::StringValue string_value("fortytwo");
+
+  test_dict.SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+  scoped_ptr<RegistryDict> subdict(new RegistryDict());
+  subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy()));
+  test_dict.SetKey("three", subdict.Pass());
+  scoped_ptr<RegistryDict> list(new RegistryDict());
+  list->SetValue("1", make_scoped_ptr(string_value.DeepCopy()));
+  test_dict.SetKey("four", list.Pass());
+
+  base::DictionaryValue schema;
+  scoped_ptr<base::DictionaryValue> list_schema(new base::DictionaryValue());
+  list_schema->SetString(schema::kType, schema::kArray);
+  scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue());
+  properties->Set("four", list_schema.release());
+  schema.SetString(schema::kType, schema::kObject);
+  schema.Set(schema::kProperties, properties.release());
+
+  scoped_ptr<base::Value> actual(test_dict.ConvertToJSON(&schema));
+
+  base::DictionaryValue expected;
+  expected.Set("one", int_value.DeepCopy());
+  scoped_ptr<base::DictionaryValue> expected_subdict(
+      new base::DictionaryValue());
+  expected_subdict->Set("two", string_value.DeepCopy());
+  expected.Set("three", expected_subdict.release());
+  scoped_ptr<base::ListValue> expected_list(new base::ListValue());
+  expected_list->Append(string_value.DeepCopy());
+  expected.Set("four", expected_list.release());
+
+  EXPECT_TRUE(base::Value::Equals(actual.get(), &expected));
+}
+
+TEST(RegistryDictTest, KeyValueNameClashes) {
+  RegistryDict test_dict;
+
+  base::FundamentalValue int_value(42);
+  base::StringValue string_value("fortytwo");
+
+  test_dict.SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+  scoped_ptr<RegistryDict> subdict(new RegistryDict());
+  subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy()));
+  test_dict.SetKey("one", subdict.Pass());
+
+  EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("one")));
+  RegistryDict* actual_subdict = test_dict.GetKey("one");
+  ASSERT_TRUE(actual_subdict);
+  EXPECT_TRUE(base::Value::Equals(&string_value,
+                                  actual_subdict->GetValue("two")));
+}
+
+}  // namespace
+}  // namespace policy
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 49d883f..c6328c6 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -65,6 +65,7 @@
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/sync/sync_prefs.h"
 #include "chrome/browser/task_manager/task_manager.h"
 #include "chrome/browser/translate/translate_prefs.h"
@@ -78,7 +79,6 @@
 #include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
 #include "chrome/browser/ui/startup/autolaunch_prompt.h"
 #include "chrome/browser/ui/startup/default_browser_prompt.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/tabs/pinned_tab_codec.h"
 #include "chrome/browser/ui/webui/extensions/extension_settings_handler.h"
 #include "chrome/browser/ui/webui/flags_ui.h"
@@ -89,7 +89,7 @@
 #include "chrome/browser/ui/window_snapshot/window_snapshot.h"
 #include "chrome/browser/upgrade_detector.h"
 #include "chrome/browser/web_resource/promo_resource_service.h"
-#include "chrome/common/metrics/entropy_provider.h"
+#include "chrome/common/metrics/caching_permuted_entropy_provider.h"
 #include "chrome/common/pref_names.h"
 #include "components/autofill/core/browser/autofill_manager.h"
 #include "components/user_prefs/pref_registry_syncable.h"
@@ -102,8 +102,8 @@
 #endif
 
 #if defined(ENABLE_MANAGED_USERS)
-#include "chrome/browser/managed_mode/managed_user_registration_service.h"
 #include "chrome/browser/managed_mode/managed_user_service.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service.h"
 #endif
 
 #if defined(OS_MACOSX)
@@ -123,8 +123,6 @@
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/audio/audio_devices_pref_handler_impl.h"
-#include "chrome/browser/chromeos/audio/audio_handler.h"
-#include "chrome/browser/chromeos/audio/audio_pref_handler_impl.h"
 #include "chrome/browser/chromeos/customization_document.h"
 #include "chrome/browser/chromeos/display/display_preferences.h"
 #include "chrome/browser/chromeos/login/default_pinned_apps_field_trial.h"
@@ -250,7 +248,6 @@
 
 #if defined(OS_CHROMEOS)
   chromeos::AudioDevicesPrefHandlerImpl::RegisterPrefs(registry);
-  chromeos::AudioPrefHandlerImpl::RegisterPrefs(registry);
   chromeos::DataPromoNotification::RegisterPrefs(registry);
   chromeos::DeviceOAuth2TokenService::RegisterPrefs(registry);
   chromeos::device_settings_cache::RegisterPrefs(registry);
@@ -325,7 +322,7 @@
 
 #if defined(ENABLE_MANAGED_USERS)
   ManagedUserService::RegisterProfilePrefs(registry);
-  ManagedUserRegistrationService::RegisterProfilePrefs(registry);
+  ManagedUserSyncService::RegisterProfilePrefs(registry);
 #endif
 
 #if defined(ENABLE_NOTIFICATIONS)
@@ -351,7 +348,7 @@
   DeviceIDFetcher::RegisterProfilePrefs(registry);
   DevToolsWindow::RegisterProfilePrefs(registry);
   extensions::CommandService::RegisterProfilePrefs(registry);
-  ExtensionSettingsHandler::RegisterProfilePrefs(registry);
+  extensions::ExtensionSettingsHandler::RegisterProfilePrefs(registry);
   PepperFlashSettingsManager::RegisterProfilePrefs(registry);
   PinnedTabCodec::RegisterProfilePrefs(registry);
   PluginsUI::RegisterProfilePrefs(registry);
@@ -359,7 +356,7 @@
   print_dialog_cloud::RegisterProfilePrefs(registry);
   printing::StickySettings::RegisterProfilePrefs(registry);
   RegisterAutolaunchUserPrefs(registry);
-  SyncPromoUI::RegisterProfilePrefs(registry);
+  signin::RegisterProfilePrefs(registry);
 #endif
 
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
diff --git a/chrome/browser/prefs/pref_functional_browsertest.cc b/chrome/browser/prefs/pref_functional_browsertest.cc
new file mode 100644
index 0000000..86bc5ee
--- /dev/null
+++ b/chrome/browser/prefs/pref_functional_browsertest.cc
@@ -0,0 +1,160 @@
+// Copyright 2013 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.
+
+#include <string>
+
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/prefs/pref_service_syncable.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/download_test_observer.h"
+
+using content::BrowserContext;
+using content::DownloadManager;
+
+class PrefsFunctionalTest : public InProcessBrowserTest {
+ protected:
+  // Create a DownloadTestObserverTerminal that will wait for the
+  // specified number of downloads to finish.
+  scoped_ptr<content::DownloadTestObserver> CreateWaiter(Browser* browser,
+                                                         int num_downloads) {
+    DownloadManager* download_manager =
+        BrowserContext::GetDownloadManager(browser->profile());
+
+    content::DownloadTestObserver* downloads_observer =
+         new content::DownloadTestObserverTerminal(
+             download_manager,
+             num_downloads,
+             content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL);
+    return make_scoped_ptr(downloads_observer);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(PrefsFunctionalTest, TestDownloadDirPref) {
+  ASSERT_TRUE(test_server()->Start());
+
+  DownloadManager* download_manager =
+      BrowserContext::GetDownloadManager(browser()->profile());
+  base::FilePath download_dir =
+      (DownloadPrefs::FromDownloadManager(download_manager))->DownloadPath();
+  base::FilePath new_download_dir = download_dir.AppendASCII("my_downloads");
+  base::FilePath downloaded_pkg =
+      new_download_dir.AppendASCII("a_zip_file.zip");
+
+  // If the directory exists, delete it.
+  if (base::PathExists(new_download_dir)) {
+    base::DeleteFile(new_download_dir, true);
+  }
+
+  // Create the new downloads directory.
+  file_util::CreateDirectory(new_download_dir);
+  // Set pref to download in new_download_dir.
+  browser()->profile()->GetPrefs()->SetFilePath(
+      prefs::kDownloadDefaultDirectory,
+      new_download_dir);
+
+  // Create a downloads observer.
+  scoped_ptr<content::DownloadTestObserver> downloads_observer(
+      CreateWaiter(browser(), 1));
+  ui_test_utils::NavigateToURL(
+      browser(),
+      test_server()->GetURL("files/downloads/a_zip_file.zip"));
+  // Waits for the download to complete.
+  downloads_observer->WaitForFinished();
+  EXPECT_TRUE(base::PathExists(downloaded_pkg));
+}
+
+// Verify image content settings show or hide images.
+IN_PROC_BROWSER_TEST_F(PrefsFunctionalTest, TestImageContentSettings) {
+  ASSERT_TRUE(test_server()->Start());
+
+  ui_test_utils::NavigateToURL(
+      browser(),
+      test_server()->GetURL("files/settings/image_page.html"));
+
+  bool result = false;
+  std::string script =
+      "for (i=0; i < document.images.length; i++) {"
+      "  if ((document.images[i].naturalWidth != 0) &&"
+      "      (document.images[i].naturalHeight != 0)) {"
+      "    window.domAutomationController.send(true);"
+      "  }"
+      "}"
+      "window.domAutomationController.send(false);";
+  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      script,
+      &result));
+  EXPECT_TRUE(result);
+
+  base::DictionaryValue value;
+  value.SetInteger("images", 2);
+  browser()->profile()->GetPrefs()->Set(prefs::kDefaultContentSettings,
+                                        value);
+
+  ui_test_utils::NavigateToURL(
+      browser(),
+      test_server()->GetURL("files/settings/image_page.html"));
+
+  result = false;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      script,
+      &result));
+  EXPECT_FALSE(result);
+}
+
+// Verify that enabling/disabling Javascript in prefs works.
+IN_PROC_BROWSER_TEST_F(PrefsFunctionalTest, TestJavascriptEnableDisable) {
+  ASSERT_TRUE(test_server()->Start());
+
+  EXPECT_TRUE(browser()->profile()->GetPrefs()->GetBoolean(
+      prefs::kWebKitJavascriptEnabled));
+  ui_test_utils::NavigateToURL(
+      browser(),
+      test_server()->GetURL("files/javaScriptTitle.html"));
+  EXPECT_EQ(ASCIIToUTF16("Title from script javascript enabled"),
+            browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
+  browser()->profile()->GetPrefs()->SetBoolean(prefs::kWebKitJavascriptEnabled,
+                                               false);
+  ui_test_utils::NavigateToURL(
+      browser(),
+      test_server()->GetURL("files/javaScriptTitle.html"));
+  EXPECT_EQ(ASCIIToUTF16("This is html title"),
+            browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
+}
+
+// Verify DNS prefetching pref.
+IN_PROC_BROWSER_TEST_F(PrefsFunctionalTest, TestNetworkPredictionEnabledPref) {
+  EXPECT_TRUE(browser()->profile()->GetPrefs()->GetBoolean(
+      prefs::kNetworkPredictionEnabled));
+  browser()->profile()->GetPrefs()->SetBoolean(prefs::kNetworkPredictionEnabled,
+                                               false);
+  EXPECT_FALSE(browser()->profile()->GetPrefs()->GetBoolean(
+      prefs::kNetworkPredictionEnabled));
+}
+
+// Verify restore for bookmark bar visibility.
+IN_PROC_BROWSER_TEST_F(PrefsFunctionalTest,
+                       TestSessionRestoreShowBookmarkBar) {
+  EXPECT_FALSE(browser()->profile()->GetPrefs()->GetBoolean(
+      prefs::kShowBookmarkBar));
+  browser()->profile()->GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true);
+  EXPECT_TRUE(browser()->profile()->GetPrefs()->GetBoolean(
+      prefs::kShowBookmarkBar));
+
+  EXPECT_TRUE(browser()->profile()->GetPrefs()->GetBoolean(
+      prefs::kShowBookmarkBar));
+  EXPECT_EQ(BookmarkBar::SHOW, browser()->bookmark_bar_state());
+}
diff --git a/chrome/browser/prefs/pref_service_browsertest.cc b/chrome/browser/prefs/pref_service_browsertest.cc
index c3c0856..c5433a8 100644
--- a/chrome/browser/prefs/pref_service_browsertest.cc
+++ b/chrome/browser/prefs/pref_service_browsertest.cc
@@ -19,14 +19,11 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "ui/gfx/rect.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 // On GTK, resizing happens asynchronously and we currently don't have a way to
 // get called back (it's probably possible, but we don't have that code). Since
 // the GTK code is going away, not spending more time on this.
@@ -48,7 +45,7 @@
 IN_PROC_BROWSER_TEST_F(PreservedWindowPlacement, MAYBE_Test) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -124,7 +121,7 @@
 IN_PROC_BROWSER_TEST_F(PreservedWindowPlacementIsLoaded, Test) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -183,7 +180,7 @@
 IN_PROC_BROWSER_TEST_F(PreservedWindowPlacementIsMigrated, Test) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/prefs/proxy_policy_unittest.cc b/chrome/browser/prefs/proxy_policy_unittest.cc
index 60e1943..50b47b2 100644
--- a/chrome/browser/prefs/proxy_policy_unittest.cc
+++ b/chrome/browser/prefs/proxy_policy_unittest.cc
@@ -5,6 +5,7 @@
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
 #include "chrome/browser/policy/external_data_fetcher.h"
 #include "chrome/browser/policy/mock_configuration_policy_provider.h"
 #include "chrome/browser/policy/policy_map.h"
@@ -108,6 +109,7 @@
     return prefs;
   }
 
+  base::MessageLoop loop_;
   CommandLine command_line_;
   MockConfigurationPolicyProvider provider_;
   scoped_ptr<PolicyServiceImpl> policy_service_;
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 41c3423..e075cc1 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -41,6 +41,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/devtools_agent_host.h"
@@ -64,10 +65,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::BrowserThread;
 using content::DevToolsAgentHost;
 using content::DevToolsClientHost;
@@ -2701,7 +2698,7 @@
                        PrerenderNaClPluginEnabled) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -2837,6 +2834,22 @@
   NavigateToDestURL();
 }
 
+// Ensure that about:blank is permitted for any subresource.
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
+                       PrerenderAllowAboutBlankSubresource) {
+  GURL image_url = GURL("about:blank");
+  std::vector<net::SpawnedTestServer::StringPair> replacement_text;
+  replacement_text.push_back(
+      std::make_pair("REPLACE_WITH_IMAGE_URL", image_url.spec()));
+  std::string replacement_path;
+  ASSERT_TRUE(net::SpawnedTestServer::GetFilePathWithReplacements(
+      "files/prerender/prerender_with_image.html",
+      replacement_text,
+      &replacement_path));
+  PrerenderTestURL(replacement_path, FINAL_STATUS_USED, 1);
+  NavigateToDestURL();
+}
+
 // Checks that non-http/https/chrome-extension subresource cancels the prerender
 // on redirect.
 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index b955695..14aaa3b 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -57,6 +57,7 @@
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_view.h"
 #include "content/public/common/favicon_url.h"
+#include "content/public/common/url_constants.h"
 #include "extensions/common/constants.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -904,6 +905,11 @@
           url.SchemeIs("data"));
 }
 
+// static
+bool PrerenderManager::DoesSubresourceURLHaveValidScheme(const GURL& url) {
+  return DoesURLHaveValidScheme(url) || url == GURL(content::kAboutBlankURL);
+}
+
 DictionaryValue* PrerenderManager::GetAsValue() const {
   DCHECK(CalledOnValidThread());
   DictionaryValue* dict_value = new DictionaryValue();
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index 8789699..91c547d 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -244,6 +244,10 @@
   // Returns true iff the scheme of the URL given is valid for prerendering.
   static bool DoesURLHaveValidScheme(const GURL& url);
 
+  // Returns true iff the scheme of the subresource URL given is valid for
+  // prerendering.
+  static bool DoesSubresourceURLHaveValidScheme(const GURL& url);
+
   // Returns a Value object containing the active pages being prerendered, and
   // a history of pages which were prerendered. The caller is responsible for
   // deleting the return value.
diff --git a/chrome/browser/prerender/prerender_tab_helper.cc b/chrome/browser/prerender/prerender_tab_helper.cc
index 5203148..cd67ad2 100644
--- a/chrome/browser/prerender/prerender_tab_helper.cc
+++ b/chrome/browser/prerender/prerender_tab_helper.cc
@@ -250,6 +250,15 @@
   if (params.password_form.origin.is_valid() && prerender_manager) {
     prerender_manager->RecordLikelyLoginOnURL(params.url);
     RecordEvent(EVENT_LOGIN_ACTION_ADDED);
+    if (details.is_main_frame) {
+      RecordEvent(EVENT_LOGIN_ACTION_ADDED_MAINFRAME);
+      if (params.password_form.password_value.empty())
+        RecordEvent(EVENT_LOGIN_ACTION_ADDED_MAINFRAME_PW_EMPTY);
+    } else {
+      RecordEvent(EVENT_LOGIN_ACTION_ADDED_SUBFRAME);
+      if (params.password_form.password_value.empty())
+        RecordEvent(EVENT_LOGIN_ACTION_ADDED_SUBFRAME_PW_EMPTY);
+    }
   }
 }
 
diff --git a/chrome/browser/prerender/prerender_tab_helper.h b/chrome/browser/prerender/prerender_tab_helper.h
index 1e889ce..c48290e 100644
--- a/chrome/browser/prerender/prerender_tab_helper.h
+++ b/chrome/browser/prerender/prerender_tab_helper.h
@@ -34,6 +34,10 @@
     EVENT_MAINFRAME_COMMIT = 4,
     EVENT_MAINFRAME_COMMIT_DOMAIN_LOGGED_IN = 5,
     EVENT_LOGIN_ACTION_ADDED = 6,
+    EVENT_LOGIN_ACTION_ADDED_MAINFRAME = 7,
+    EVENT_LOGIN_ACTION_ADDED_MAINFRAME_PW_EMPTY = 8,
+    EVENT_LOGIN_ACTION_ADDED_SUBFRAME = 9,
+    EVENT_LOGIN_ACTION_ADDED_SUBFRAME_PW_EMPTY = 10,
     EVENT_MAX_VALUE
   };
 
diff --git a/chrome/browser/printing/cloud_print/test/cloud_print_policy_browsertest.cc b/chrome/browser/printing/cloud_print/test/cloud_print_policy_browsertest.cc
index d298a2d..e4d0b62 100644
--- a/chrome/browser/printing/cloud_print/test/cloud_print_policy_browsertest.cc
+++ b/chrome/browser/printing/cloud_print/test/cloud_print_policy_browsertest.cc
@@ -11,14 +11,11 @@
 #include "chrome/common/chrome_result_codes.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/common/result_codes.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 // These tests don't apply to the Mac version; see GetCommandLineForRelaunch
 // for details.
 #if defined(OS_MACOSX)
@@ -35,7 +32,7 @@
 IN_PROC_BROWSER_TEST_F(CloudPrintPolicyTest, NormalPassedFlag) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/printing/print_preview_dialog_controller.cc b/chrome/browser/printing/print_preview_dialog_controller.cc
index 6d96eb3..9c00e65 100644
--- a/chrome/browser/printing/print_preview_dialog_controller.cc
+++ b/chrome/browser/printing/print_preview_dialog_controller.cc
@@ -173,7 +173,7 @@
       const NativeWebKeyboardEvent& event) OVERRIDE;
 
  private:
-  WebContents* tab_;
+  WebContents* initiator_;
 
   DISALLOW_COPY_AND_ASSIGN(PrintPreviewWebContentDelegate);
 };
@@ -182,7 +182,7 @@
     Profile* profile,
     WebContents* initiator)
     : WebDialogWebContentsDelegate(profile, new ChromeWebContentsHandler),
-      tab_(initiator) {}
+      initiator_(initiator) {}
 
 PrintPreviewWebContentDelegate::~PrintPreviewWebContentDelegate() {}
 
@@ -191,7 +191,7 @@
     const NativeWebKeyboardEvent& event) {
   // Disabled on Mac due to http://crbug.com/112173
 #if !defined(OS_MACOSX)
-  Browser* current_browser = chrome::FindBrowserWithWebContents(tab_);
+  Browser* current_browser = chrome::FindBrowserWithWebContents(initiator_);
   if (!current_browser)
     return;
   current_browser->window()->HandleKeyboardEvent(event);
@@ -302,7 +302,7 @@
   if (it == preview_dialog_map_.end())
     return;
 
-  RemoveObservers(it->second, INITIATOR);
+  RemoveObservers(it->second);
   preview_dialog_map_[preview_dialog] = NULL;
 }
 
@@ -360,33 +360,35 @@
     return;
   }
 
-  DCHECK_EQ(contents, preview_dialog);
+  if (contents == preview_dialog) {
+    // Preview dialog navigated.
+    if (details) {
+      content::PageTransition transition_type =
+          details->entry->GetTransitionType();
+      content::NavigationType nav_type = details->type;
 
-  // Preview dialog navigated.
-  if (details) {
-    content::PageTransition transition_type =
-        details->entry->GetTransitionType();
-    content::NavigationType nav_type = details->type;
+      // New |preview_dialog| is created. Don't update/erase map entry.
+      if (waiting_for_new_preview_page_ &&
+          transition_type == content::PAGE_TRANSITION_AUTO_TOPLEVEL &&
+          nav_type == content::NAVIGATION_TYPE_NEW_PAGE) {
+        waiting_for_new_preview_page_ = false;
+        SaveInitiatorTitle(preview_dialog);
+        return;
+      }
 
-    // New |preview_dialog| is created. Don't update/erase map entry.
-    if (waiting_for_new_preview_page_ &&
-        transition_type == content::PAGE_TRANSITION_AUTO_TOPLEVEL &&
-        nav_type == content::NAVIGATION_TYPE_NEW_PAGE) {
-      waiting_for_new_preview_page_ = false;
-      SaveInitiatorTitle(preview_dialog);
-      return;
+      // Cloud print sign-in causes a reload.
+      if (!waiting_for_new_preview_page_ &&
+          transition_type == content::PAGE_TRANSITION_RELOAD &&
+          nav_type == content::NAVIGATION_TYPE_EXISTING_PAGE &&
+          IsPrintPreviewURL(details->previous_url)) {
+        return;
+      }
     }
-
-    // Cloud print sign-in causes a reload.
-    if (!waiting_for_new_preview_page_ &&
-        transition_type == content::PAGE_TRANSITION_RELOAD &&
-        nav_type == content::NAVIGATION_TYPE_EXISTING_PAGE &&
-        IsPrintPreviewURL(details->previous_url)) {
-      return;
-    }
+    NOTREACHED();
+    return;
   }
 
-  NOTREACHED();
+  RemoveInitiator(contents);
 }
 
 WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
@@ -426,8 +428,8 @@
   preview_dialog_map_[preview_dialog] = initiator;
   waiting_for_new_preview_page_ = true;
 
-  AddObservers(initiator, INITIATOR);
-  AddObservers(preview_dialog, PREVIEW_DIALOG);
+  AddObservers(initiator);
+  AddObservers(preview_dialog);
 
   return preview_dialog;
 }
@@ -443,14 +445,11 @@
   }
 }
 
-void PrintPreviewDialogController::AddObservers(WebContents* contents,
-                                                ContentsType contents_type) {
+void PrintPreviewDialogController::AddObservers(WebContents* contents) {
   registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
                  content::Source<WebContents>(contents));
-  if (contents_type == PREVIEW_DIALOG) {
-    registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
-        content::Source<NavigationController>(&contents->GetController()));
-  }
+  registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+      content::Source<NavigationController>(&contents->GetController()));
 
   // Multiple sites may share the same RenderProcessHost, so check if this
   // notification has already been added.
@@ -463,14 +462,11 @@
   }
 }
 
-void PrintPreviewDialogController::RemoveObservers(WebContents* contents,
-                                                   ContentsType contents_type) {
+void PrintPreviewDialogController::RemoveObservers(WebContents* contents) {
   registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
                     content::Source<WebContents>(contents));
-  if (contents_type == PREVIEW_DIALOG) {
-    registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
-        content::Source<NavigationController>(&contents->GetController()));
-  }
+  registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+      content::Source<NavigationController>(&contents->GetController()));
 
   // Multiple sites may share the same RenderProcessHost, so check if this
   // notification has already been added.
@@ -491,7 +487,7 @@
   // and reaches RemovePreviewDialog(), it does not attempt to also remove the
   // initiator's observers.
   preview_dialog_map_[preview_dialog] = NULL;
-  RemoveObservers(initiator, INITIATOR);
+  RemoveObservers(initiator);
 
   PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
 
@@ -507,7 +503,7 @@
   // Remove the initiator's observers before erasing the mapping.
   WebContents* initiator = GetInitiator(preview_dialog);
   if (initiator) {
-    RemoveObservers(initiator, INITIATOR);
+    RemoveObservers(initiator);
     PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
   }
 
@@ -519,7 +515,7 @@
     print_preview_ui->OnPrintPreviewDialogDestroyed();
 
   preview_dialog_map_.erase(preview_dialog);
-  RemoveObservers(preview_dialog, PREVIEW_DIALOG);
+  RemoveObservers(preview_dialog);
 }
 
 }  // namespace printing
diff --git a/chrome/browser/printing/print_preview_dialog_controller.h b/chrome/browser/printing/print_preview_dialog_controller.h
index da5d385..98b2ec8 100644
--- a/chrome/browser/printing/print_preview_dialog_controller.h
+++ b/chrome/browser/printing/print_preview_dialog_controller.h
@@ -1,4 +1,4 @@
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
 
@@ -76,14 +76,7 @@
  private:
   friend class base::RefCounted<PrintPreviewDialogController>;
 
-  // Used to distinguish between the two varieties of WebContents dealt with by
-  // this class.
-  enum ContentsType {
-    INITIATOR,
-    PREVIEW_DIALOG
-  };
-
-  // 1:1 relationship between a print preview dialog and its initiator.
+  // 1:1 relationship between a print preview dialog and its initiator tab.
   // Key: Print preview dialog.
   // Value: Initiator.
   typedef std::map<content::WebContents*, content::WebContents*>
@@ -113,9 +106,8 @@
   void SaveInitiatorTitle(content::WebContents* preview_dialog);
 
   // Adds/Removes observers for notifications from |contents|.
-  void AddObservers(content::WebContents* contents, ContentsType contents_type);
-  void RemoveObservers(content::WebContents* contents,
-                       ContentsType contents_type);
+  void AddObservers(content::WebContents* contents);
+  void RemoveObservers(content::WebContents* contents);
 
   // Removes WebContents when they close/crash/navigate.
   void RemoveInitiator(content::WebContents* initiator);
diff --git a/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc b/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
index c64c3bf..ee9fbb3 100644
--- a/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
+++ b/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
@@ -62,8 +62,8 @@
   }
   virtual ~PrintPreviewDialogClonedObserver() {}
 
-  RequestPrintPreviewObserver* request_preview_tab_observer() {
-    return request_preview_tab_observer_.get();
+  RequestPrintPreviewObserver* request_preview_dialog_observer() {
+    return request_preview_dialog_observer_.get();
   }
 
  private:
@@ -71,11 +71,11 @@
   virtual void DidCloneToNewWebContents(
       WebContents* old_web_contents,
       WebContents* new_web_contents) OVERRIDE {
-    request_preview_tab_observer_.reset(
+    request_preview_dialog_observer_.reset(
         new RequestPrintPreviewObserver(new_web_contents));
   }
 
-  scoped_ptr<RequestPrintPreviewObserver> request_preview_tab_observer_;
+  scoped_ptr<RequestPrintPreviewObserver> request_preview_dialog_observer_;
 
   DISALLOW_COPY_AND_ASSIGN(PrintPreviewDialogClonedObserver);
 };
@@ -112,7 +112,7 @@
 
   void PrintPreview() {
     base::RunLoop run_loop;
-    request_preview_tab_observer()->set_quit_closure(run_loop.QuitClosure());
+    request_preview_dialog_observer()->set_quit_closure(run_loop.QuitClosure());
     chrome::Print(browser());
     run_loop.Run();
   }
@@ -153,8 +153,8 @@
     initiator_ = NULL;
   }
 
-  RequestPrintPreviewObserver* request_preview_tab_observer() {
-    return cloned_tab_observer_->request_preview_tab_observer();
+  RequestPrintPreviewObserver* request_preview_dialog_observer() {
+    return cloned_tab_observer_->request_preview_dialog_observer();
   }
 
   scoped_ptr<PrintPreviewDialogClonedObserver> cloned_tab_observer_;
diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
index b427be9..464b941 100644
--- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc
+++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
@@ -429,6 +429,8 @@
 }
 
 TEST_F(ProfileResetterTest, ResetExtensionsByDisabling) {
+  service_->Init();
+
   base::ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
diff --git a/chrome/browser/profile_resetter/resettable_settings_snapshot.cc b/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
index f6892ba..fa04699 100644
--- a/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
+++ b/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
@@ -121,11 +121,11 @@
   feedback_data->set_category_tag(kProfileResetFeedbackBucket);
   feedback_data->set_description(report);
 
-  feedback_data->set_image(ScreenshotDataPtr());
+  feedback_data->set_image(scoped_ptr<std::string>(new std::string));
   feedback_data->set_profile(profile);
 
   feedback_data->set_page_url("");
   feedback_data->set_user_email("");
 
-  FeedbackUtil::SendReport(feedback_data);
+  feedback_util::SendReport(feedback_data);
 }
diff --git a/chrome/browser/profiles/avatar_menu_model.cc b/chrome/browser/profiles/avatar_menu_model.cc
index de6b904..88db32d 100644
--- a/chrome/browser/profiles/avatar_menu_model.cc
+++ b/chrome/browser/profiles/avatar_menu_model.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -238,27 +239,20 @@
 }
 
 bool AvatarMenuModel::ShouldShowAddNewProfileLink() const {
-#if defined(ENABLE_MANAGED_USERS)
   // |browser_| can be NULL in unit_tests.
-  return !browser_ ||
-      !ManagedUserService::ProfileIsManaged(browser_->profile());
-#endif
-  return true;
+  return !browser_ || !browser_->profile()->IsManaged();
 }
 
 base::string16 AvatarMenuModel::GetManagedUserInformation() const {
-#if defined(ENABLE_MANAGED_USERS)
   // |browser_| can be NULL in unit_tests.
-  if (!browser_)
-    return base::string16();
-
-  ManagedUserService* service = ManagedUserServiceFactory::GetForProfile(
-      browser_->profile());
-  if (service->ProfileIsManaged()) {
+  if (browser_ && browser_->profile()->IsManaged()) {
+#if defined(ENABLE_MANAGED_USERS)
+    ManagedUserService* service = ManagedUserServiceFactory::GetForProfile(
+        browser_->profile());
     base::string16 custodian = UTF8ToUTF16(service->GetCustodianName());
     return l10n_util::GetStringFUTF16(IDS_MANAGED_USER_INFO, custodian);
-  }
 #endif
+  }
   return base::string16();
 }
 
@@ -344,7 +338,7 @@
   size_t index = cache.GetIndexOfProfileWithPath(current_profile->GetPath());
   cache.SetProfileSigninRequiredAtIndex(index, true);
 
-  std::string landing_url = SyncPromoUI::GetSyncLandingURL("close", 1);
+  std::string landing_url = signin::GetLandingURL("close", 1).spec();
   GURL logout_url(GaiaUrls::GetInstance()->service_logout_url() +
                   "?continue=" + landing_url);
   if (!logout_override_.empty()) {
diff --git a/chrome/browser/profiles/avatar_menu_model_browsertest.cc b/chrome/browser/profiles/avatar_menu_model_browsertest.cc
index bf32eb0..cd155fe 100644
--- a/chrome/browser/profiles/avatar_menu_model_browsertest.cc
+++ b/chrome/browser/profiles/avatar_menu_model_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/profiles/avatar_menu_model.h"
 
+#include "base/command_line.h"
 #include "base/path_service.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -12,13 +13,10 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "content/public/test/test_utils.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 namespace {
 
 // An observer that returns back to test code after a new profile is
@@ -61,7 +59,7 @@
 IN_PROC_BROWSER_TEST_F(AvatarMenuModelTest, SwitchToProfile) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/profiles/avatar_menu_model_unittest.cc b/chrome/browser/profiles/avatar_menu_model_unittest.cc
index 0a48e4a..c999931 100644
--- a/chrome/browser/profiles/avatar_menu_model_unittest.cc
+++ b/chrome/browser/profiles/avatar_menu_model_unittest.cc
@@ -63,9 +63,9 @@
   string16 name2(ASCIIToUTF16("Test 2"));
 
   manager()->CreateTestingProfile("p1", scoped_ptr<PrefServiceSyncable>(),
-                                  name1, 0);
+                                  name1, 0, false);
   manager()->CreateTestingProfile("p2", scoped_ptr<PrefServiceSyncable>(),
-                                  name2, 0);
+                                  name2, 0, false);
 
   MockObserver observer;
   EXPECT_EQ(0, observer.change_count());
@@ -89,9 +89,9 @@
   string16 name2(ASCIIToUTF16("Test 2"));
 
   manager()->CreateTestingProfile("p1", scoped_ptr<PrefServiceSyncable>(),
-                                  name1, 0);
+                                  name1, 0, false);
   manager()->CreateTestingProfile("p2", scoped_ptr<PrefServiceSyncable>(),
-                                  name2, 0);
+                                  name2, 0, false);
 
   MockObserver observer;
   AvatarMenuModel model(manager()->profile_info_cache(), &observer, browser());
@@ -107,9 +107,9 @@
   string16 newname1(ASCIIToUTF16("Gamma"));
 
   manager()->CreateTestingProfile("p1", scoped_ptr<PrefServiceSyncable>(),
-                                  name1, 0);
+                                  name1, 0, false);
   manager()->CreateTestingProfile("p2", scoped_ptr<PrefServiceSyncable>(),
-                                  name2, 0);
+                                  name2, 0, false);
 
   MockObserver observer;
   AvatarMenuModel model(manager()->profile_info_cache(), &observer, browser());
@@ -143,9 +143,9 @@
   string16 name2(ASCIIToUTF16("Test 2"));
 
   manager()->CreateTestingProfile("p1", scoped_ptr<PrefServiceSyncable>(),
-                                  name1, 0);
+                                  name1, 0, false);
   manager()->CreateTestingProfile("p2", scoped_ptr<PrefServiceSyncable>(),
-                                  name2, 0);
+                                  name2, 0, false);
 
   MockObserver observer;
   EXPECT_EQ(0, observer.change_count());
@@ -156,7 +156,7 @@
 
   string16 name3(ASCIIToUTF16("Test 3"));
   manager()->CreateTestingProfile("p3", scoped_ptr<PrefServiceSyncable>(),
-                                  name3, 0);
+                                  name3, 0, false);
 
   // Four changes happened via the call to CreateTestingProfile: adding the
   // profile to the cache, setting the user name, rebuilding the list of
@@ -198,7 +198,7 @@
 TEST_F(AvatarMenuModelTest, DontShowAvatarMenu) {
   string16 name1(ASCIIToUTF16("Test 1"));
   manager()->CreateTestingProfile("p1", scoped_ptr<PrefServiceSyncable>(),
-                                  name1, 0);
+                                  name1, 0, false);
 
   EXPECT_FALSE(AvatarMenuModel::ShouldShowAvatarMenu());
 
@@ -209,7 +209,7 @@
 
   string16 name2(ASCIIToUTF16("Test 2"));
   manager()->CreateTestingProfile("p2", scoped_ptr<PrefServiceSyncable>(),
-                                  name2, 0);
+                                  name2, 0, false);
 
   EXPECT_FALSE(AvatarMenuModel::ShouldShowAvatarMenu());
 }
@@ -223,9 +223,9 @@
   string16 name2(ASCIIToUTF16("Test 2"));
 
   manager()->CreateTestingProfile("p1", scoped_ptr<PrefServiceSyncable>(),
-                                  name1, 0);
+                                  name1, 0, false);
   manager()->CreateTestingProfile("p2", scoped_ptr<PrefServiceSyncable>(),
-                                  name2, 0);
+                                  name2, 0, false);
 
 #if defined(OS_CHROMEOS)
   EXPECT_FALSE(AvatarMenuModel::ShouldShowAvatarMenu());
@@ -240,7 +240,7 @@
     return;
 
   manager()->CreateTestingProfile("p1", scoped_ptr<PrefServiceSyncable>(),
-                                  ASCIIToUTF16("Test 1"), 0);
+                                  ASCIIToUTF16("Test 1"), 0, false);
 
   // Add a managed user profile.
   ProfileInfoCache* cache = manager()->profile_info_cache();
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index da71a6b..f27b868 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -46,7 +46,6 @@
 #include "chrome/browser/extensions/api/serial/serial_connection.h"
 #include "chrome/browser/extensions/api/session_restore/session_restore_api.h"
 #include "chrome/browser/extensions/api/socket/socket.h"
-#include "chrome/browser/extensions/api/spellcheck/spellcheck_api.h"
 #include "chrome/browser/extensions/api/streams_private/streams_private_api.h"
 #include "chrome/browser/extensions/api/system_info/system_info_api.h"
 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry_factory.h"
@@ -86,7 +85,6 @@
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/signin/token_service_factory.h"
 #include "chrome/browser/speech/extension_api/tts_extension_api.h"
-#include "chrome/browser/spellchecker/spellcheck_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/thumbnails/thumbnail_service_factory.h"
@@ -102,6 +100,7 @@
 #endif
 
 #if defined(ENABLE_CONFIGURATION_POLICY)
+#include "chrome/browser/policy/cloud/user_cloud_policy_invalidator_factory.h"
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/policy/recommendation_restorer_factory.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h"
@@ -135,6 +134,11 @@
 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
 #endif
 
+#if defined(ENABLE_SPELLCHECK)
+#include "chrome/browser/extensions/api/spellcheck/spellcheck_api.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#endif
+
 namespace chrome {
 
 void AddProfilesExtraParts(ChromeBrowserMainParts* main_parts) {
@@ -236,7 +240,9 @@
   extensions::ProcessesAPI::GetFactoryInstance();
   extensions::PushMessagingAPI::GetFactoryInstance();
   extensions::SessionRestoreAPI::GetFactoryInstance();
+#if defined(ENABLE_SPELLCHECK)
   extensions::SpellcheckAPI::GetFactoryInstance();
+#endif
   extensions::StreamsPrivateAPI::GetFactoryInstance();
   extensions::SystemInfoAPI::GetFactoryInstance();
   extensions::SuggestedLinksRegistryFactory::GetInstance();
@@ -284,6 +290,7 @@
   policy::UserCloudPolicyManagerFactory::GetInstance();
   policy::UserPolicySigninServiceFactory::GetInstance();
 #endif
+  policy::UserCloudPolicyInvalidatorFactory::GetInstance();
 #endif
   predictors::AutocompleteActionPredictorFactory::GetInstance();
   predictors::PredictorDatabaseFactory::GetInstance();
@@ -297,7 +304,9 @@
 #endif
   ShortcutsBackendFactory::GetInstance();
   SigninManagerFactory::GetInstance();
+#if defined(ENABLE_SPELLCHECK)
   SpellcheckServiceFactory::GetInstance();
+#endif
   ThumbnailServiceFactory::GetInstance();
   TabRestoreServiceFactory::GetInstance();
   TemplateURLFetcherFactory::GetInstance();
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc
index a14d3ab..7f42c5d 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.cc
+++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -28,6 +28,8 @@
 #include "chrome/browser/geolocation/chrome_geolocation_permission_context.h"
 #include "chrome/browser/geolocation/chrome_geolocation_permission_context_factory.h"
 #include "chrome/browser/io_thread.h"
+#include "chrome/browser/media/chrome_midi_permission_context.h"
+#include "chrome/browser/media/chrome_midi_permission_context_factory.h"
 #include "chrome/browser/net/pref_proxy_config_tracker.h"
 #include "chrome/browser/net/proxy_service_factory.h"
 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
@@ -117,7 +119,8 @@
   InitHostZoomMap();
 
   // Make the chrome//extension-icon/ resource available.
-  ExtensionIconSource* icon_source = new ExtensionIconSource(profile_);
+  extensions::ExtensionIconSource* icon_source =
+      new extensions::ExtensionIconSource(profile_);
   content::URLDataSource::Add(this, icon_source);
 
 #if defined(ENABLE_PLUGINS)
@@ -230,6 +233,10 @@
   return GetOriginalProfile()->GetExtensionSpecialStoragePolicy();
 }
 
+bool OffTheRecordProfileImpl::IsManaged() {
+  return GetOriginalProfile()->IsManaged();
+}
+
 PrefService* OffTheRecordProfileImpl::GetPrefs() {
   return prefs_;
 }
@@ -239,7 +246,7 @@
 }
 
 DownloadManagerDelegate* OffTheRecordProfileImpl::GetDownloadManagerDelegate() {
-  return DownloadServiceFactory::GetForProfile(this)->
+  return DownloadServiceFactory::GetForBrowserContext(this)->
       GetDownloadManagerDelegate();
 }
 
@@ -286,8 +293,12 @@
       int render_view_id,
       const GURL& requesting_frame,
       const MIDISysExPermissionCallback& callback) {
-  // TODO(toyoshim): Implement. http://crbug.com/257618 .
-  callback.Run(false);
+  ChromeMIDIPermissionContext* context =
+      ChromeMIDIPermissionContextFactory::GetForProfile(this);
+  context->RequestMIDISysExPermission(render_process_id,
+                                      render_view_id,
+                                      requesting_frame,
+                                      callback);
 }
 
 net::URLRequestContextGetter*
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.h b/chrome/browser/profiles/off_the_record_profile_impl.h
index c43ad0a..85fced7 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.h
+++ b/chrome/browser/profiles/off_the_record_profile_impl.h
@@ -40,6 +40,7 @@
   virtual void DestroyOffTheRecordProfile() OVERRIDE;
   virtual bool HasOffTheRecordProfile() OVERRIDE;
   virtual Profile* GetOriginalProfile() OVERRIDE;
+  virtual bool IsManaged() OVERRIDE;
   virtual ExtensionService* GetExtensionService() OVERRIDE;
   virtual ExtensionSpecialStoragePolicy*
       GetExtensionSpecialStoragePolicy() OVERRIDE;
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index 1e5270b..3af9eeb 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -198,6 +198,9 @@
   // profile is not incognito.
   virtual Profile* GetOriginalProfile() = 0;
 
+  // Returns whether the profile is managed (see ManagedUserService).
+  virtual bool IsManaged() = 0;
+
   // Returns a pointer to the TopSites (thumbnail manager) instance
   // for this profile.
   virtual history::TopSites* GetTopSites() = 0;
diff --git a/chrome/browser/profiles/profile_destroyer.cc b/chrome/browser/profiles/profile_destroyer.cc
index 3f810ed..fb16f81 100644
--- a/chrome/browser/profiles/profile_destroyer.cc
+++ b/chrome/browser/profiles/profile_destroyer.cc
@@ -43,7 +43,8 @@
   // RenderProcessHostImpl::Release() avoids destroying RenderProcessHosts in
   // --single-process mode to avoid race conditions.
   DCHECK(hosts.empty() || profile->IsOffTheRecord() ||
-         content::RenderProcessHost::run_renderer_in_process());
+      content::RenderProcessHost::run_renderer_in_process()) << \
+      "Profile still has " << hosts.size() << " hosts";
   // Note that we still test for !profile->IsOffTheRecord here even though we
   // DCHECK'd above because we want to protect Release builds against this even
   // we need to identify if there are leaks when we run Debug builds.
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index b5b0b8d..09be43e 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -44,6 +44,8 @@
 #include "chrome/browser/geolocation/chrome_geolocation_permission_context_factory.h"
 #include "chrome/browser/history/shortcuts_backend.h"
 #include "chrome/browser/history/top_sites.h"
+#include "chrome/browser/media/chrome_midi_permission_context.h"
+#include "chrome/browser/media/chrome_midi_permission_context_factory.h"
 #include "chrome/browser/metrics/metrics_service.h"
 #include "chrome/browser/net/chrome_url_request_context.h"
 #include "chrome/browser/net/net_pref_observer.h"
@@ -699,6 +701,10 @@
   return this;
 }
 
+bool ProfileImpl::IsManaged() {
+  return GetPrefs()->GetBoolean(prefs::kProfileIsManaged);
+}
+
 ExtensionService* ProfileImpl::GetExtensionService() {
   return extensions::ExtensionSystem::Get(this)->extension_service();
 }
@@ -863,8 +869,12 @@
       int render_view_id,
       const GURL& requesting_frame,
       const MIDISysExPermissionCallback& callback) {
-  // TODO(toyoshim): Implement. http://crbug.com/257618 .
-  callback.Run(false);
+  ChromeMIDIPermissionContext* context =
+      ChromeMIDIPermissionContextFactory::GetForProfile(this);
+  context->RequestMIDISysExPermission(render_process_id,
+                                      render_view_id,
+                                      requesting_frame,
+                                      callback);
 }
 
 content::ResourceContext* ProfileImpl::GetResourceContext() {
@@ -908,7 +918,7 @@
 }
 
 DownloadManagerDelegate* ProfileImpl::GetDownloadManagerDelegate() {
-  return DownloadServiceFactory::GetForProfile(this)->
+  return DownloadServiceFactory::GetForBrowserContext(this)->
       GetDownloadManagerDelegate();
 }
 
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h
index 4fb1a51..544091e 100644
--- a/chrome/browser/profiles/profile_impl.h
+++ b/chrome/browser/profiles/profile_impl.h
@@ -94,6 +94,7 @@
   virtual void DestroyOffTheRecordProfile() OVERRIDE;
   virtual bool HasOffTheRecordProfile() OVERRIDE;
   virtual Profile* GetOriginalProfile() OVERRIDE;
+  virtual bool IsManaged() OVERRIDE;
   virtual history::TopSites* GetTopSites() OVERRIDE;
   virtual history::TopSites* GetTopSitesWithoutCreating() OVERRIDE;
   virtual ExtensionService* GetExtensionService() OVERRIDE;
diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc
index dfbc6e1..cc37007 100644
--- a/chrome/browser/profiles/profile_info_cache.cc
+++ b/chrome/browser/profiles/profile_info_cache.cc
@@ -613,6 +613,13 @@
                     OnProfileAvatarChanged(profile_path));
 }
 
+void ProfileInfoCache::SetProfileIsManagedAtIndex(size_t index, bool value) {
+  scoped_ptr<DictionaryValue> info(GetInfoForProfileAtIndex(index)->DeepCopy());
+  info->SetBoolean(kIsManagedKey, value);
+  // This takes ownership of |info|.
+  SetInfoForProfileAtIndex(index, info.release());
+}
+
 void ProfileInfoCache::SetProfileSigninRequiredAtIndex(size_t index,
                                                        bool value) {
   if (value == ProfileIsSigninRequiredAtIndex(index))
diff --git a/chrome/browser/profiles/profile_info_cache.h b/chrome/browser/profiles/profile_info_cache.h
index cf3aabb..93c8511 100644
--- a/chrome/browser/profiles/profile_info_cache.h
+++ b/chrome/browser/profiles/profile_info_cache.h
@@ -89,6 +89,7 @@
   void SetIsUsingGAIANameOfProfileAtIndex(size_t index, bool value);
   void SetGAIAPictureOfProfileAtIndex(size_t index, const gfx::Image* image);
   void SetIsUsingGAIAPictureOfProfileAtIndex(size_t index, bool value);
+  void SetProfileIsManagedAtIndex(size_t index, bool value);
   void SetProfileSigninRequiredAtIndex(size_t index, bool value);
 
   // Returns unique name that can be assigned to a newly created profile.
diff --git a/chrome/browser/profiles/profile_info_cache_unittest.cc b/chrome/browser/profiles/profile_info_cache_unittest.cc
index 2dc49ea..ee0c592 100644
--- a/chrome/browser/profiles/profile_info_cache_unittest.cc
+++ b/chrome/browser/profiles/profile_info_cache_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/prefs/pref_service_syncable.h"
 #include "chrome/browser/profiles/profile_info_cache.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -77,10 +78,7 @@
 }
 
 ProfileInfoCacheTest::ProfileInfoCacheTest()
-    : testing_profile_manager_(
-        TestingBrowserProcess::GetGlobal()),
-      ui_thread_(BrowserThread::UI, &ui_loop_),
-      file_thread_(BrowserThread::FILE, &ui_loop_),
+    : testing_profile_manager_(TestingBrowserProcess::GetGlobal()),
       name_observer_(&testing_profile_manager_) {
 }
 
@@ -95,7 +93,7 @@
 void ProfileInfoCacheTest::TearDown() {
   // Drain the UI thread to make sure all tasks are completed. This prevents
   // memory leaks.
-  ui_loop_.RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
 }
 
 ProfileInfoCache* ProfileInfoCacheTest::GetCache() {
@@ -125,9 +123,10 @@
     const SkBitmap* icon = rb.GetImageNamed(
         ProfileInfoCache::GetDefaultAvatarIconResourceIDAtIndex(
             i)).ToSkBitmap();
+    bool is_managed = i == 3;
 
     GetCache()->AddProfileToCache(profile_path, profile_name, string16(), i,
-                                  false);
+                                  is_managed);
     GetCache()->SetBackgroundStatusOfProfileAtIndex(i, true);
     string16 gaia_name = ASCIIToUTF16(base::StringPrintf("gaia_%ud", i));
     GetCache()->SetGAIANameOfProfileAtIndex(i, gaia_name);
@@ -135,10 +134,11 @@
     EXPECT_EQ(i + 1, GetCache()->GetNumberOfProfiles());
     EXPECT_EQ(profile_name, GetCache()->GetNameOfProfileAtIndex(i));
     EXPECT_EQ(profile_path, GetCache()->GetPathOfProfileAtIndex(i));
-    const SkBitmap* actual_icon = GetCache()->GetAvatarIconOfProfileAtIndex(
-        i).ToSkBitmap();
+    const SkBitmap* actual_icon =
+        GetCache()->GetAvatarIconOfProfileAtIndex(i).ToSkBitmap();
     EXPECT_EQ(icon->width(), actual_icon->width());
     EXPECT_EQ(icon->height(), actual_icon->height());
+    EXPECT_EQ(is_managed, GetCache()->ProfileIsManagedAtIndex(i));
   }
 
   // Reset the cache and test the it reloads correctly.
@@ -394,6 +394,21 @@
     gaia_image, *GetCache()->GetGAIAPictureOfProfileAtIndex(0)));
 }
 
+TEST_F(ProfileInfoCacheTest, SetProfileIsManaged) {
+  GetCache()->AddProfileToCache(
+      GetProfilePath("test"), ASCIIToUTF16("Test"), string16(), 0, false);
+  EXPECT_FALSE(GetCache()->ProfileIsManagedAtIndex(0));
+
+  GetCache()->SetProfileIsManagedAtIndex(0, true);
+  EXPECT_TRUE(GetCache()->ProfileIsManagedAtIndex(0));
+
+  ResetCache();
+  EXPECT_TRUE(GetCache()->ProfileIsManagedAtIndex(0));
+
+  GetCache()->SetProfileIsManagedAtIndex(0, false);
+  EXPECT_FALSE(GetCache()->ProfileIsManagedAtIndex(0));
+}
+
 TEST_F(ProfileInfoCacheTest, EmptyGAIAInfo) {
   string16 profile_name = ASCIIToUTF16("name_1");
   int id = ProfileInfoCache::GetDefaultAvatarIconResourceIDAtIndex(0);
@@ -415,4 +430,16 @@
       profile_image, GetCache()->GetAvatarIconOfProfileAtIndex(0)));
 }
 
+TEST_F(ProfileInfoCacheTest, CreateManagedTestingProfile) {
+  testing_profile_manager_.CreateTestingProfile("default");
+  string16 managed_user_name = ASCIIToUTF16("Supervised User");
+  testing_profile_manager_.CreateTestingProfile(
+      "test1", scoped_ptr<PrefServiceSyncable>(), managed_user_name, 0, true);
+  for (size_t i = 0; i < GetCache()->GetNumberOfProfiles(); i++) {
+    bool is_managed =
+        GetCache()->GetNameOfProfileAtIndex(i) == managed_user_name;
+    EXPECT_EQ(is_managed, GetCache()->ProfileIsManagedAtIndex(i));
+  }
+}
+
 }  // namespace
diff --git a/chrome/browser/profiles/profile_info_cache_unittest.h b/chrome/browser/profiles/profile_info_cache_unittest.h
index 7bddb77..2a16395 100644
--- a/chrome/browser/profiles/profile_info_cache_unittest.h
+++ b/chrome/browser/profiles/profile_info_cache_unittest.h
@@ -7,10 +7,9 @@
 
 #include <set>
 
-#include "base/message_loop/message_loop.h"
 #include "chrome/browser/profiles/profile_info_cache_observer.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "content/public/test/test_browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class ProfileInfoCache;
@@ -62,9 +61,7 @@
   TestingProfileManager testing_profile_manager_;
 
  private:
-  base::MessageLoopForUI ui_loop_;
-  content::TestBrowserThread ui_thread_;
-  content::TestBrowserThread file_thread_;
+  content::TestBrowserThreadBundle thread_bundle_;
   ProfileNameVerifierObserver name_observer_;
 };
 
diff --git a/chrome/browser/profiles/profile_manager_browsertest.cc b/chrome/browser/profiles/profile_manager_browsertest.cc
index 7ca01e7..84c7279 100644
--- a/chrome/browser/profiles/profile_manager_browsertest.cc
+++ b/chrome/browser/profiles/profile_manager_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile_info_cache.h"
@@ -15,13 +16,10 @@
 #include "chrome/browser/ui/host_desktop.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/ui_test_utils.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 namespace {
 
 // An observer that returns back to test code after a new profile is
@@ -191,7 +189,7 @@
                        SwitchToProfile) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/profiles/profile_metrics.cc b/chrome/browser/profiles/profile_metrics.cc
index 6087a95..5e743ad 100644
--- a/chrome/browser/profiles/profile_metrics.cc
+++ b/chrome/browser/profiles/profile_metrics.cc
@@ -8,7 +8,6 @@
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/managed_mode/managed_user_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_info_cache.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -232,7 +231,7 @@
                             GetProfileType(profile_path),
                             NUM_PROFILE_TYPE_METRICS);
 
-  if (ManagedUserService::ProfileIsManaged(profile)) {
+  if (profile->IsManaged()) {
     content::RecordAction(
         content::UserMetricsAction("ManagedMode_NewManagedUserWindow"));
   }
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index 7ca57b5..0f40752 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -9,11 +9,15 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/metrics/histogram.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/automation/automation_resource_message_filter.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/cookie_settings.h"
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
+#include "chrome/browser/extensions/activity_log/activity_action_constants.h"
+#include "chrome/browser/extensions/activity_log/activity_actions.h"
 #include "chrome/browser/extensions/activity_log/activity_log.h"
+#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
 #include "chrome/browser/extensions/api/messaging/message_service.h"
 #include "chrome/browser/extensions/event_router.h"
 #include "chrome/browser/extensions/extension_process_manager.h"
@@ -37,85 +41,31 @@
 
 using content::BrowserThread;
 using extensions::APIPermission;
+using extensions::api::activity_log_private::BlockedChromeActivityDetail;
 using WebKit::WebCache;
 
 namespace {
 
-enum ActivityLogCallType {
-  ACTIVITYAPI,
-  ACTIVITYEVENT
-};
-
-void AddAPIActionToExtensionActivityLog(
+// Logs an action to the extension activity log for the specified profile.  Can
+// be called from any thread.
+void AddActionToExtensionActivityLog(
     Profile* profile,
-    const ActivityLogCallType call_type,
-    const std::string& extension_id,
-    const std::string& api_call,
-    scoped_ptr<ListValue> args,
-    const std::string& extra) {
+    scoped_refptr<extensions::Action> action) {
   // The ActivityLog can only be accessed from the main (UI) thread.  If we're
   // running on the wrong thread, re-dispatch from the main thread.
   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
-        base::Bind(&AddAPIActionToExtensionActivityLog, profile, call_type,
-                   extension_id, api_call, base::Passed(&args), extra));
+        base::Bind(&AddActionToExtensionActivityLog, profile, action));
   } else {
+    // If the action included a URL, check whether it is for an incognito
+    // profile.  The check is performed here so that it can safely be done from
+    // the UI thread.
+    if (action->page_url().is_valid() || !action->page_title().empty())
+      action->set_page_incognito(profile->IsOffTheRecord());
     extensions::ActivityLog* activity_log =
         extensions::ActivityLog::GetInstance(profile);
-    if (activity_log->IsLogEnabled()) {
-      if (call_type == ACTIVITYAPI)
-        activity_log->LogAPIAction(extension_id, api_call, args.get(), extra);
-      else if (call_type == ACTIVITYEVENT)
-        activity_log->LogEventAction(extension_id, api_call, args.get(), extra);
-    }
-  }
-}
-
-void AddBlockedActionToExtensionActivityLog(
-    Profile* profile,
-    const std::string& extension_id,
-    const std::string& api_call) {
-  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::Bind(&AddBlockedActionToExtensionActivityLog, profile,
-                   extension_id, api_call));
-  } else {
-    extensions::ActivityLog* activity_log =
-        extensions::ActivityLog::GetInstance(profile);
-    if (activity_log->IsLogEnabled()) {
-      scoped_ptr<ListValue> empty_args(new ListValue());
-      activity_log->LogBlockedAction(extension_id, api_call, empty_args.get(),
-                                     extensions::BlockedAction::ACCESS_DENIED,
-                                     std::string());
-    }
-  }
-}
-
-void AddDOMActionToExtensionActivityLog(
-    Profile* profile,
-    const std::string& extension_id,
-    const GURL& url,
-    const string16& url_title,
-    const std::string& api_call,
-    scoped_ptr<ListValue> args,
-    const int call_type) {
-  // The ActivityLog can only be accessed from the main (UI) thread.  If we're
-  // running on the wrong thread, re-dispatch from the main thread.
-  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::Bind(&AddDOMActionToExtensionActivityLog, profile, extension_id,
-                   url, url_title, api_call, base::Passed(&args), call_type));
-  } else {
-    extensions::ActivityLog* activity_log =
-        extensions::ActivityLog::GetInstance(profile);
-    if (activity_log->IsLogEnabled())
-      activity_log->LogDOMAction(
-          extension_id, url, url_title, api_call, args.get(),
-          static_cast<extensions::DomActionType::Type>(call_type),
-          std::string());
+    activity_log->LogAction(action);
   }
 }
 
@@ -249,7 +199,8 @@
 
 void ChromeRenderMessageFilter::OnPreconnect(const GURL& url) {
   if (profile_->GetNetworkPredictor())
-    profile_->GetNetworkPredictor()->PreconnectUrlAndSubresources(url, GURL());
+    profile_->GetNetworkPredictor()->PreconnectUrl(
+        url, GURL(), chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED, 1);
 }
 
 void ChromeRenderMessageFilter::OnResourceTypeStats(
@@ -557,40 +508,56 @@
 void ChromeRenderMessageFilter::OnAddAPIActionToExtensionActivityLog(
     const std::string& extension_id,
     const ExtensionHostMsg_APIActionOrEvent_Params& params) {
-  scoped_ptr<ListValue> args(params.arguments.DeepCopy());
-  // The activity is recorded as an API action in the extension
-  // activity log.
-  AddAPIActionToExtensionActivityLog(profile_, ACTIVITYAPI, extension_id,
-                                     params.api_call, args.Pass(),
-                                     params.extra);
+  scoped_refptr<extensions::Action> action = new extensions::Action(
+      extension_id, base::Time::Now(), extensions::Action::ACTION_API_CALL,
+      params.api_call);
+  action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
+  if (!params.extra.empty()) {
+    action->mutable_other()->SetString(
+        activity_log_constants::kActionExtra, params.extra);
+  }
+  AddActionToExtensionActivityLog(profile_, action);
 }
 
 void ChromeRenderMessageFilter::OnAddDOMActionToExtensionActivityLog(
     const std::string& extension_id,
     const ExtensionHostMsg_DOMAction_Params& params) {
-  scoped_ptr<ListValue> args(params.arguments.DeepCopy());
-  // The activity is recorded as a DOM action on the extension
-  // activity log.
-  AddDOMActionToExtensionActivityLog(profile_, extension_id, params.url,
-                                     params.url_title, params.api_call,
-                                     args.Pass(), params.call_type);
+  scoped_refptr<extensions::Action> action = new extensions::Action(
+      extension_id, base::Time::Now(), extensions::Action::ACTION_DOM_ACCESS,
+      params.api_call);
+  action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
+  action->set_page_url(params.url);
+  action->set_page_title(base::UTF16ToUTF8(params.url_title));
+  action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
+                                      params.call_type);
+  AddActionToExtensionActivityLog(profile_, action);
 }
 
 void ChromeRenderMessageFilter::OnAddEventToExtensionActivityLog(
     const std::string& extension_id,
     const ExtensionHostMsg_APIActionOrEvent_Params& params) {
-  scoped_ptr<ListValue> args(params.arguments.DeepCopy());
-  // The activity is recorded as an event in the extension
-  // activity log.
-  AddAPIActionToExtensionActivityLog(profile_, ACTIVITYEVENT, extension_id,
-                                     params.api_call, args.Pass(),
-                                     params.extra);
+  scoped_refptr<extensions::Action> action = new extensions::Action(
+      extension_id, base::Time::Now(), extensions::Action::ACTION_API_EVENT,
+      params.api_call);
+  action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
+  if (!params.extra.empty()) {
+    action->mutable_other()->SetString(activity_log_constants::kActionExtra,
+                                       params.extra);
+  }
+  AddActionToExtensionActivityLog(profile_, action);
 }
 
 void ChromeRenderMessageFilter::OnAddBlockedCallToExtensionActivityLog(
     const std::string& extension_id,
     const std::string& function_name) {
-  AddBlockedActionToExtensionActivityLog(profile_, extension_id, function_name);
+  scoped_refptr<extensions::Action> action = new extensions::Action(
+      extension_id, base::Time::Now(), extensions::Action::ACTION_API_BLOCKED,
+      function_name);
+  action->mutable_other()->SetString(
+      activity_log_constants::kActionBlockedReason,
+      BlockedChromeActivityDetail::ToString(
+          BlockedChromeActivityDetail::REASON_ACCESS_DENIED));
+  AddActionToExtensionActivityLog(profile_, action);
 }
 
 void ChromeRenderMessageFilter::OnAllowDatabase(int render_view_id,
diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc
index bda101c..b499b21 100644
--- a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc
+++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc
@@ -4,12 +4,20 @@
 
 #include "chrome/browser/renderer_host/chrome_render_view_host_observer.h"
 
+#include <vector>
+
 #include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/net/predictor.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/search_terms_data.h"
+#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/search_engines/template_url_service.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_messages.h"
@@ -18,18 +26,27 @@
 #include "chrome/common/url_constants.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/page_navigator.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/page_transition_types.h"
 #include "extensions/common/constants.h"
+#include "net/http/http_request_headers.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/window_open_disposition.h"
+#include "ui/gfx/codec/jpeg_codec.h"
 
 #if defined(OS_WIN)
 #include "base/win/win_util.h"
 #endif  // OS_WIN
 
 using content::ChildProcessSecurityPolicy;
+using content::OpenURLParams;
 using content::RenderViewHost;
 using content::SiteInstance;
+using content::WebContents;
 using extensions::Extension;
 using extensions::Manifest;
 
@@ -75,6 +92,8 @@
   IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewHostObserver, message)
     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FocusedNodeTouched,
                         OnFocusedNodeTouched)
+    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RequestThumbnailForContextNode_ACK,
+                        OnRequestThumbnailForContextNodeACK)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
@@ -182,3 +201,55 @@
 #endif
   }
 }
+
+// Handles the image thumbnail for the context node, composes a image search
+// request based on the received thumbnail and opens the request in a new tab.
+void ChromeRenderViewHostObserver::OnRequestThumbnailForContextNodeACK(
+    const SkBitmap& bitmap) {
+  const int kDefaultQualityForImageSearch = 90;
+  WebContents* web_contents =
+      WebContents::FromRenderViewHost(render_view_host());
+  if (!web_contents)
+    return;
+
+  std::vector<unsigned char> data;
+  if (!gfx::JPEGCodec::Encode(
+      reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
+      gfx::JPEGCodec::FORMAT_SkBitmap, bitmap.width(), bitmap.height(),
+      static_cast<int>(bitmap.rowBytes()), kDefaultQualityForImageSearch,
+      &data))
+    return;
+
+  const TemplateURL* const default_provider =
+      TemplateURLServiceFactory::GetForProfile(profile_)->
+          GetDefaultSearchProvider();
+  DCHECK(default_provider);
+  TemplateURLRef::SearchTermsArgs search_args =
+      TemplateURLRef::SearchTermsArgs(base::string16());
+  search_args.image_thumbnail_content = std::string(data.begin(),
+                                                    data.end());
+  // TODO(jnd): Add a method in WebContentsViewDelegate to get the image URL
+  // from the ContextMenuParams which creates current context menu.
+  search_args.image_url = GURL();
+  TemplateURLRef::PostContent post_content;
+  GURL result(default_provider->image_url_ref().ReplaceSearchTerms(
+      search_args, &post_content));
+  if (!result.is_valid())
+    return;
+
+  OpenURLParams open_url_params(result, content::Referrer(), NEW_FOREGROUND_TAB,
+                                content::PAGE_TRANSITION_LINK, false);
+  const std::string& content_type = post_content.first;
+  std::string& post_data = post_content.second;
+  if (!post_data.empty()) {
+    DCHECK(!content_type.empty());
+    open_url_params.uses_post = true;
+    open_url_params.browser_initiated_post_data =
+        base::RefCountedString::TakeString(&post_data);
+    open_url_params.extra_headers += base::StringPrintf(
+        "%s: %s\r\n", net::HttpRequestHeaders::kContentType,
+        content_type.c_str());
+  }
+
+  web_contents->OpenURL(open_url_params);
+}
diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.h b/chrome/browser/renderer_host/chrome_render_view_host_observer.h
index af6a699..0ce4c4b 100644
--- a/chrome/browser/renderer_host/chrome_render_view_host_observer.h
+++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.h
@@ -5,9 +5,12 @@
 #ifndef CHROME_BROWSER_RENDERER_HOST_CHROME_RENDER_VIEW_HOST_OBSERVER_H_
 #define CHROME_BROWSER_RENDERER_HOST_CHROME_RENDER_VIEW_HOST_OBSERVER_H_
 
+#include <string>
+
 #include "content/public/browser/render_view_host_observer.h"
 
 class Profile;
+class SkBitmap;
 
 namespace chrome_browser_net {
 class Predictor;
@@ -32,15 +35,18 @@
   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
 
  private:
-   // Does extension-specific initialization when a new renderer process is
-   // created by a RenderViewHost.
+  // Does extension-specific initialization when a new renderer process is
+  // created by a RenderViewHost.
   void InitRenderViewForExtensions();
   // Gets the extension or app (if any) that is associated with the RVH.
   const extensions::Extension* GetExtension();
   // Cleans up when a RenderViewHost is removed, or on destruction.
   void RemoveRenderViewHostForExtensions(content::RenderViewHost* rvh);
+
   void OnFocusedNodeTouched(bool editable);
 
+  void OnRequestThumbnailForContextNodeACK(const SkBitmap& bitmap);
+
   Profile* profile_;
   chrome_browser_net::Predictor* predictor_;
 
diff --git a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc
index 556eb26..1f91bf5 100644
--- a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc
@@ -230,7 +230,7 @@
           child_id, route_id, prerender::FINAL_STATUS_INVALID_HTTP_METHOD);
       return false;
     }
-    if (!prerender::PrerenderManager::DoesURLHaveValidScheme(url)) {
+    if (!prerender::PrerenderManager::DoesSubresourceURLHaveValidScheme(url)) {
       ReportUnsupportedPrerenderScheme(url);
       prerender_tracker_->TryCancelOnIOThread(
           child_id, route_id, prerender::FINAL_STATUS_UNSUPPORTED_SCHEME);
diff --git a/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc b/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
index 4c13452..b7ab2f0 100644
--- a/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
+++ b/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
@@ -12,6 +12,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_process_host.h"
@@ -19,10 +20,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::RenderViewHost;
 using content::RenderWidgetHost;
 using content::WebContents;
@@ -294,7 +291,7 @@
                        DevToolsOnSelfInOwnProcessPPT) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -337,7 +334,7 @@
                        DevToolsOnSelfInOwnProcess) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/repost_form_warning_controller.cc b/chrome/browser/repost_form_warning_controller.cc
index e665b29..c7f0c08 100644
--- a/chrome/browser/repost_form_warning_controller.cc
+++ b/chrome/browser/repost_form_warning_controller.cc
@@ -45,15 +45,17 @@
 #endif  // defined(TOOLKIT_GTK)
 
 void RepostFormWarningController::OnAccepted() {
-  operations_delegate()->SetPreventCloseOnLoadStart(true);
   web_contents()->GetController().ContinuePendingReload();
-  operations_delegate()->SetPreventCloseOnLoadStart(false);
 }
 
 void RepostFormWarningController::OnCanceled() {
   web_contents()->GetController().CancelPendingReload();
 }
 
+void RepostFormWarningController::OnClosed() {
+  web_contents()->GetController().CancelPendingReload();
+}
+
 void RepostFormWarningController::BeforeFormRepostWarningShow() {
   // Close the dialog if we show an additional dialog, to avoid them
   // stacking up.
diff --git a/chrome/browser/repost_form_warning_controller.h b/chrome/browser/repost_form_warning_controller.h
index c6bf034..1b2cc9b 100644
--- a/chrome/browser/repost_form_warning_controller.h
+++ b/chrome/browser/repost_form_warning_controller.h
@@ -29,6 +29,7 @@
 #endif  // defined(TOOLKIT_GTK)
   virtual void OnAccepted() OVERRIDE;
   virtual void OnCanceled() OVERRIDE;
+  virtual void OnClosed() OVERRIDE;
 
   // content::WebContentsObserver methods:
   virtual void BeforeFormRepostWarningShow() OVERRIDE;
diff --git a/chrome/browser/resources/apps_debugger/css/items.css b/chrome/browser/resources/apps_debugger/css/items.css
index 1090c82..7889398 100644
--- a/chrome/browser/resources/apps_debugger/css/items.css
+++ b/chrome/browser/resources/apps_debugger/css/items.css
@@ -5,18 +5,20 @@
 body,
 html {
   color: rgb(48, 57, 66);
-  font-family: Arial, sans-serif;
-  font-size: 90%;
+  font-family: Ubuntu, Arial, sans-serif;
   height: 100%;
+  overflow: hidden;
   width: 100%;
 }
 
-html {
-  overflow: hidden;
+body {
+  font-size: 75%;
 }
 
-body {
+#container {
+  height: 100%;
   overflow-y: scroll;
+  width: 100%;
 }
 
 hr {
@@ -29,19 +31,22 @@
 #tabs-header-container {
   border-bottom: 1px solid #e0e0e0;
   height: calc(100% - 53px);
+  margin-top: 10px;
 }
 
 #tabs {
   border-bottom: none;
+  left: -5px;
   margin: 5px auto 0;
   padding: 0 5px;
-  width: 710px;
+  position: relative;
+  width: 720px;
 }
 
 tabs tab {
   border-color: #e0e0e0;
-  min-width: 150px;
-  padding: 5px 20px;
+  min-width: 100px;
+  padding: 7px 20px;
 }
 
 tabs > [selected] {
@@ -72,14 +77,16 @@
 
 tabpanel {
   margin: 0 auto;
-  padding-top: 80px;
+  padding-top: 24px;
   width: 710px;
 }
 
 /* Header */
 
 #header {
-  background-color: rgba(255, 255, 255, 0.9);
+  background-image: -webkit-linear-gradient(white,
+                                            white 40%,
+                                            rgba(255, 255, 255, 0.92));
   position: fixed;
   top: 0;
   width: 100%;
@@ -103,21 +110,23 @@
 
 #header-bottom-gradient {
   background: linear-gradient(to bottom, white, rgba(255, 255, 255, 0));
-  height: 30px;
+  height: 20px;
   position: fixed;
-  top: 80px;
+  top: 42px;
   width: 100%;
 }
 
 #search {
-  float: right;
-  margin-top: 7px;
   padding: 4px;
+  position: absolute;
+  right: 15px;
+  text-align: left;
   width: 200px;
 }
 
 html[dir='rtl'] #search {
-  float: left;
+  left: 15px;
+  right: auto;
 }
 
 /* Contents */
@@ -125,28 +134,56 @@
 #extension-settings {
   margin: 0;
   max-width: 100%;
-  padding: 10px 0;
+  padding: 0 0 10px;
 }
 
 #no-extensions-message,
-#no-apps-message,
-#no-unpacked-message {
+#no-apps-message {
   font-weight: bold;
 }
 
+.update-items-container,
+.load-unpacked {
+  float: right;
+}
+
+html[dir='rtl'] .update-items-container,
+html[dir='rtl'] .load-unpacked {
+  float: left;
+}
+
+.update-items-progress.updating ~ .update-items-now,
+.update-items-progress {
+  display: none;
+}
+
+.update-items-progress ~ .update-items-now,
+.update-items-progress.updating {
+  display: inline;
+}
+
+.packed-list:not(.empty-item-list) #no-packed-extensions,
+.unpacked-list:not(.empty-item-list) #no-unpacked-extensions,
+.packed-list:not(.empty-item-list) #no-packed-apps,
+.unpacked-list:not(.empty-item-list) #no-unpacked-apps,
+.loading #no-packed-extensions,
+.loading #no-unpacked-extensions,
+.loading #no-packed-apps,
+.loading #no-unpacked-apps {
+  display: none;
+}
+
 .empty-item-list {
-  height: 3em;
+  height: 6em;
 }
 
 #no-extensions,
-#no-apps,
-#no-unpacked {
+#no-apps {
   margin: 10px;
 }
 
-#packed-app-list,
-#unpacked-list,
-#packed-extension-list {
+.packed-list,
+.unpacked-list {
   margin-top: 30px;
 }
 
@@ -158,17 +195,24 @@
 }
 
 .list-header {
+  border-bottom: 1px solid #e0e0e0;
   font-weight: bold;
-  margin-bottom: 20px;
+  margin-bottom: 15px;
+  padding-bottom: 15px;
 }
 
-#packed-list.empty-item-list .list-header,
-#unpacked-list.empty-item-list .list-header {
-  display: none;
+.list-header .title {
+  display: inline-block;
+  margin-top: 5px;
 }
 
 .extension-list-item-wrapper {
-  padding: 0 0 15px;
+  padding: 0 0 25px;
+}
+
+.extension-list-item-wrapper.highlighted {
+  background-color: rgba(255, 255, 128, 255);
+  transition: background-color 500ms;
 }
 
 .extension-list-item {
@@ -184,9 +228,16 @@
 
 .extension-title {
   -webkit-padding-end: 20px;
+  -webkit-user-select: text;
   color: rgb(48, 57, 66);
   font-size: 14px;
   font-weight: 500;
+  margin-bottom: 5px;
+  text-decoration: none;
+}
+
+.extension-title:hover {
+  text-decoration: underline;
 }
 
 /**
@@ -199,12 +250,14 @@
 
 .extension-version {
   -webkit-padding-end: 7px;
+  -webkit-user-select: text;
   font-size: 13px;
   font-weight: 400;
 }
 
 .extension-description {
   -webkit-padding-end: 5px;
+  -webkit-user-select: text;
   font-size: 13px;
   margin: 5px 0;
   white-space: normal;
@@ -212,7 +265,9 @@
 
 .extension-details {
   -webkit-box-flex: 1;
+  -webkit-margin-start: 8px;
   -webkit-padding-start: 55px;
+  margin-top: 15px;
   max-width: 600px;
 }
 
@@ -223,10 +278,6 @@
   color: rgb(151, 156, 160);
 }
 
-.site-link {
-  font-size: 90%;
-}
-
 html[dir='rtl'] .enable-control {
   float: left;
 }
@@ -284,144 +335,12 @@
   -webkit-margin-start: 0;
 }
 
-.extension-run-button,
-.extension-restart-button,
-.extension-show-logs-button,
-.extension-more-details-button {
-  opacity: 0;
-  transition: 250ms opacity ease-in;
+.extension-details-summary {
+  margin-bottom: 10px;
 }
 
-.extension-list-item-wrapper:hover .extension-run-button,
-.extension-list-item-wrapper:hover .extension-restart-button,
-.extension-list-item-wrapper:hover .extension-show-logs-button,
-.extension-list-item-wrapper:hover .extension-more-details-button {
-  opacity: 1;
-  transition-duration: 100ms;
-}
-
-.extension-details.expanded .extension-run-button,
-.extension-details.expanded .extension-restart-button,
-.extension-details.expanded .extension-show-logs-button,
-.extension-details.expanded .extension-more-details-button {
-  opacity: 1;
-}
-
-.extension-run-button,
-.extension-restart-button {
-  background-position: center;
-  background-repeat: no-repeat;
-  border: 1px solid transparent;
-  border-radius: 5px;
-  display: inline-block;
-  font-size: 12px;
-  height: 16px;
-  padding: 5px;
-  width: 16px;
-}
-
-.extension-run-button:hover,
-.extension-restart-button:hover {
-  border-color: #ddd;
-}
-
-.extension-run-button:active,
-.extension-restart-button:active {
-  background-color: #f8f8f8;
-  border-color: #ddd;
-}
-
-.extension-run-button {
-  background-image: url(../images/run.png);
-}
-
-.extension-restart-button {
-  background-image: url(../images/restart.png);
-}
-
-.extension-show-logs-button,
-.extension-more-details-button {
-  color: #aaa;
-  cursor: pointer;
-  display: inline-block;
-  font-size: 12px;
-  text-decoration: underline;
-}
-
-.extension-show-logs-button {
-  position: relative;
-  top: -9px;
-}
-
-.extension-more-details-button {
-  float: right;
-  padding: 8px 0;
-}
-
-html[dir='rtl'] .extension-more-details-button {
-  float: left;
-}
-
-.extension-details-all {
-  height: 0;
-  margin-top: -5px;
-  opacity: 0;
-  overflow: hidden;
-  transition: height 250ms ease-in, opacity 250ms ease-in;
-}
-
-.extension-details.expanded .extension-details-all {
-  opacity: 1;
-}
-
-.extension-details-all-container {
-  position: relative;
-}
-
-.extension-details-all-arrow {
-  border: 9px solid transparent;
-  border-bottom-color: #f8f8f8;
-  height: 0;
-  position: absolute;
-  right: 21px;
-  top: -17px;
-  width: 0;
-}
-
-html[dir='rtl'] .extension-details-all-arrow {
-  left: 21px;
-  right: auto;
-}
-
-.extension-details-all-arrow-border {
-  border: 10px solid transparent;
-  border-bottom-color: #ddd;
-  height: 0;
-  position: absolute;
-  right: 20px;
-  top: -19px;
-  width: 0;
-}
-
-html[dir='rtl'] .extension-details-all-arrow-border {
-  left: 20px;
-  right: auto;
-}
-
-.extension-details-all-bubble {
-  background-color: #f8f8f8;
-  border: 1px solid #ddd;
-  border-radius: 5px;
-  margin-top: 10px;
-  padding: 0 10px;
-}
-
-.extension-details-all-bubble > div {
-  margin: 10px 0;
-}
-
-.delete-link {
-  float: right;
+.extension-details-all div {
+  margin: 5px 0;
 }
 
 html[dir='rtl'] .delete-link {
diff --git a/chrome/browser/resources/apps_debugger/js/items.js b/chrome/browser/resources/apps_debugger/js/items.js
index 278b692..aaa1ea7 100644
--- a/chrome/browser/resources/apps_debugger/js/items.js
+++ b/chrome/browser/resources/apps_debugger/js/items.js
@@ -22,10 +22,16 @@
       cr.ui.decorate('tabbox', cr.ui.TabBox);
 
       // Set up the three buttons (load unpacked, pack and update).
-      $('load-unpacked').addEventListener('click',
-          this.handleLoadUnpackedItem_.bind(this));
-      $('update-items-now').addEventListener('click',
-          this.handleUpdateItemNow_.bind(this));
+      document.querySelector('#apps-tab .load-unpacked').
+          addEventListener('click', this.handleLoadUnpackedItem_.bind(this));
+      document.querySelector('#extensions-tab .load-unpacked').
+          addEventListener('click', this.handleLoadUnpackedItem_.bind(this));
+      document.querySelector('#apps-tab .update-items-now').
+          addEventListener('click', this.handleUpdateItemNow_.bind(this,
+          document.querySelector('#apps-tab .update-items-progress')));
+      document.querySelector('#extensions-tab .update-items-now').
+          addEventListener('click', this.handleUpdateItemNow_.bind(this,
+          document.querySelector('#extensions-tab .update-items-progress')));
       var packItemOverlay =
           apps_dev_tool.PackItemOverlay.getInstance().initializePage();
 
@@ -37,19 +43,25 @@
      * @param {!Event} e Click event.
      * @private
      */
-    handleLoadUnpackedItem_: function(e) {
-      chrome.developerPrivate.loadUnpacked(function(success) {
-        apps_dev_tool.ItemsList.loadItemsInfo();
-      });
+   handleLoadUnpackedItem_: function(e) {
+      chrome.developerPrivate.loadUnpacked();
     },
 
     /**
      * Handles the Update Extension Now Button.
+     * @param {!Element} tabNode Element containing the progress label.
      * @param {!Event} e Click event.
      * @private
      */
-    handleUpdateItemNow_: function(e) {
-      chrome.developerPrivate.autoUpdate(function(response) {});
+    handleUpdateItemNow_: function(progressLabelNode, e) {
+      progressLabelNode.classList.add('updating');
+      chrome.developerPrivate.autoUpdate(function(response) {
+        // autoUpdate() will run too fast. We wait for 2 sec
+        // before hiding the label so that the user can see it.
+        setTimeout(function() {
+          progressLabelNode.classList.remove('updating');
+        }, 2000);
+      });
     },
   };
 
diff --git a/chrome/browser/resources/apps_debugger/js/items_list.js b/chrome/browser/resources/apps_debugger/js/items_list.js
index ba843fc..999ef37 100644
--- a/chrome/browser/resources/apps_debugger/js/items_list.js
+++ b/chrome/browser/resources/apps_debugger/js/items_list.js
@@ -20,6 +20,13 @@
   // The list of all unpacked extensions.
   var unpackedExtensionList = [];
 
+  // Highlight animation is in progress in apps or extensions list.
+  var animating = false;
+
+  // If an update of the list of apps/extensions happened while animation was
+  // in progress, we'll need to update the list at the end of the animation.
+  var needsReloadAppDisplay = false;
+
   /** const*/ var AppsDevTool = apps_dev_tool.AppsDevTool;
 
   /**
@@ -67,7 +74,8 @@
 
   /**
    * Applies the given |filter| to the items list.
-   * @param {string} filter Curent string in the search box.
+   * @param {string} filter Regular expression to that will be used to
+   *     match the name of the items we want to show.
    */
   function rebuildAppList(filter) {
     packedAppList = [];
@@ -77,7 +85,7 @@
 
     for (var i = 0; i < completeList.length; i++) {
       var item = completeList[i];
-      if (filter && item.name.toLowerCase().search(filter.toLowerCase()) < 0)
+      if (filter && item.name.toLowerCase().search(filter) < 0)
         continue;
       if (item.isApp) {
         if (item.is_unpacked)
@@ -132,9 +140,15 @@
      * Creates all items from scratch.
      */
     showItemNodes: function() {
-      var packedItemsList = this.tabNode_.querySelector('#packed-list .items');
+      // Don't reset the content until the animation is finished.
+      if (animating) {
+        needsReloadAppDisplay = true;
+        return;
+      }
+
+      var packedItemsList = this.tabNode_.querySelector('.packed-list .items');
       var unpackedItemsList = this.tabNode_.querySelector(
-          '#unpacked-list .items');
+          '.unpacked-list .items');
       packedItemsList.innerHTML = '';
       unpackedItemsList.innerHTML = '';
 
@@ -145,7 +159,7 @@
 
       // Iterate over the items in the packed items and add each item to the
       // list.
-      this.tabNode_.querySelector('#packed-list').classList.toggle(
+      this.tabNode_.querySelector('.packed-list').classList.toggle(
           'empty-item-list', this.packedItems_.length == 0);
       for (var i = 0; i < this.packedItems_.length; ++i) {
         packedItemsList.appendChild(this.createNode_(this.packedItems_[i]));
@@ -153,7 +167,7 @@
 
       // Iterate over the items in the unpacked items and add each item to the
       // list.
-      this.tabNode_.querySelector('#unpacked-list').classList.toggle(
+      this.tabNode_.querySelector('.unpacked-list').classList.toggle(
           'empty-item-list', this.unpackedItems_.length == 0);
       for (var i = 0; i < this.unpackedItems_.length; ++i) {
         unpackedItemsList.appendChild(this.createNode_(this.unpackedItems_[i]));
@@ -238,47 +252,15 @@
           });
         });
         restart.hidden = false;
-
-        var launchButton = node.querySelector('.extension-run-button');
-        launchButton.addEventListener('click', function(e) {
-          ItemsList.launchApp(item.id);
-        });
-
-        var restartButton = node.querySelector('.extension-restart-button');
-        restartButton.addEventListener('click', function(e) {
-          chrome.developerPrivate.restart(item.id, function() {
-            ItemsList.loadItemsInfo();
-          });
-        });
-
-        var showLogs = node.querySelector('.extension-show-logs-button');
-        showLogs.addEventListener('click', function(e) {
-          if (!item.views.length)
-            return;
-          var view = item.views[0];
-
-          // Opens the devtools inspect window for the page.
-          chrome.developerPrivate.inspect({
-            extension_id: String(item.id),
-            render_process_id: String(view.render_process_id),
-            render_view_id: String(view.render_view_id),
-            incognito: view.incognito,
-          });
-        });
-      } else {
-        node.querySelector('.extension-run-button').hidden = true;
-        node.querySelector('.extension-restart-button').hidden = true;
-        node.querySelector('.extension-show-logs-button').hidden = true;
       }
-
       // The terminated reload link.
       if (!item.terminated)
         this.setEnabledCheckbox_(item, node);
       else
         this.setTerminatedReloadLink_(item, node);
 
-      // Set remove button handler.
-      this.setRemoveButton_(item, node);
+      // Set delete button handler.
+      this.setDeleteButton_(item, node);
 
       // First get the item id.
       var idLabel = node.querySelector('.extension-id');
@@ -300,12 +282,6 @@
 
       this.setActiveViews_(item, node);
 
-      var moreDetailsLink =
-          node.querySelector('.extension-more-details-button');
-      moreDetailsLink.addEventListener('click', function(e) {
-        this.toggleExtensionDetails_(item, node);
-      }.bind(this));
-
       return node;
     },
 
@@ -385,19 +361,46 @@
     },
 
     /**
-     * Sets the remove button handler.
+     * Shows the delete confirmation dialog and will trigger the deletion
+     * if the user confirms deletion.
+     * @param {!Object} item Information about the item being deleted.
+     * @private
+     */
+    showDeleteConfirmationDialog: function(item) {
+      var message;
+      if (item.isApp)
+        message = loadTimeData.getString('deleteConfirmationMessageApp');
+      else
+        message = loadTimeData.getString('deleteConfirmationMessageExtension');
+
+      alertOverlay.setValues(
+          loadTimeData.getString('deleteConfirmationTitle'),
+          message,
+          loadTimeData.getString('deleteConfirmationDeleteButton'),
+          loadTimeData.getString('cancel'),
+          function() {
+            AppsDevTool.showOverlay(null);
+            var options = {showConfirmDialog: false};
+            chrome.management.uninstall(item.id, options);
+          },
+          function() {
+            AppsDevTool.showOverlay(null);
+          });
+
+      AppsDevTool.showOverlay($('alertOverlay'));
+    },
+
+    /**
+     * Sets the delete button handler.
      * @param {!Object} item A dictionary of item metadata.
      * @param {!HTMLElement} el HTML element containing all items.
      * @private
      */
-    setRemoveButton_: function(item, el) {
+    setDeleteButton_: function(item, el) {
       var deleteLink = el.querySelector('.delete-link');
       deleteLink.addEventListener('click', function(e) {
-        var options = {showConfirmDialog: false};
-        chrome.management.uninstall(item.id, options, function() {
-          ItemsList.loadItemsInfo();
-        });
-      });
+        this.showDeleteConfirmationDialog(item);
+      }.bind(this));
     },
 
     /**
@@ -494,60 +497,15 @@
         }
       });
     },
-
-    /**
-     * If the details of an app / extension is expanded, this function will
-     * collapse it, else it will expand this them.
-     * @param {!Object} item A dictionary of item metadata.
-     * @param {!HTMLElement} node HTML element containing all items.
-     * @private
-     */
-    toggleExtensionDetails_: function(item, node)  {
-      var itemNode = node.querySelector('.extension-details');
-      if (itemNode.classList.contains('expanded'))
-        this.setExtensionDetailsVisible_(node, false);
-      else
-        this.setExtensionDetailsVisible_(node, true);
-    },
-
-    /**
-     * If visible is true, this function will expand the details of an
-     * app/extension, else it will collapse them.
-     * @param {!HTMLElement} node HTML element containing all items.
-     * @param {boolean} visible Visiblity of the details.
-     * @private
-     */
-    setExtensionDetailsVisible_: function(node, visible) {
-      var itemNode = node.querySelector('.extension-details');
-      var details = node.querySelector('.extension-details-all');
-      if (visible) {
-        // Hide other details.
-        var otherNodeList =
-            document.querySelectorAll('extension-list-item-wrapper');
-        for (var i = 0; i < otherNodeList.length; i++) {
-          if (otherNodeList[i] != node)
-            this.setExtensionDetailsVisible_(otherNodeList[i], false);
-        }
-
-        var container =
-            details.querySelector('.extension-details-all-container');
-        // Adds 10 pixels to height because .extension-details-all-bubble has
-        // a 10px top margin.
-        var height = container.clientHeight + 10;
-        details.style.height = height + 'px';
-        itemNode.classList.add('expanded');
-      } else {
-        details.style.height = 0;
-        itemNode.classList.remove('expanded');
-      }
-    }
   };
 
   /**
    * Rebuilds the item list and reloads the app on every search input.
    */
   ItemsList.onSearchInput = function() {
-    rebuildAppList($('search').value);
+    // Escape regexp special chars (e.g. ^, $, etc.).
+    rebuildAppList($('search').value.toLowerCase().replace(
+        /[-\/\\^$*+?.()|[\]{}]/g, '\\$&'));
     reloadAppDisplay();
   };
 
@@ -572,10 +530,7 @@
    */
   ItemsList.launchApp = function(id) {
     chrome.management.launchApp(id, function() {
-      ItemsList.loadItemsInfo(function() {
-        var unpacked = new ItemsList($('unpacked-list'), unpackedList);
-        unpacked.setExtensionDetailsVisible_($(id), true);
-      });
+      ItemsList.loadItemsInfo();
     });
   };
 
@@ -585,19 +540,52 @@
    * @param {string} id Identifier of the app / extension.
    */
   ItemsList.makeUnpackedExtensionVisible = function(id) {
+    // Find which tab contains the item.
     var tabbox = document.querySelector('tabbox');
-    // Unpacked tab is the first tab.
-    tabbox.selectedIndex = 0;
 
-    var firstItem =
-        document.querySelector('#unpacked-list .extension-list-item-wrapper');
-    if (!firstItem)
-      return;
-    // Scroll relatively to the position of the first item.
+    // Select the correct tab.
     var node = $(id);
-    document.body.scrollTop = node.offsetTop - firstItem.offsetTop;
-    var unpacked = new ItemsList($('unpacked-list'), unpackedList);
-    unpacked.setExtensionDetailsVisible_(node, true);
+    if (!node)
+      return;
+
+    var tabNode = findAncestor(node, function(el) {
+      return el.tagName == 'TABPANEL';
+    });
+    tabbox.selectedIndex = tabNode == $('apps-tab') ? 0 : 1;
+
+    // Highlights the item.
+    animating = true;
+    needsReloadAppDisplay = true;
+    node.classList.add('highlighted');
+    // Show highlighted item for 1 sec.
+    setTimeout(function() {
+      node.style.backgroundColor = 'rgba(255, 255, 128, 0)';
+      // Wait for fade animation to happen.
+      node.addEventListener('webkitTransitionEnd', function f(e) {
+        assert(e.propertyName == 'background-color');
+
+        animating = false;
+        if (needsReloadAppDisplay)
+          reloadAppDisplay();
+
+        node.removeEventListener('webkitTransitionEnd', f);
+      });
+    }, 1000);
+
+    // Scroll relatively to the position of the first item.
+    var header = tabNode.querySelector('.unpacked-list .list-header');
+    var container = $('container');
+    if (node.offsetTop - header.offsetTop < container.scrollTop) {
+      // Some padding between the top edge and the node is already provided
+      // by the HTML layout.
+      container.scrollTop = node.offsetTop - header.offsetTop;
+    } else if (node.offsetTop + node.offsetHeight > container.scrollTop +
+        container.offsetHeight + 20) {
+      // Adds padding of 20px between the bottom edge and the bottom of the
+      // node.
+      container.scrollTop = node.offsetTop + node.offsetHeight -
+          container.offsetHeight + 20;
+    }
   };
 
   return {
diff --git a/chrome/browser/resources/apps_debugger/main.html b/chrome/browser/resources/apps_debugger/main.html
index c3390d0..a618da9 100644
--- a/chrome/browser/resources/apps_debugger/main.html
+++ b/chrome/browser/resources/apps_debugger/main.html
@@ -20,72 +20,93 @@
     <script src="js/main_scripts.js"></script>
   </head>
   <body>
-    <div id="overlay" class="overlay" hidden>
-      <include src="pack_item_overlay.html">
-      <include src="../../../../ui/webui/resources/html/alert_overlay.html">
-    </div>
-    <div class="page" id="extension-settings">
-      <tabbox id="tab-box">
-        <div id="header">
-          <div id="developer-controls">
-            <button id="load-unpacked"
-                i18n-content="appsDevtoolLoadUnpackedButton"></button>
-            <button id="update-items-now"
-                i18n-content="appsDevtoolUpdateButton">
-            </button>
-            <h1 id="header-title" i18n-content="appsDevtoolTitle"></h1>
-            <input id="search" type="text"
-                i18n-values=".placeholder:appsDevtoolSearch" spellcheck="false">
+    <div id="container">
+      <div id="overlay" class="overlay" hidden>
+        <include src="pack_item_overlay.html">
+        <include src="../../../../ui/webui/resources/html/alert_overlay.html">
+      </div>
+      <div class="page" id="extension-settings">
+        <tabbox id="tab-box">
+          <div id="header">
+            <div id="tabs-header-container">
+              <tabs id="tabs" tabindex="0">
+                <tab i18n-content="appsDevtoolApps"></tab>
+                <tab i18n-content="appsDevtoolExtensions"></tab>
+                <input id="search" type="text"
+                    i18n-values=".placeholder:appsDevtoolSearch"
+                    spellcheck="false">
+              </tabs>
+            </div>
+            <div id="header-bottom-gradient"></div>
           </div>
-          <div id="tabs-header-container">
-            <tabs id="tabs" tabindex="0">
-              <tab i18n-content="appsDevtoolApps"></tab>
-              <tab i18n-content="appsDevtoolExtensions"></tab>
-            </tabs>
-          </div>
-          <div id="header-bottom-gradient"></div>
-        </div>
-        <tabpanels id="tab-panels">
-          <!-- Apps Tab -->
-          <tabpanel id="apps-tab">
-            <div id="unpacked-list">
-              <div class="list-header">
-                <span i18n-content="appsDevtoolUnpacked"></span>
+          <tabpanels id="tab-panels">
+            <!-- Apps Tab -->
+            <tabpanel id="apps-tab">
+              <div class="unpacked-list">
+                <div class="list-header">
+                  <span class="title" i18n-content="appsDevtoolUnpacked"></span>
+                  <button class="load-unpacked"
+                      i18n-content="appsDevtoolLoadUnpackedButton"></button>
+                </div>
+                <div class="items"></div>
+                <div id="no-unpacked-apps">
+                  <span id="no-unpacked-apps-message"
+                      i18n-content="appsDevtoolNoUnpackedApps"></span>
+                </div>
               </div>
-              <div class="items"></div>
-            </div>
-            <div id="packed-list">
-              <div class="list-header">
-                <span i18n-content="appsDevtoolInstalled"></span>
+              <div class="packed-list">
+                <div class="list-header">
+                  <span class="title" i18n-content="appsDevtoolInstalled">
+                  </span>
+                  <div class="update-items-container">
+                    <button class="update-items-progress"
+                        i18n-content="appsDevtoolUpdating" disabled></span>
+                    <button class="update-items-now"
+                        i18n-content="appsDevtoolUpdateButton"></button>
+                  </div>
+                </div>
+                <div class="items"></div>
+                <div id="no-packed-apps">
+                  <span id="no-packed-apps-message"
+                      i18n-content="appsDevtoolNoPackedApps"></span>
+                </div>
               </div>
-              <div class="items"></div>
-            </div>
-            <div id="no-apps">
-              <span id="no-apps-message"
-                  i18n-content="appsDevtoolNoApps"></span>
-            </div>
-          </tabpanel>
-          <!-- Extensions Tab -->
-          <tabpanel id="extensions-tab">
-            <div id="unpacked-list">
-              <div class="list-header">
-                <span i18n-content="appsDevtoolUnpacked"></span>
+            </tabpanel>
+            <!-- Extensions Tab -->
+            <tabpanel id="extensions-tab">
+              <div class="unpacked-list">
+                <div class="list-header">
+                  <span class="title" i18n-content="appsDevtoolUnpacked"></span>
+                  <button class="load-unpacked"
+                      i18n-content="appsDevtoolLoadUnpackedButton"></button>
+                </div>
+                <div class="items"></div>
+                <div id="no-unpacked-extensions">
+                  <span id="no-unpacked-extensions-message"
+                      i18n-content="appsDevtoolNoUnpackedExtensions"></span>
+                </div>
               </div>
-              <div class="items"></div>
-            </div>
-            <div id="packed-list">
-              <div class="list-header">
-                <span i18n-content="appsDevtoolInstalled"></span>
+              <div class="packed-list">
+                <div class="list-header">
+                  <span class="title" i18n-content="appsDevtoolInstalled">
+                  </span>
+                  <div class="update-items-container">
+                    <button class="update-items-progress"
+                        i18n-content="appsDevtoolUpdating" disabled></span>
+                    <button class="update-items-now"
+                        i18n-content="appsDevtoolUpdateButton"></button>
+                  </div>
+                </div>
+                <div class="items"></div>
+                <div id="no-packed-extensions">
+                  <span id="no-packed-extensions-message"
+                      i18n-content="appsDevtoolNoPackedExtensions"></span>
+                </div>
               </div>
-              <div class="items"></div>
-            </div>
-            <div id="no-extensions">
-              <span id="no-extensions-message"
-                  i18n-content="appsDevtoolNoExtensions"></span>
-            </div>
-          </tabpanel>
-        </tabpanels>
-      </tabbox>
+            </tabpanel>
+          </tabpanels>
+        </tabbox>
+      </div>
     </div>
     <div id="template-collection" hidden>
       <div class="extension-list-item-wrapper">
@@ -93,123 +114,102 @@
           <div class="extension-details">
             <div class="extension-details-summary">
               <div>
-                <span class="extension-title"></span>
+                <a class="extension-title" href="#"></a>
                 <span class="extension-disabled"
                     i18n-content="extensionDisabled"></span>
               </div>
               <p class="extension-description"><span></span></p>
-              <div class="extension-buttons-container">
-                <div class="extension-run-button"></div>
-                <div class="extension-restart-button"></div>
-                <div class="extension-show-logs-button"
-                    i18n-content="extensionSettingsShowLogsButton"></div>
-                <div class="extension-more-details-button"
-                    i18n-content="extensionSettingsMoreDetailsButton"></div>
-              </div>
             </div>
             <div class="extension-details-all">
-              <div class="extension-details-all-container">
-                <div class="extension-details-all-arrow-border"></div>
-                <div class="extension-details-all-arrow"></div>
-                <div class="extension-details-all-bubble">
-                  <div>
-                    <div>
-                      <span i18n-content="extensionSettingsVersion"></span>
-                      <span class="extension-version"></span>
-                    </div>
-                    <div class="item-id">
-                      <span i18n-content="extensionSettingsExtensionId"></span>
-                      <span class="extension-id"></span>
-                    </div>
-                    <div class="load-path" hidden>
-                      <span i18n-content="extensionSettingsExtensionPath">
+              <div>
+                <div>
+                  <span i18n-content="extensionSettingsVersion"></span>
+                  <span class="extension-version"></span>
+                </div>
+                <div class="item-id">
+                  <span i18n-content="extensionSettingsExtensionId"></span>
+                  <span class="extension-id"></span>
+                </div>
+                <div class="load-path" hidden>
+                  <span i18n-content="extensionSettingsExtensionPath"></span>
+                  <span></span>
+                </div>
+              </div>
+              <div>
+                <span class="extension-disable-reason"></span>
+              </div>
+              <div class="managed-message"
+                  i18n-content="extensionSettingsPolicyControlled" hidden>
+              </div>
+              <div class="extension-warnings" hidden>
+                <span i18n-content="extensionSettingsInstallWarnings"></span>
+              </div>
+              <div class="install-warnings" hidden>
+                <span i18n-content="extensionSettingsWarningsTitle"></span>
+                <ul></ul>
+              </div>
+              <div>
+                <div class="optional-controls">
+                  <button class="show-button"
+                      i18n-content="extensionSettingsShowButton" hidden>
+                  </button>
+                  <span class="optional-control-disableable">
+                    <label class="incognito-control" hidden>
+                      <input type="checkbox">
+                      <span i18n-content="extensionSettingsEnableIncognito">
                       </span>
-                      <span></span>
-                    </div>
-                  </div>
-                  <div>
-                    <span class="extension-disable-reason"></span>
-                  </div>
-                  <div class="managed-message"
-                      i18n-content="extensionSettingsPolicyControlled" hidden>
-                  </div>
-                  <div class="extension-warnings" hidden>
-                    <span i18n-content="extensionSettingsInstallWarnings">
-                    </span>
-                  </div>
-                  <div class="install-warnings" hidden>
-                    <span i18n-content="extensionSettingsWarningsTitle"></span>
-                    <ul></ul>
-                  </div>
-                  <div>
-                    <div class="optional-controls">
-                      <button class="show-button"
-                          i18n-content="extensionSettingsShowButton" hidden>
-                      </button>
-                      <span class="optional-control-disableable">
-                        <label class="incognito-control" hidden>
-                          <input type="checkbox">
-                          <span
-                              i18n-content="extensionSettingsEnableIncognito">
-                          </span>
-                        </label>
-                        <label class="file-access-control" hidden>
-                          <input type="checkbox">
-                          <span i18n-content="extensionSettingsAllowFileAccess">
-                          </span>
-                        </label>
+                    </label>
+                    <label class="file-access-control" hidden>
+                      <input type="checkbox">
+                      <span i18n-content="extensionSettingsAllowFileAccess">
                       </span>
-                    </div>
-                    <div class="enable-controls">
-                      <a class="terminated-reload-link" href="#"
-                          i18n-content="extensionSettingsReloadTerminated"
-                          hidden></a>
-                      <div class="checkbox enable-checkbox" hidden>
-                        <label>
-                          <input type="checkbox">
-                          <span class="enable-checkbox-text">
-                            <span class="enabled-text"
-                                i18n-content="extensionSettingsEnabled"></span>
-                            <span class="enable-text"
-                                i18n-content="extensionSettingsEnable"></span>
-                          </span>
-                        </label>
-                        <span class="location-text"></span>
-                      </div>
-                    </div>
-                  </div>
-                  <div>
-                    <div class="active-views" hidden>
-                      <span i18n-content="extensionSettingsInspectViews"></span>
-                      <a href="#"></a>
-                    </div>
-                  </div>
-                  <div>
-                    <a class="reload-link" href="#"
-                        i18n-content="extensionSettingsReloadUnpacked" hidden>
-                    </a>
-                    <a class="launch-link"
-                        i18n-content="extensionSettingsLaunch"
-                        href="#" hidden></a>
-                    <a class="restart-link"
-                        i18n-content="extensionSettingsRestart"
-                        href="#" hidden></a>
-                  </div>
-                  <div>
-                    <a class="permissions-link"
-                        i18n-content="extensionSettingsPermissions"
-                        href="#"></a>
-                    <a class="site-link" target="_blank"></a>
-                    <a class="options-link"
-                        i18n-content="extensionSettingsOptions"
-                        href="#" target="_blank" hidden></a>
-                    <a class="pack-link" i18n-content="extensionSettingsPack"
-                        href="#" hidden></a>
-                    <a class="delete-link"
-                        i18n-content="extensionSettingsDelete" href="#"></a>
+                    </label>
+                  </span>
+                </div>
+                <div class="enable-controls">
+                  <a class="terminated-reload-link" href="#"
+                      i18n-content="extensionSettingsReloadTerminated" hidden>
+                  </a>
+                  <div class="checkbox enable-checkbox" hidden>
+                    <label>
+                      <input type="checkbox">
+                      <span class="enable-checkbox-text">
+                        <span class="enabled-text"
+                            i18n-content="extensionSettingsEnabled"></span>
+                        <span class="enable-text"
+                            i18n-content="extensionSettingsEnable"></span>
+                      </span>
+                    </label>
+                    <span class="location-text"></span>
                   </div>
                 </div>
               </div>
+              <div>
+                <div class="active-views" hidden>
+                  <span i18n-content="extensionSettingsInspectViews"></span>
+                  <a href="#"></a>
+                </div>
+              </div>
+              <div>
+                <a class="reload-link" href="#"
+                    i18n-content="extensionSettingsReloadUnpacked" hidden>
+                </a>
+                <a class="launch-link" i18n-content="extensionSettingsLaunch"
+                    href="#" hidden></a>
+                <a class="restart-link" i18n-content="extensionSettingsRestart"
+                    href="#" hidden></a>
+              </div>
+              <div>
+                <a class="permissions-link"
+                    i18n-content="extensionSettingsPermissions" href="#"></a>
+                <a class="site-link" target="_blank" hidden></a>
+                <a class="options-link" i18n-content="extensionSettingsOptions"
+                    href="#" target="_blank" hidden></a>
+                <a class="pack-link" i18n-content="extensionSettingsPack"
+                    href="#" hidden></a>
+                <a class="delete-link" i18n-content="extensionSettingsDelete"
+                    href="#"></a>
+              </div>
             </div>
           </div>
         </div>
diff --git a/chrome/browser/resources/chromeos/access_chromevox/manifest.json b/chrome/browser/resources/chromeos/access_chromevox/manifest.json
index 2ec96d9..cd1cf05 100644
--- a/chrome/browser/resources/chromeos/access_chromevox/manifest.json
+++ b/chrome/browser/resources/chromeos/access_chromevox/manifest.json
@@ -27,7 +27,8 @@
   "web_accessible_resources": [
     "chromevox/injected/api.js",
     "chromevox/injected/api_util.js",
-    "chromevox/injected/mathjax.js"
+    "chromevox/injected/mathjax.js",
+    "chromevox/injected/mathjax_external_util.js"
   ],
   "default_locale": "en"
 }
diff --git a/chrome/browser/resources/chromeos/connectivity_diagnostics/manifest.json b/chrome/browser/resources/chromeos/connectivity_diagnostics/manifest.json
index 01322cb..f1368d8 100644
--- a/chrome/browser/resources/chromeos/connectivity_diagnostics/manifest.json
+++ b/chrome/browser/resources/chromeos/connectivity_diagnostics/manifest.json
@@ -23,5 +23,10 @@
    "experimental",
    "http://*.google.com/*",
    "http://www.yahoo.com/*"
-  ]
+  ],
+  "app": {
+    "background": {
+      "scripts": ["background.js"]
+    }
+  }
 }
diff --git a/chrome/browser/resources/chromeos/connectivity_diagnostics_launcher/manifest.json b/chrome/browser/resources/chromeos/connectivity_diagnostics_launcher/manifest.json
new file mode 100644
index 0000000..6dd5276
--- /dev/null
+++ b/chrome/browser/resources/chromeos/connectivity_diagnostics_launcher/manifest.json
@@ -0,0 +1,11 @@
+{
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOg+/GcwTYomV06ymtKpD08+M7qjbINqbvPUT/+RFdGm30vVIWivqIrFuiXr+h8d77yTUHcpkRa4LEMulsG2Q44SWfWhBZOVnohsu/OUdHKZ+nw82Q0G+4jjkVOz3/AvUBCBwBaL90Jn05hockJriLAvNUWKLheirw58BVzF9z9wIDAQAB",
+  "manifest_version": 2,
+  "name": "NCDLauncher",
+  "version": "1",
+  "minimum_chrome_version": "27",
+  "offline_enabled": true,
+  "incognito": "split",
+  "web_accessible_resources": ["index.html"]
+}
+
diff --git a/chrome/browser/resources/chromeos/drive_internals.css b/chrome/browser/resources/chromeos/drive_internals.css
index c2845a0..b14b68f 100644
--- a/chrome/browser/resources/chromeos/drive_internals.css
+++ b/chrome/browser/resources/chromeos/drive_internals.css
@@ -14,3 +14,15 @@
 #cache-contents {
   font-size: small;
 }
+
+.log-error {
+  color: red;
+  font-weight: bolder;
+}
+
+.log-warning {
+  color: red;
+}
+
+.log-info {
+}
diff --git a/chrome/browser/resources/chromeos/drive_internals.js b/chrome/browser/resources/chromeos/drive_internals.js
index b6904ce..bb5b9e9 100644
--- a/chrome/browser/resources/chromeos/drive_internals.js
+++ b/chrome/browser/resources/chromeos/drive_internals.js
@@ -218,16 +218,20 @@
 /**
  * Updates <ul> element with the given key-value list.
  * @param {HTMLElement} ul <ul> element to be modified.
- * @param {Array} list List of dictionaries containing 'key' and 'value'.
+ * @param {Array} list List of dictionaries containing 'key', 'value' (optional)
+ * and 'class' (optional). For each element <li> element with specified class is
+ * created.
  */
 function updateKeyValueList(ul, list) {
   for (var i = 0; i < list.length; i++) {
-    var flag = list[i];
-    var text = flag.key;
-    if (list.value != '')
-      text += ': ' + flag.value;
+    var item = list[i];
+    var text = item.key;
+    if (item.value != '')
+      text += ': ' + item.value;
 
     var li = createElementFromText('li', text);
+    if (item.class)
+      li.classList.add(item.class);
     ul.appendChild(li);
   }
 }
diff --git a/chrome/browser/resources/chromeos/echo/manifest.json b/chrome/browser/resources/chromeos/echo/manifest.json
index 1f69e5a..b1ad4f3 100644
--- a/chrome/browser/resources/chromeos/echo/manifest.json
+++ b/chrome/browser/resources/chromeos/echo/manifest.json
@@ -21,7 +21,7 @@
   "web_accessible_resources": [
     "broker.html",
     "broker.js",
-    "error.html"
+    "not-eligible.html"
   ],
   "background": {
     "scripts": ["main.js"],
diff --git a/chrome/browser/resources/chromeos/login/user_pod_row.css b/chrome/browser/resources/chromeos/login/user_pod_row.css
index 33d7135..04c6eda 100644
--- a/chrome/browser/resources/chromeos/login/user_pod_row.css
+++ b/chrome/browser/resources/chromeos/login/user_pod_row.css
@@ -307,7 +307,7 @@
   outline: none;
 }
 
-.action-box-remove-managed-user-warning {
+.action-box-remove-user-warning {
   border-top: 1px solid lightgray;
   color: #000;
   font-size: 12px;
@@ -315,11 +315,11 @@
   padding: 20px;
 }
 
-.action-box-remove-managed-user-warning-text {
+.action-box-remove-user-warning-text {
   padding-bottom: 20px;
 }
 
-.action-box-remove-managed-user-warning .remove-warning-button {
+.action-box-remove-user-warning .remove-warning-button {
   width: 100%;
 }
 
diff --git a/chrome/browser/resources/chromeos/login/user_pod_row.js b/chrome/browser/resources/chromeos/login/user_pod_row.js
index 5482a9c..cb91bd8 100644
--- a/chrome/browser/resources/chromeos/login/user_pod_row.js
+++ b/chrome/browser/resources/chromeos/login/user_pod_row.js
@@ -155,8 +155,8 @@
       this.actionBoxMenuRemoveElement.addEventListener('blur',
           this.handleRemoveCommandBlur_.bind(this));
 
-      if (this.actionBoxRemoveManagedUserWarningButtonElement) {
-        this.actionBoxRemoveManagedUserWarningButtonElement.addEventListener(
+      if (this.actionBoxRemoveUserWarningButtonElement) {
+        this.actionBoxRemoveUserWarningButtonElement.addEventListener(
             'click',
             this.handleRemoveUserConfirmationClick_.bind(this));
       }
@@ -313,15 +313,15 @@
      * Gets action box menu, remove user command item div.
      * @type {!HTMLInputElement}
      */
-    get actionBoxRemoveManagedUserWarningElement() {
-      return this.querySelector('.action-box-remove-managed-user-warning');
+    get actionBoxRemoveUserWarningElement() {
+      return this.querySelector('.action-box-remove-user-warning');
     },
 
     /**
      * Gets action box menu, remove user command item div.
      * @type {!HTMLInputElement}
      */
-    get actionBoxRemoveManagedUserWarningButtonElement() {
+    get actionBoxRemoveUserWarningButtonElement() {
       return this.querySelector(
           '.remove-warning-button');
     },
@@ -416,8 +416,8 @@
 
       if (active) {
         this.actionBoxMenuRemoveElement.hidden = !this.user_.canRemove;
-        if (this.actionBoxRemoveManagedUserWarningElement)
-          this.actionBoxRemoveManagedUserWarningElement.hidden = true;
+        if (this.actionBoxRemoveUserWarningElement)
+          this.actionBoxRemoveUserWarningElement.hidden = true;
 
         // Clear focus first if another pod is focused.
         if (!this.parentNode.isFocused(this)) {
@@ -558,7 +558,7 @@
      * @param {Event} e Click event.
      */
     handleRemoveCommandClick_: function(e) {
-      if (this.user.locallyManagedUser) {
+      if (this.user.locallyManagedUser || this.user.isDesktopUser) {
         this.showRemoveWarning_();
         return;
       }
@@ -571,7 +571,7 @@
      */
     showRemoveWarning_: function() {
       this.actionBoxMenuRemoveElement.hidden = true;
-      this.actionBoxRemoveManagedUserWarningElement.hidden = false;
+      this.actionBoxRemoveUserWarningElement.hidden = false;
     },
 
     /**
@@ -884,10 +884,8 @@
     },
 
     /** @override */
-    handleRemoveCommandClick_: function(e) {
-      //TODO(noms): Add deletion confirmation overlay before attempting
-      // to delete the user.
-      UserPod.prototype.handleRemoveCommandClick_.call(this, e);
+    handleRemoveUserConfirmationClick_: function(e) {
+      chrome.send('removeUser', [this.user.profilePath]);
     },
   };
 
diff --git a/chrome/browser/resources/chromeos/login/user_pod_template.html b/chrome/browser/resources/chromeos/login/user_pod_template.html
index 635b22d..1be71cb 100644
--- a/chrome/browser/resources/chromeos/login/user_pod_template.html
+++ b/chrome/browser/resources/chromeos/login/user_pod_template.html
@@ -23,12 +23,12 @@
     <div class="action-box-menu-remove">
       <span class="action-box-menu-remove-command"/>
     </div>
-    <div class="action-box-remove-managed-user-warning" hidden>
-      <div class="action-box-remove-managed-user-warning-text"
-          i18n-content="removeManagedUserWarningText"></div>
+    <div class="action-box-remove-user-warning" hidden>
+      <div class="action-box-remove-user-warning-text"
+          i18n-content="removeUserWarningText"></div>
       <button class="remove-warning-button
                      custom-appearance button-fancy button-red"
-          i18n-content="removeManagedUserWarningButtonTitle"></button>
+          i18n-content="removeUserWarningButtonTitle"></button>
     </div>
   </div>
 </div>
diff --git a/chrome/browser/resources/chromeos/offline_load.html b/chrome/browser/resources/chromeos/offline_load.html
index 1af9baa..bab14d9 100644
--- a/chrome/browser/resources/chromeos/offline_load.html
+++ b/chrome/browser/resources/chromeos/offline_load.html
@@ -45,14 +45,6 @@
   font-size: 160%;
 }
 
-#lower {
-  -webkit-box-flex: 1;
-  position: relative;
-  margin-top: 0;
-  width: 100%;
-  background-image: -webkit-gradient(radial, 50% -20%, 700, 50% -70%, 0, from(#FFF), color-stop(.7, #EEE), to(#EEE));
-}
-
 #heading {
   font-size: 15px;
   padding-top: 20px;
@@ -117,11 +109,7 @@
 
 document.addEventListener('DOMContentLoaded', function() {
   var timeToWait = localStrings.getString('time_to_wait');
-  var showActivation = localStrings.getString('show_activation') == 'true';
   window.setTimeout(showPage, timeToWait);
-  var lower = document.getElementById('lower');
-  if (showActivation)
-    lower.style.display = 'block';
 });
 </script>
 
@@ -141,13 +129,6 @@
              i18n-content="network_settings"></button>
         </div>
       </div>
-      <div id="lower" class="hidden">
-        <div class="activation_message">
-          <div class="splitter"></div>
-          <iframe src="chrome-extension://iadeocfgjdjdmpenejdbfeaocpbikmab/activation_in_offline.html"
-                  id="carrierPage" frameborder="0"></iframe>
-        </div>
-      </div>
     </div>
     <div id="right">
       <img src="images/broken_robot.png">
diff --git a/chrome/browser/resources/chromeos/user_images_grid.js b/chrome/browser/resources/chromeos/user_images_grid.js
index 3479d58..d47818d 100644
--- a/chrome/browser/resources/chromeos/user_images_grid.js
+++ b/chrome/browser/resources/chromeos/user_images_grid.js
@@ -43,10 +43,10 @@
   };
 
   /**
-   * Internal URL scheme.
+   * Path for internal URLs.
    * @const
    */
-  var CHROME_SCHEME = 'chrome://';
+  var CHROME_THEME_PATH = 'chrome://theme';
 
   /**
    * Creates a new user images grid item.
@@ -69,9 +69,10 @@
     decorate: function() {
       GridItem.prototype.decorate.call(this);
       var imageEl = cr.doc.createElement('img');
-      // Force 1x scale for internal URLs. Grid elements are much smaller
+      // Force 1x scale for chrome://theme URLs. Grid elements are much smaller
       // than actual images so there is no need in full scale on HDPI.
-      if (this.dataItem.url.slice(0, CHROME_SCHEME.length) == CHROME_SCHEME)
+      var url = this.dataItem.url;
+      if (url.slice(0, CHROME_THEME_PATH.length) == CHROME_THEME_PATH)
         imageEl.src = this.dataItem.url + '@1x';
       else
         imageEl.src = this.dataItem.url;
@@ -200,7 +201,7 @@
     updatePreview_: function() {
       var url = this.selectedItemUrl;
       if (url && this.previewImage_) {
-        if (url.slice(0, CHROME_SCHEME.length) == CHROME_SCHEME)
+        if (url.slice(0, CHROME_THEME_PATH.length) == CHROME_THEME_PATH)
           this.previewImage_.src = url + '@' + window.devicePixelRatio + 'x';
         else
           this.previewImage_.src = url;
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
index af31cec..0830878 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
@@ -125,7 +125,8 @@
 
 /**
  * Sets wallpaper to one of the wallpapers displayed in wallpaper picker. If
- * the wallpaper download fails, retry one hour later.
+ * the wallpaper download fails, retry one hour later. Wallpapers that are
+ * disabled for surprise me are excluded.
  * @param {string} dateString String representation of current local date.
  * @private
  */
@@ -134,9 +135,15 @@
   Constants.WallpaperLocalStorage.get(Constants.AccessManifestKey,
                                       function(items) {
     var manifest = items[Constants.AccessManifestKey];
-    if (manifest) {
-      var index = Math.floor(Math.random() * manifest.wallpaper_list.length);
-      var wallpaper = manifest.wallpaper_list[index];
+    if (manifest && manifest.wallpaper_list) {
+      var filtered = manifest.wallpaper_list.filter(function(element) {
+        // Older version manifest do not have available_for_surprise_me field.
+        // In this case, no wallpaper should be filtered out.
+        return element.available_for_surprise_me ||
+            element.available_for_surprise_me == undefined;
+      });
+      var index = Math.floor(Math.random() * filtered.length);
+      var wallpaper = filtered[index];
       var wallpaperURL = wallpaper.base_url + Constants.HighResolutionSuffix;
       var onSuccess = function() {
         WallpaperUtil.saveToStorage(
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index 659f13e..8e67c62 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -43,6 +43,12 @@
         <include name="IDR_BACKLOADER_BACKGROUND_JS" file="backloader/scripts/background.js" type="BINDATA" />
         <include name="IDR_BACKLOADER_PAGES_JS" file="backloader/scripts/pages.js" type="BINDATA" />
       </if>
+      <include name="IDR_FEEDBACK_DEFAULT_HTML" file="feedback/html/default.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+      <include name="IDR_FEEDBACK_EVENT_HANDLER_JS" file="feedback/js/event_handler.js" flattenhtml="true" type="BINDATA" />
+      <include name="IDR_FEEDBACK_FEEDBACK_JS" file="feedback/js/feedback.js" flattenhtml="true" type="BINDATA" />
+      <include name="IDR_FEEDBACK_TAKE_SCREENSHOT_JS" file="feedback/js/take_screenshot.js" flattenhtml="true" type="BINDATA" />
+      <include name="IDR_FEEDBACK_TOPBAR_HANDLER_JS" file="feedback/js/topbar_handlers.js" flattenhtml="true" type="BINDATA" />
+      <include name="IDR_FEEDBACK_FEEDBACK_CSS" file="feedback/css/feedback.css" type="BINDATA" />
       <if expr="pp_ifdef('file_manager_extension')">
         <!-- App pages and scripts. -->
         <include name="IDR_FILE_MANAGER_MAIN" file="file_manager/main.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
diff --git a/chrome/browser/resources/downloads/downloads.js b/chrome/browser/resources/downloads/downloads.js
index dd9b457..c6a6253 100644
--- a/chrome/browser/resources/downloads/downloads.js
+++ b/chrome/browser/resources/downloads/downloads.js
@@ -345,6 +345,9 @@
       loadTimeData.getString('control_cancel'));
   this.nodeControls_.appendChild(this.controlCancel_);
 
+  this.controlByExtension_ = document.createElement('span');
+  this.nodeControls_.appendChild(this.controlByExtension_);
+
   // Container for 'unsafe download' UI.
   this.danger_ = createElementWithClassName('div', 'show-dangerous');
   this.node.appendChild(this.danger_);
@@ -421,6 +424,8 @@
   this.fileExternallyRemoved_ = download.file_externally_removed;
   this.dangerType_ = download.danger_type;
   this.lastReasonDescription_ = download.last_reason_text;
+  this.byExtensionId_ = download.by_ext_id;
+  this.byExtensionName_ = download.by_ext_name;
 
   this.since_ = download.since_string;
   this.date_ = download.date_string;
@@ -521,6 +526,23 @@
     showInline(this.controlCancel_, showCancel);
     showInline(this.controlRemove_, !showCancel);
 
+    if (this.byExtensionId_ && this.byExtensionName_) {
+      // Format 'control_by_extension' with a link instead of plain text by
+      // splitting the formatted string into pieces.
+      var slug = 'XXXXX';
+      var formatted = loadTimeData.getStringF('control_by_extension', slug);
+      var slugIndex = formatted.indexOf(slug);
+      this.controlByExtension_.textContent = formatted.substr(0, slugIndex);
+      this.controlByExtensionLink_ = document.createElement('a');
+      this.controlByExtensionLink_.href =
+          'chrome://extensions#' + this.byExtensionId_;
+      this.controlByExtensionLink_.textContent = this.byExtensionName_;
+      this.controlByExtension_.appendChild(this.controlByExtensionLink_);
+      if (slugIndex < (formatted.length - slug.length))
+        this.controlByExtension_.appendChild(document.createTextNode(
+            formatted.substr(slugIndex + 1)));
+    }
+
     this.nodeSince_.textContent = this.since_;
     this.nodeDate_.textContent = this.date_;
     // Don't unnecessarily update the url, as doing so will remove any
diff --git a/chrome/browser/resources/extensions/extension_list.js b/chrome/browser/resources/extensions/extension_list.js
index 78ce406..0e0f694 100644
--- a/chrome/browser/resources/extensions/extension_list.js
+++ b/chrome/browser/resources/extensions/extension_list.js
@@ -32,6 +32,14 @@
    */
   var butterBarVisibility = {};
 
+  /**
+   * @type {Object.<string, string>} A map from extension id to last reloaded
+   *     timestamp. The timestamp is recorded when the user click the 'Reload'
+   *     link. It is used to refresh the icon of an unpacked extension.
+   *     This persists between calls to decorate.
+   */
+  var extensionReloadedTimestamp = {};
+
   ExtensionsList.prototype = {
     __proto__: HTMLDivElement.prototype,
 
@@ -97,10 +105,16 @@
         node.classList.add('extension-highlight');
 
       var item = node.querySelector('.extension-list-item');
-      var extensionIconUrl = extension.icon;
-      if (extension.allow_reload)
-        extensionIconUrl = extension.icon + '?' + Date.now();
-      item.style.backgroundImage = 'url(' + extensionIconUrl + ')';
+      // Prevent the image cache of extension icon by using the reloaded
+      // timestamp as a query string. The timestamp is recorded when the user
+      // clicks the 'Reload' link. http://crbug.com/159302.
+      if (extensionReloadedTimestamp[extension.id]) {
+        item.style.backgroundImage =
+            'url(' + extension.icon + '?' +
+            extensionReloadedTimestamp[extension.id] + ')';
+      } else {
+        item.style.backgroundImage = 'url(' + extension.icon + ')';
+      }
 
       var title = node.querySelector('.extension-title');
       title.textContent = extension.name;
@@ -182,6 +196,7 @@
         var reload = node.querySelector('.reload-link');
         reload.addEventListener('click', function(e) {
           chrome.send('extensionSettingsReload', [extension.id]);
+          extensionReloadedTimestamp[extension.id] = Date.now();
         });
         reload.hidden = false;
 
@@ -318,6 +333,15 @@
       }
 
       this.appendChild(node);
+      if (location.hash.substr(1) == extension.id) {
+        // Scroll beneath the fixed header so that the extension is not
+        // obscured.
+        var topScroll = node.offsetTop - $('page-header').offsetHeight;
+        var pad = parseInt(getComputedStyle(node, null).marginTop, 10);
+        if (!isNaN(pad))
+          topScroll -= pad / 2;
+        document.body.scrollTop = topScroll;
+      }
     }
   };
 
diff --git a/chrome/browser/resources/extensions/extensions.css b/chrome/browser/resources/extensions/extensions.css
index e69c0c3..579fdaa 100644
--- a/chrome/browser/resources/extensions/extensions.css
+++ b/chrome/browser/resources/extensions/extensions.css
@@ -6,31 +6,64 @@
   -webkit-transition-duration: 0 !important;
 }
 
+html:not(.focus-outline-visible)
+:enabled:focus:-webkit-any(input[type='checkbox'], input[type='radio']) {
+  /* Cancel border-color for :focus specified in widgets.css. */
+  border-color: rgba(0, 0, 0, 0.25);
+}
+
 /* Developer mode */
 
 #dev-controls {
-  -webkit-padding-end: 3px;
-  -webkit-padding-start: 4px;
+  -webkit-margin-end: 20px;
   -webkit-transition: padding 100ms, height 100ms, opacity 100ms;
   border-bottom: 1px solid #eee;
-  display: -webkit-box;
   height: 0;
   opacity: 0;
   overflow: hidden;
 }
 
+#dev-controls .buttons-container,
+#apps-dev-tool-banner {
+  -webkit-padding-end: 3px;
+  -webkit-padding-start: 4px;
+}
+
+#dev-controls .buttons-container {
+  display: -webkit-box;
+  padding-top: 13px;
+}
+
 #dev-controls button {
   white-space: nowrap;
 }
 
 #extension-settings.dev-mode #dev-controls {
   -webkit-transition-duration: 250ms;
-  height: 32px;
+  height: 45px;
   opacity: 1;
   padding-bottom: 7px;
+}
+
+#extension-settings.dev-mode.apps-dev-tools-mode #dev-controls {
+  height: 100px;
+}
+
+#apps-dev-tool-banner {
+  border-bottom: 1px solid #eee;
+  display: none;
+  padding-bottom: 13px;
   padding-top: 13px;
 }
 
+#extension-settings.dev-mode.apps-dev-tools-mode #apps-dev-tool-banner {
+  display: -webkit-box;
+}
+
+.apps-dev-tool-banner-text {
+  -webkit-box-flex: 1;
+}
+
 #dev-controls-spacer {
   -webkit-box-flex: 1;
 }
diff --git a/chrome/browser/resources/extensions/extensions.html b/chrome/browser/resources/extensions/extensions.html
index 02c5c16..c6418d8 100644
--- a/chrome/browser/resources/extensions/extensions.html
+++ b/chrome/browser/resources/extensions/extensions.html
@@ -19,6 +19,7 @@
 <script src="chrome://resources/js/cr/ui/alert_overlay.js"></script>
 <script src="chrome://resources/js/cr/ui/drag_wrapper.js"></script>
 <script src="chrome://resources/js/cr/ui/focus_manager.js"></script>
+<script src="chrome://resources/js/cr/ui/focus_outline_manager.js"></script>
 <script src="chrome://resources/js/cr/ui/overlay.js"></script>
 
 <if expr="pp_ifdef('chromeos')">
@@ -71,17 +72,25 @@
     </div>
   </header>
   <div id="dev-controls" hidden>
-    <button id="load-unpacked"
-        i18n-content="extensionSettingsLoadUnpackedButton"></button>
-    <button id="pack-extension"
-        i18n-content="extensionSettingsPackButton"></button>
+    <div id="apps-dev-tool-banner">
+      <div class="apps-dev-tool-banner-text"
+          i18n-content="extensionSettingsUseAppsDevTools"></div>
+      <button id="open-apps-dev-tools"
+          i18n-content="extensionSettingsOpenAppsDevTools"></button>
+    </div>
+    <div class="buttons-container">
+      <button id="load-unpacked"
+          i18n-content="extensionSettingsLoadUnpackedButton"></button>
+      <button id="pack-extension"
+          i18n-content="extensionSettingsPackButton"></button>
 <if expr="pp_ifdef('chromeos')">
       <button id="add-kiosk-app"
           i18n-content="addKioskAppButton" hidden></button>
 </if>
-    <div id="dev-controls-spacer"></div>
-    <button id="update-extensions-now"
-        i18n-content="extensionSettingsUpdateButton"></button>
+      <div id="dev-controls-spacer"></div>
+      <button id="update-extensions-now"
+          i18n-content="extensionSettingsUpdateButton"></button>
+    </div>
   </div>
   <div id="extension-settings-list" class="empty-extension-list"></div>
   <div id="no-extensions">
diff --git a/chrome/browser/resources/extensions/extensions.js b/chrome/browser/resources/extensions/extensions.js
index 89bfa71..f07af56 100644
--- a/chrome/browser/resources/extensions/extensions.js
+++ b/chrome/browser/resources/extensions/extensions.js
@@ -89,7 +89,7 @@
      */
     initialize: function() {
       uber.onContentFrameLoaded();
-
+      cr.ui.FocusOutlineManager.forDocument(document);
       measureCheckboxStrings();
 
       // Set the title.
@@ -104,6 +104,8 @@
           this.handleToggleDevMode_.bind(this));
       $('dev-controls').addEventListener('webkitTransitionEnd',
           this.handleDevControlsTransitionEnd_.bind(this));
+      $('open-apps-dev-tools').addEventListener('click',
+          this.handleOpenAppsDevTools_.bind(this));
 
       // Set up the three dev mode buttons (load unpacked, pack and update).
       $('load-unpacked').addEventListener('click',
@@ -237,6 +239,17 @@
         $('dev-controls').hidden = true;
       }
     },
+
+    /**
+     * Called when the user clicked on the button to launch Apps Developer
+     * Tools.
+     * @param {!Event} e A click event.
+     * @private
+     */
+    handleOpenAppsDevTools_: function(e) {
+      chrome.send('extensionSettingsLaunch',
+                  ['lphgohfeebnhcpiohjndkgbhhkoapkjc']);
+    },
   };
 
   /**
@@ -296,6 +309,9 @@
       $('toggle-dev-on').checked = false;
     }
 
+    if (extensionsData.appsDevToolsEnabled)
+      pageDiv.classList.add('apps-dev-tools-mode');
+
     $('load-unpacked').disabled = extensionsData.loadUnpackedDisabled;
 
     ExtensionsList.prototype.data_ = extensionsData;
@@ -346,6 +362,12 @@
 
     if (node)
       node.classList.add('showing');
+
+    var pages = document.querySelectorAll('.page');
+    for (var i = 0; i < pages.length; i++) {
+      pages[i].setAttribute('aria-hidden', node ? 'true' : 'false');
+    }
+
     overlay.hidden = !node;
     uber.invokeMethodOnParent(node ? 'beginInterceptingEvents' :
                                      'stopInterceptingEvents');
diff --git a/chrome/browser/resources/feedback.css b/chrome/browser/resources/feedback.css
deleted file mode 100644
index 9e2a9f2..0000000
--- a/chrome/browser/resources/feedback.css
+++ /dev/null
@@ -1,123 +0,0 @@
-/* Copyright (c) 2012 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. */
-
-#feedback-page {
-  -webkit-margin-start: 80px;
-  color: rgb(48, 57, 66);
-}
-
-#feedback-page > h1 {
-  border-bottom: 1px solid #eee;
-  padding-bottom: 6px;
-  width: 718px;
-}
-
-#content {
-  width: 600px;
-}
-
-#description {
-  display: block;
-  font-weight: 300;
-  text-align: start;
-  vertical-align: text-top;
-}
-
-#description-text {
-  box-sizing: border-box;
-  margin-top: 1em;
-  width: 100%;
-}
-
-.input-text-container {
-  -webkit-box-align: baseline;
-  display: -webkit-box;
-}
-
-.input-text-container > label {
-  -webkit-margin-end: 1em;
-  width: 150px;
-}
-
-.input-text-container > input {
-  -webkit-box-flex: 1;
-  display: block;
-  /* Don't push checkboxes out of alignment with bulky input. */
-  margin-bottom: -0.25em;
-}
-
-.thumbnail-list {
-  margin-bottom: 1em;
-  margin-top: 1em;
-  min-height: 151px;
-}
-
-.image-thumbnail-container {
-  border: 2px solid #ccc;
-  border-radius: 3px;
-  display: inline-block;
-}
-
-.image-thumbnail-container-selected:not(:only-of-type) {
-  border-color: green;
-}
-
-.image-thumbnail-container:not(:only-of-type):not(
-    .image-thumbnail-container-selected):hover {
-  border-color: rgb(184, 218, 176);
-}
-
-.image-thumbnail {
-  border: 2px solid white;
-}
-
-.image-thumbnail img {
-  display: block;
-  height: 140px;
-}
-
-#privacy-note {
-  margin-bottom: 0.8em;
-  width: 44em;
-}
-
-#buttons-pane {
-  /* Linux and Mac OS X display OK/Cancel dialogs in the reverse direction. */
-<if expr="not pp_ifdef('toolkit_views')">
-  -webkit-box-direction: reverse;
-</if>
-  display: -webkit-box;
-  padding: 0 0 12px;
-}
-
-#buttons-pane > input {
-  -webkit-margin-end: 0.8em;
-  display: block;
-}
-
-#attach-file-note {
-  -webkit-margin-start: 150px;
-  -webkit-padding-start: 1em;
-  padding-bottom: 20px
-  padding-top: 10px;
-}
-
-#attach-file {
-  margin: 0;
-}
-
-.attach-file-notification {
-  color: rgb(204, 0, 0);
-  font-weight: bold;
-  padding-left: 20px;
-}
-
-.launcher-layout #page-url,
-.launcher-layout #screenshot-row,
-.launcher-layout #title,
-.launcher-layout #description,
-html:not(.launcher-layout) #launcher-title,
-html:not(.launcher-layout) #launcher-description {
-  display: none;
-}
diff --git a/chrome/browser/resources/feedback.html b/chrome/browser/resources/feedback.html
deleted file mode 100644
index b5ea6cc..0000000
--- a/chrome/browser/resources/feedback.html
+++ /dev/null
@@ -1,112 +0,0 @@
-<!DOCTYPE HTML>
-<html i18n-values="dir:textdirection;">
-<head>
-<meta charset="utf-8">
-<title i18n-content="page-title"></title>
-<link rel="stylesheet" href="chrome://resources/css/widgets.css">
-<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
-<link rel="stylesheet" href="feedback.css">
-
-<script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://resources/js/load_time_data.js"></script>
-<script src="chrome://resources/js/util.js"></script>
-<script src="chrome://resources/js/cr/ui.js"></script>
-<script src="chrome://resources/js/cr/ui/focus_manager.js"></script>
-<script src="chrome://feedback/feedback.js"></script>
-<script src="chrome://feedback/strings.js"></script>
-</head>
-<body id="feedback-page"
-    i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
-  <h1 id="title" i18n-content="title"></h1>
-  <h1 id="launcher-title" i18n-content="launcher-title"></h1>
-  <div id="content">
-    <span id="description" i18n-content="description"></span>
-    <span id="launcher-description" i18n-content="launcher-description"></span>
-    <textarea id="description-text" rows="10"></textarea>
-    <div id="page-url" class="input-text-container checkbox">
-      <label>
-        <input id="page-url-checkbox" type="checkbox"
-            value="pageurl" checked>
-        <span id="page-url-label" i18n-content="page-url"></span>
-      </label>
-      <input id="page-url-text">
-    </div>
-    <!-- User e-mail -->
-    <div id="user-email" class="input-text-container checkbox">
-      <label>
-        <input id="user-email-checkbox" type="checkbox">
-        <span id="user-email-label" i18n-content="user-email"></span>
-      </label>
-      <input id="user-email-text">
-    </div>
-<if expr="pp_ifdef('chromeos')">
-    <!-- Attach a file -->
-    <!-- Normal -->
-    <div id="attach-file-container" class="input-text-container checkbox">
-      <label>
-        <input id="attach-file-checkbox" type="checkbox">
-        <span i18n-content="attach-file-label"></span>
-      </label>
-      <input id="attach-file" type="file">
-      <div id="attach-error" class="attach-file-notification"
-          i18n-content="attach-file-to-big" hidden></div>
-      <div id="reading-file" class="attach-file-notification"
-          i18n-content="reading-file" hidden></div>
-    </div>
-    <!-- Custom -->
-    <div id="attach-file-custom-container" class="input-text-container checkbox"
-      hidden>
-      <label>
-        <input id="attach-file-custom-checkbox" type="checkbox" checked>
-        <span i18n-content="attach-file-label"></span>
-      </label>
-      <span id="attach-file-custom-name" ></span>
-    </div>
-    <div id="attach-file-note" i18n-values=".innerHTML:attach-file-note"></div>
-
-    <!-- System Information -->
-    <div class="checkbox">
-      <label>
-        <input id="sys-info-checkbox" type="checkbox" checked>
-        <span id="sysinfo-label">
-          <a id="sysinfo-url" href="#" i18n-content="sysinfo"></a>
-        </span>
-      </label>
-    </div>
-</if>
-    <!-- Screenshot -->
-    <div id="screenshot-row" hidden>
-      <div class="checkbox">
-        <label>
-          <input id="screenshot-checkbox" type="checkbox">
-          <span id="screenshot-label-current"
-              i18n-content="current-screenshot"></span>
-<if expr="pp_ifdef('chromeos')">
-          <span id="screenshot-label-saved"
-              i18n-content="saved-screenshot" hidden></span>
-        </label>
-        <a id="screenshot-link-tosaved" href="#"
-            i18n-content="choose-different-screenshot" hidden>
-        </a>
-        <a id="screenshot-link-tocurrent" href="#"
-            i18n-content="choose-original-screenshot" hidden>
-        </a>
-</if>
-      </div>
-<if expr="pp_ifdef('chromeos')">
-      <div id="saved-screenshots" class="thumbnail-list" hidden></div>
-</if>
-      <div id="current-screenshots" class="thumbnail-list"></div>
-    </div>
-    <div id="privacy-note" i18n-values=".innerHTML:privacy-note"></div>
-    <!-- Buttons -->
-    <div id="buttons-pane">
-      <input id="send-report-button" type="submit"
-          class="feedback-button" i18n-values="value:send-report">
-      <input id="cancel-button" type="submit"
-          class="feedback-button" i18n-values="value:cancel">
-    </div>
-  </div>
-  <script src="chrome://resources/js/i18n_template2.js"></script>
-</body>
-</html>
diff --git a/chrome/browser/resources/feedback.js b/chrome/browser/resources/feedback.js
deleted file mode 100644
index 6cb16f6..0000000
--- a/chrome/browser/resources/feedback.js
+++ /dev/null
@@ -1,413 +0,0 @@
-// Copyright (c) 2012 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.
-
-// Constants.
-/** @const */ var FEEDBACK_LANDING_PAGE =
-    'https://www.google.com/support/chrome/go/feedback_confirmation';
-/** @const */ var MAX_ATTACH_FILE_SIZE = 3 * 1024 * 1024;
-
-var selectedThumbnailDivId = '';
-var selectedThumbnailId = '';
-var selectedImageUrl;
-
-var savedThumbnailIds = [];
-savedThumbnailIds['current-screenshots'] = '';
-savedThumbnailIds['saved-screenshots'] = '';
-
-var categoryTag = '';
-var filePath = '';
-var forceDisableScreenshots = false;
-
-// Globals to manage reading data from the attach a file option.
-var attachFileBinaryData = '';
-var lastReader = null;
-
-/**
- * Returns the base filename for a given path. Handles only Unix style paths.
- * @param {string} path The path to return the basename for.
- * @return {string} Basename for the path.
- */
-function getBaseName(path) {
-  lastSeparator = path.lastIndexOf('/');
-  if (lastSeparator == -1)
-    return '';
-  else
-    return path.substr(lastSeparator + 1);
-}
-
-/**
- * Selects an image thumbnail in the specified div.
- * @param {string} divId The id of the div to search in.
- * @param {string} thumbnailId The id of the thumbnail to search for.
- */
-function selectImage(divId, thumbnailId) {
-  var thumbnailDivs = $(divId).children;
-  selectedThumbnailDivId = divId;
-  if (thumbnailDivs.length == 0) {
-    $(divId).hidden = true;
-    return;
-  }
-
-  for (var i = 0; i < thumbnailDivs.length; i++) {
-    thumbnailDivs[i].className = 'image-thumbnail-container';
-
-    // If the the current div matches the thumbnail id provided,
-    // or there is no thumbnail id given, and we're at the first thumbnail.
-    if (thumbnailDivs[i].id == thumbnailId || (!thumbnailId && !i)) {
-      thumbnailDivs[i].classList.add('image-thumbnail-container-selected');
-      selectedThumbnailId = thumbnailDivs[i].id;
-      savedThumbnailIds[divId] = thumbnailId;
-    }
-  }
-}
-
-/**
- * Adds an image thumbnail to the specified div.
- * @param {string} divId The id of the div to add a screenshot to.
- * @param {string} screenshot The URL of the screenshot being added.
- */
-function addScreenshot(divId, screenshot) {
-  var thumbnailDiv = document.createElement('div');
-  thumbnailDiv.className = 'image-thumbnail-container';
-
-  thumbnailDiv.id = divId + '-thumbnailDiv-' + $(divId).children.length;
-  thumbnailDiv.onclick = function() {
-    selectImage(divId, thumbnailDiv.id);
-  };
-
-  var innerDiv = document.createElement('div');
-  innerDiv.className = 'image-thumbnail';
-
-  var thumbnail = document.createElement('img');
-  thumbnail.id = thumbnailDiv.id + '-image';
-  // We add the ?+timestamp to make sure the image URLs are unique
-  // and Chrome does not load the image from cache.
-  thumbnail.src = screenshot + '?' + Date.now();
-  innerDiv.appendChild(thumbnail);
-
-  thumbnailDiv.appendChild(innerDiv);
-  $(divId).appendChild(thumbnailDiv);
-
-  if (!selectedThumbnailId)
-    selectImage(divId, thumbnailDiv.id);
-}
-
-/**
- * Enables screenshots.
- */
-function enableScreenshots() {
-  if (forceDisableScreenshots)
-    return;
-  $('screenshot-row').hidden = false;
-}
-
-/**
- * Reads the selected file when the user selects a file.
- * @param {event} evtFileSelected The on changed event for the file input box.
- */
-function onFileSelected(evtFileSelected) {
-  var file = evtFileSelected.target.files[0];
-  if (!file) {
-    // User canceled file selection.
-    $('attach-file-checkbox').checked = false;
-    attachFileBinaryData = null;
-    return;
-  }
-
-  if (file.size > MAX_ATTACH_FILE_SIZE) {
-    $('attach-error').hidden = false;
-
-    // Clear our selected file.
-    $('attach-file').value = '';
-    attachFileBinaryData = null;
-    $('attach-file-checkbox').checked = false;
-
-    return;
-  }
-
-  $('attach-error').hidden = true;
-
-  // Abort an existing file read operation if one exists.
-  if (lastReader) {
-    lastReader.abort();
-    lastReader = null;
-  }
-
-  var reader = new FileReader();
-  reader.onloadend = function(evtLoadEnd) {
-    if (evtLoadEnd.target.readyState == FileReader.DONE) {
-      attachFileBinaryData = evtLoadEnd.target.result;
-      lastReader = null;
-      // Check the checkbox so we do send this file. Users can uncheck the
-      // box if they don't want to send the file.
-      $('attach-file-checkbox').checked = true;
-      $('reading-file').hidden = true;
-      $('send-report-button').disabled = false;
-    }
-  };
-
-  lastReader = reader;
-  reader.readAsBinaryString(file);
-  $('reading-file').hidden = false;
-  $('send-report-button').disabled = true;
-}
-
-/**
- * Sends the report; after the report is sent, we need to be redirected to
- * the landing page, but we shouldn't be able to navigate back, hence
- * we open the landing page in a new tab and sendReport closes this tab.
- * @return {boolean} True if the report was sent.
- */
-function sendReport() {
-  if ($('description-text').value.length == 0) {
-    alert(loadTimeData.getString('no-description'));
-    return false;
-  }
-
-  var imagePath = '';
-  if ($('screenshot-checkbox').checked && selectedThumbnailId)
-    imagePath = $(selectedThumbnailId + '-image').src;
-  var pageUrl = $('page-url-text').value;
-  if (!$('page-url-checkbox').checked)
-    pageUrl = '';
-  var userEmail = $('user-email-text').value;
-  if (!$('user-email-checkbox').checked)
-    userEmail = '';
-
-  var reportArray = [pageUrl,
-                     categoryTag,
-                     $('description-text').value,
-                     userEmail,
-                     imagePath];
-
-  // Add chromeos data if it exists.
-  if ($('sys-info-checkbox')) {
-    reportArray = reportArray.concat([String($('sys-info-checkbox').checked)]);
-  }
-
-  if ($('attach-file-checkbox') &&
-      $('attach-file-checkbox').checked) {
-    if (attachFileBinaryData) {
-      reportArray = reportArray.concat(
-          [$('attach-file').files[0].name, btoa(attachFileBinaryData)]);
-    }
-  } else if ($('attach-file-custom-checkbox') &&
-             $('attach-file-custom-checkbox').checked) {
-    if (filePath)
-      reportArray = reportArray.concat([filePath, '']);
-  }
-
-  // open the landing page in a new tab, sendReport will close this one.
-  window.open(FEEDBACK_LANDING_PAGE, '_blank');
-  chrome.send('sendReport', reportArray);
-  return true;
-}
-
-/**
- * Click listener for the cancel button.
- * @param {Event} e The click event being handled.
- */
-function cancel(e) {
-  chrome.send('cancel');
-  e.preventDefault();
-}
-
-/**
- * Select the current screenshots div, restoring the image that was
- * selected when we had this div open previously.
- */
-function currentSelected() {
-  // TODO(rkc): Change this to use a class instead.
-  $('current-screenshots').hidden = false;
-  if ($('saved-screenshots'))
-    $('saved-screenshots').hidden = true;
-
-  if (selectedThumbnailDivId != 'current-screenshots')
-    selectImage('current-screenshots',
-                savedThumbnailIds['current-screenshots']);
-}
-
-/**
- * Select the saved screenshots div, restoring the image that was
- * selected when we had this div open previously.
- */
-function savedSelected() {
-  if ($('saved-screenshots').childElementCount == 0) {
-    // setupSavedScreenshots will take care of changing visibility
-    chrome.send('refreshSavedScreenshots');
-  } else {
-    $('current-screenshots').hidden = true;
-    $('saved-screenshots').hidden = false;
-    if (selectedThumbnailDivId != 'saved-screenshots')
-      selectImage('saved-screenshots', savedThumbnailIds['saved-screenshots']);
-  }
-}
-
-/**
- * Change the type of screenshot we're showing to the user from
- * the current screenshot to saved screenshots
- */
-function changeToSaved() {
-  $('screenshot-label-current').hidden = true;
-  $('screenshot-label-saved').hidden = false;
-
-  // Change the link to say "go to original"
-  $('screenshot-link-tosaved').hidden = true;
-  $('screenshot-link-tocurrent').hidden = false;
-
-  savedSelected();
-}
-
-/**
- * Change the type of screenshot we're showing to the user from
- * the saved screenshots to the current screenshots
- */
-function changeToCurrent() {
-  $('screenshot-label-current').hidden = false;
-  $('screenshot-label-saved').hidden = true;
-
-  // Change the link to say "go to saved"
-  $('screenshot-link-tosaved').hidden = false;
-  $('screenshot-link-tocurrent').hidden = true;
-
-  currentSelected();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Document Functions:
-/**
- * Window onload handler, sets up the page.
- */
-function load() {
-  cr.ui.FocusManager.disableMouseFocusOnButtons();
-  if ($('attach-file'))
-    $('attach-file').addEventListener('change', onFileSelected);
-
-  if ($('sysinfo-url')) {
-    $('sysinfo-url').onclick = function(event) {
-      chrome.send('openSystemTab');
-    };
-  }
-
-<if expr="pp_ifdef('chromeos')">
-  $('screenshot-link-tosaved').onclick = changeToSaved;
-  $('screenshot-link-tocurrent').onclick = changeToCurrent;
-</if>
-  $('send-report-button').onclick = sendReport;
-  $('cancel-button').onclick = cancel;
-
-  // Set default values for the possible parameters, and then parse the actual
-  // values from the URL href.
-  var parameters = {
-    'description': '',
-    'categoryTag': '',
-    'customPageUrl': '',
-    'filePath': '',
-  };
-
-  var loc = window.location;
-  // Split the query string into an array of parameters.
-  var query = loc.search.substr(1).split('&');
-  // If we have a query in the hash.
-  if (loc.hash.indexOf('?') >= 0) {
-    // Remove the hash and split this query into parameters too.
-    query = query.concat(loc.hash.substr(loc.hash.indexOf('?') + 1).split('&'));
-  }
-  for (var i = 0; i < query.length; i++) {
-    // Decode and store each parameter value.
-    parameter = query[i].split('=');
-    parameters[parameter[0]] = decodeURIComponent(parameter[1]);
-  }
-
-  // Set the initial description text.
-  $('description-text').textContent = parameters['description'];
-  // If a page url is spcified in the parameters, override the default page url.
-  if (parameters['customPageUrl'] != '') {
-    $('page-url-text').value = parameters['customPageUrl'];
-    // and disable the page image, since it doesn't make sense on a custom url.
-    $('screenshot-checkbox').checked = false;
-    forceDisableScreenshots = true;
-  }
-
-  // Pick up the category tag (for most cases this will be an empty string)
-  categoryTag = parameters['categoryTag'];
-
-  // Pick up the file path for the attached file (only user for this at the
-  // moment is the quick office extension).
-  filePath = parameters['filePath'];
-
-  if (filePath != '') {
-    var baseName = getBaseName(filePath);
-    if (baseName) {
-      // Don't let the user choose another file, we were invoked by an
-      // extension already providing us the file, this report should only
-      // attach that file, or no file at all.
-      $('attach-file-container').hidden = true;
-
-      // Set our filename and unhide the "Attach this file" span.
-      $('attach-file-custom-name').textContent = baseName;
-      $('attach-file-custom-container').hidden = false;
-      // No screenshots if we're being invoked by an extension - screenshot was
-      // never taken.
-      $('screenshot-checkbox').checked = false;
-      forceDisableScreenshots = true;
-    } else {
-      filePath = '';
-    }
-  }
-
-  chrome.send('getDialogDefaults');
-  chrome.send('refreshCurrentScreenshot');
-}
-
-function setupCurrentScreenshot(screenshot) {
-  addScreenshot('current-screenshots', screenshot);
-}
-
-function setupSavedScreenshots(screenshots) {
-  if (screenshots.length == 0) {
-    $('saved-screenshots').textContent =
-        loadTimeData.getString('no-saved-screenshots');
-
-    // Make sure we make the display the message.
-    $('current-screenshots').hidden = true;
-    $('saved-screenshots').hidden = false;
-
-    // In case the user tries to send now; fail safe, do not send a screenshot
-    // at all versus sending the current screenshot.
-    selectedThumbnailDivId = '';
-    selectedThumbnailId = '';
-  } else {
-    $('saved-screenshots').textContent = '';
-    for (i = 0; i < screenshots.length; ++i)
-      addScreenshot('saved-screenshots', screenshots[i]);
-
-    // Now that we have our screenshots, try selecting the saved screenshots
-    // again.
-    savedSelected();
-  }
-}
-
-function setupDialogDefaults(defaults) {
-  // Current url.
-  if ($('page-url-text').value == '')
-    $('page-url-text').value = defaults.currentUrl;
-  if (defaults.currentUrl == '')
-    $('page-url-checkbox').checked = false;
-  // User e-mail.
-  $('user-email-text').value = defaults.userEmail;
-  $('user-email-checkbox').checked = defaults.emailCheckboxDefault;
-
-  document.documentElement.classList.toggle('launcher-layout',
-                                            defaults.launcherFeedback);
-
-  if (!defaults.disableScreenshots)
-    enableScreenshots();
-
-  if (defaults.useSaved) {
-    $('screenshot-link-tosaved').hidden = false;
-  }
-}
-
-window.addEventListener('DOMContentLoaded', load);
diff --git a/chrome/browser/resources/feedback/css/feedback.css b/chrome/browser/resources/feedback/css/feedback.css
new file mode 100644
index 0000000..ff1b4b7
--- /dev/null
+++ b/chrome/browser/resources/feedback/css/feedback.css
@@ -0,0 +1,137 @@
+/* Copyright 2013 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. */
+
+
+html {
+  height: 100%;
+}
+
+body {
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  width: 100%;
+}
+
+.title-bar {
+  -webkit-align-items: center;
+  background-color: #fff;
+  box-shadow: 0 1px #d0d0d0;
+  color: rgb(80, 80, 82);
+  display: -webkit-flex;
+  font-size: 15px;
+  height: 48px;
+}
+
+.title-bar #page-title {
+  -webkit-app-region: drag;
+  -webkit-flex: 1 1 auto;
+  -webkit-margin-start: 20px;
+}
+
+.title-bar .button-bar {
+  -webkit-flex: 0 1 auto;
+}
+
+.content {
+  background-color: #fbfbfb;
+  color: #646464;
+  font-size: 12px;
+  margin: 20px;
+}
+
+.content #description-text {
+  border-color: #c8c8c8;
+  box-sizing: border-box;
+  height: 120px;
+  line-height: 18px;
+  padding: 10px;
+  resize: none;
+  width: 100%;
+}
+
+.content .text-field-container {
+  -webkit-align-items: center;
+  -webkit-padding-start: 10px;
+  display: -webkit-flex;
+  height: 29px;
+  margin-top: 10px;
+}
+
+.content .text-field-container > label {
+  -webkit-flex: 0 1 auto;
+  width: 100px;
+}
+
+.content .text-field-container > input[type=text] {
+  -webkit-flex: 1 1 auto;
+  -webkit-padding-start: 5px;
+  border: 1px solid;
+  border-color: #c8c8c8;
+  color: #585858;
+  height: 100%;
+}
+
+.content .text-field-container > input[type=checkbox] {
+  margin-right: 9px;
+}
+
+.content .checkbox-field-container {
+  -webkit-align-items: center;
+  display: -webkit-flex;
+  height: 29px;
+}
+
+#screenshot-container {
+  margin-top: 30px;
+}
+
+.content #screenshot-image {
+  -webkit-margin-start: 30px;
+  display: block;
+  height: 60px;
+  margin-top: 40px;
+  transition: all 250ms ease;
+}
+
+.content #screenshot-image:hover {
+  height: 205px;
+  margin-top: 80px;
+  z-index: 1;
+}
+
+.content #privacy-note {
+  color: #969696;
+  font-size: 10px;
+  line-height: 15px;
+  margin-bottom: 20px;
+  margin-top: 40px;
+}
+
+.content .buttons-pane {
+  display: -webkit-flex;
+  justify-content: flex-end
+}
+
+.content #attach-file-note {
+  -webkit-margin-start: 112px;
+  margin-bottom: 10px;
+  margin-top: 10px;
+}
+
+.content .attach-file-notification {
+  -webkit-padding-start: 20px;
+  color: rgb(204, 0, 0);
+  font-weight: bold;
+}
+
+button.white-button {
+  -webkit-margin-end: 10px;
+  color: #000;
+}
+
+button.blue-button {
+  color: #fff;
+  text-shadow: 1px sharp drop shadow rgb(45, 106, 218);
+}
diff --git a/chrome/browser/resources/feedback/html/default.html b/chrome/browser/resources/feedback/html/default.html
new file mode 100644
index 0000000..3455d04
--- /dev/null
+++ b/chrome/browser/resources/feedback/html/default.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html i18n-values="dir:textdirection;">
+<head>
+<meta charset="utf-8">
+<link rel="stylesheet" href="chrome://resources/css/apps/common.css"></link>
+<link rel="stylesheet" href="chrome://resources/css/apps/topbutton_bar.css"></link>
+<link rel="stylesheet" href="../css/feedback.css">
+
+<script src="chrome://resources/js/load_time_data.js"></script>
+<script src="chrome://resources/js/i18n_template_no_process.js"></script>
+<script src="chrome://resources/js/util.js"></script>
+<script src="../js/take_screenshot.js"></script>
+<script src="../js/topbar_handlers.js"></script>
+<script src="../js/feedback.js"></script>
+</head>
+<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
+  <div class="title-bar">
+    <span id="page-title" i18n-content="page-title"></span>
+    <span class="topbutton-bar">
+      <button class="minimize-button" id="minimize-button" tabindex="-1">
+      </button>
+      <button class="close-button" id="close-button" tabindex="-1">
+      </button>
+    </span>
+  </div>
+  <div class="content">
+    <textarea id="description-text"></textarea>
+    <div id="page-url" class="text-field-container">
+      <label id="page-url-label" i18n-content="page-url"></label>
+      <input id="page-url-text" type="text">
+    </div>
+    <!-- User e-mail -->
+    <div id="user-email" class="text-field-container">
+      <label id="user-email-label" i18n-content="user-email"></label>
+      <input id="user-email-text" type="text">
+    </div>
+    <!-- Attach a file -->
+    <div id="attach-file-container" class="text-field-container">
+      <label i18n-content="attach-file-label"></label>
+      <input id="attach-file" type="file">
+      <div id="attach-error" class="attach-file-notification"
+          i18n-content="attach-file-to-big" hidden></div>
+    </div>
+    <div id="attach-file-note" i18n-content="attach-file-note"></div>
+    <!-- Screenshot -->
+    <div id="screenshot-container" class="checkbox-field-container">
+      <input id="screenshot-checkbox" type="checkbox" checked>
+      <label id="screenshot-label" i18n-content="screenshot"></label>
+      <img id="screenshot-image">
+    </div>
+<if expr="pp_ifdef('chromeos')">
+    <!-- System Information -->
+    <div class="checkbox-field-container">
+      <input id="sys-info-checkbox" type="checkbox" checked>
+      <span id="sysinfo-label">
+        <a id="sysinfo-url" href="#" i18n-content="sysinfo"></a>
+      </span>
+    </div>
+</if>
+    <!-- Privacy node -->
+    <div id="privacy-note" i18n-values=".innerHTML:privacy-note"></div>
+    <!-- Buttons -->
+    <div class="buttons-pane">
+      <button id="cancel-button" type="submit"
+          class="white-button" i18n-content="cancel">
+      </button>
+      <button id="send-report-button" type="submit"
+          class="blue-button" i18n-content="send-report">
+      </button>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/chrome/browser/resources/feedback/js/event_handler.js b/chrome/browser/resources/feedback/js/event_handler.js
new file mode 100644
index 0000000..309ecc3
--- /dev/null
+++ b/chrome/browser/resources/feedback/js/event_handler.js
@@ -0,0 +1,49 @@
+// Copyright 2013 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.
+
+/**
+ * @type {number}
+ * @const
+ */
+var FEEDBACK_WIDTH = 500;
+/**
+ * @type {number}
+ * @const
+ */
+var FEEDBACK_HEIGHT = 610;
+
+var initialFeedbackInfo = null;
+
+/**
+ * Callback which gets notified once our feedback UI has loaded and is ready to
+ * receive its initial feedback info object.
+ * @param {Object} request The message request object.
+ * @param {Object} sender The sender of the message.
+ * @param {function(Object)} sendResponse Callback for sending a response.
+ */
+function feedbackReady(request, sender, sendResponse) {
+  if (request.ready) {
+    chrome.runtime.sendMessage(
+        {sentFromEventPage: true, data: initialFeedbackInfo});
+  }
+}
+
+/**
+ * Callback which starts up the feedback UI.
+ * @param {Object} feedbackInfo Object containing any initial feedback info.
+ */
+function startFeedbackUI(feedbackInfo) {
+  initialFeedbackInfo = feedbackInfo;
+  chrome.app.window.create('html/default.html', {
+      frame: 'none',
+      id: 'default_window',
+      width: FEEDBACK_WIDTH,
+      height: FEEDBACK_HEIGHT,
+      hidden: true,
+      singleton: true },
+      function(appWindow) {});
+}
+
+chrome.runtime.onMessage.addListener(feedbackReady);
+chrome.feedbackPrivate.onFeedbackRequested.addListener(startFeedbackUI);
diff --git a/chrome/browser/resources/feedback/js/feedback.js b/chrome/browser/resources/feedback/js/feedback.js
new file mode 100644
index 0000000..80074c9
--- /dev/null
+++ b/chrome/browser/resources/feedback/js/feedback.js
@@ -0,0 +1,175 @@
+// Copyright 2013 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.
+
+/** @type {string}
+ * @const
+ */
+var FEEDBACK_LANDING_PAGE =
+    'https://www.google.com/support/chrome/go/feedback_confirmation';
+/** @type {number}
+ * @const
+ */
+var MAX_ATTACH_FILE_SIZE = 3 * 1024 * 1024;
+
+var attachedFileBlob = null;
+var lastReader = null;
+
+var feedbackInfo = null;
+var systemInfo = null;
+
+/**
+ * Reads the selected file when the user selects a file.
+ * @param {Event} fileSelectedEvent The onChanged event for the file input box.
+ */
+function onFileSelected(fileSelectedEvent) {
+  $('attach-error').hidden = true;
+  var file = fileSelectedEvent.target.files[0];
+  if (!file) {
+    // User canceled file selection.
+    attachedFileBlob = null;
+    return;
+  }
+
+  if (file.size > MAX_ATTACH_FILE_SIZE) {
+    $('attach-error').hidden = false;
+
+    // Clear our selected file.
+    $('attach-file').value = '';
+    attachedFileBlob = null;
+    return;
+  }
+
+  attachedFileBlob = file.slice();
+}
+
+/**
+ * Opens a new tab with chrome://system, showing the current system info.
+ */
+function openSystemTab() {
+  window.open('chrome://system', '_blank');
+}
+
+/**
+ * Sends the report; after the report is sent, we need to be redirected to
+ * the landing page, but we shouldn't be able to navigate back, hence
+ * we open the landing page in a new tab and sendReport closes this tab.
+ * @return {boolean} True if the report was sent.
+ */
+function sendReport() {
+  if ($('description-text').value.length == 0) {
+    $('description-text').placeholder =
+        loadTimeData.getString('no-description');
+    return false;
+  }
+
+  console.log('Feedback: Sending report');
+  if (attachedFileBlob) {
+    feedbackInfo.attachedFile = { name: $('attach-file').value,
+                                  data: attachedFileBlob };
+  }
+
+  feedbackInfo.description = $('description-text').value;
+  feedbackInfo.pageUrl = $('page-url-text').value;
+  feedbackInfo.email = $('user-email-text').value;
+
+  if ($('sys-info-checkbox') != null &&
+      $('sys-info-checkbox').checked &&
+      systemInfo != null) {
+    if (feedbackInfo.systemInformation != null) {
+      // Concatenate sysinfo if we had any initial system information
+      // sent with the feedback request event.
+      feedbackInfo.systemInformation =
+          feedbackInfo.systemInformation.concat(systemInfo);
+    } else {
+      feedbackInfo.systemInformation = systemInfo;
+    }
+  }
+
+  chrome.feedbackPrivate.sendFeedback(feedbackInfo, function(result) {
+    window.open(FEEDBACK_LANDING_PAGE, '_blank');
+    window.close();
+  });
+
+  return true;
+}
+
+/**
+ * Click listener for the cancel button.
+ * @param {Event} e The click event being handled.
+ */
+function cancel(e) {
+  e.preventDefault();
+  window.close();
+}
+
+/**
+ * Converts a blob data URL to a blob object.
+ * @param {string} url The data URL to convert.
+ * @return {Blob} Blob object containing the data.
+ */
+function dataUrlToBlob(url) {
+  var mimeString = url.split(',')[0].split(':')[1].split(';')[0];
+  var data = atob(url.split(',')[1]);
+  var dataArray = [];
+  for (var i = 0; i < data.length; ++i)
+    dataArray.push(data.charCodeAt(i));
+
+  return new Blob([new Uint8Array(dataArray)], {type: mimeString});
+}
+
+/**
+ * Initializes our page.
+ * Flow:
+ * .) DOMContent Loaded        -> . Request feedbackInfo object
+ *                                . Setup page event handlers
+ * .) Feedback Object Received -> . take screenshot
+ *                                . request email
+ *                                . request System info
+ *                                . request i18n strings
+ * .) Screenshot taken         -> . Show Feedback window.
+ */
+function initialize() {
+  // Add listener to receive the feedback info object.
+  chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
+    if (request.sentFromEventPage) {
+      feedbackInfo = request.data;
+      $('description-text').textContent = feedbackInfo.description;
+      $('page-url-text').value = feedbackInfo.pageUrl;
+
+      takeScreenshot(function(screenshotDataUrl) {
+        $('screenshot-image').src = screenshotDataUrl;
+        feedbackInfo.screenshot = dataUrlToBlob(screenshotDataUrl);
+        chrome.app.window.current().show();
+      });
+
+      chrome.feedbackPrivate.getUserEmail(function(email) {
+        $('user-email-text').value = email;
+      });
+
+      chrome.feedbackPrivate.getSystemInformation(function(sysInfo) {
+        systemInfo = sysInfo;
+      });
+
+      chrome.feedbackPrivate.getStrings(function(strings) {
+        loadTimeData.data = strings;
+        i18nTemplate.process(document, loadTimeData);
+      });
+    }
+  });
+
+  window.addEventListener('DOMContentLoaded', function() {
+    // Ready to receive the feedback object.
+    chrome.runtime.sendMessage({ready: true});
+
+    // Setup our event handlers.
+    $('attach-file').addEventListener('change', onFileSelected);
+    $('send-report-button').onclick = sendReport;
+    $('cancel-button').onclick = cancel;
+    if ($('sysinfo-url')) {
+      $('sysinfo-url').onclick = openSystemTab;
+    }
+  });
+}
+
+initialize();
diff --git a/chrome/browser/resources/feedback/js/take_screenshot.js b/chrome/browser/resources/feedback/js/take_screenshot.js
new file mode 100644
index 0000000..99b58b5
--- /dev/null
+++ b/chrome/browser/resources/feedback/js/take_screenshot.js
@@ -0,0 +1,44 @@
+// Copyright 2013 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.
+
+/**
+ * Function to take the screenshot of the current screen.
+ * @param {function(string)} callback Callback for returning the data URL to the
+ *                           screenshot.
+ */
+function takeScreenshot(callback) {
+  var streaming = false;
+  var video = document.createElement('video');
+
+  video.addEventListener('canplay', function(e) {
+    if (!streaming) {
+      streaming = true;
+
+      var canvas = document.createElement('canvas');
+      canvas.setAttribute('width', video.videoWidth);
+      canvas.setAttribute('height', video.videoHeight);
+      canvas.getContext('2d').drawImage(
+          video, 0, 0, video.videoWidth, video.videoHeight);
+
+      video.pause();
+      video.src = '';
+
+      callback(canvas.toDataURL('image/png'));
+    }
+  }, false);
+
+  navigator.webkitGetUserMedia(
+    {
+      video: {mandatory: {chromeMediaSource: 'screen'}}
+    },
+    function(stream) {
+      video.src = window.webkitURL.createObjectURL(stream);
+      video.play();
+    },
+    function(err) {
+      console.error('takeScreenshot failed: ' +
+          err.name + '; ' + err.message + '; ' + err.constraintName);
+    }
+  );
+}
diff --git a/chrome/browser/resources/feedback/js/topbar_handlers.js b/chrome/browser/resources/feedback/js/topbar_handlers.js
new file mode 100644
index 0000000..793e0e5
--- /dev/null
+++ b/chrome/browser/resources/feedback/js/topbar_handlers.js
@@ -0,0 +1,27 @@
+// Copyright 2013 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.
+
+/**
+ * Setup handlers for the minimize and close topbar buttons.
+ */
+function initializeHandlers() {
+  $('minimize-button').addEventListener('click', function(e) {
+    e.preventDefault();
+    chrome.app.window.current().minimize();
+  });
+
+  $('minimize-button').addEventListener('mousedown', function(e) {
+    e.preventDefault();
+  });
+
+  $('close-button').addEventListener('click', function() {
+    window.close();
+  });
+
+  $('close-button').addEventListener('mousedown', function(e) {
+    e.preventDefault();
+  });
+}
+
+window.addEventListener('DOMContentLoaded', initializeHandlers);
diff --git a/chrome/browser/resources/feedback/manifest.json b/chrome/browser/resources/feedback/manifest.json
new file mode 100644
index 0000000..a60d3a3
--- /dev/null
+++ b/chrome/browser/resources/feedback/manifest.json
@@ -0,0 +1,18 @@
+{
+  // chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMZElzFX2J1g1nRQ/8S3rg/1CjFyDltWOxQg+9M8aVgNVxbutEWFQz+oQzIP9BB67mJifULgiv12ToFKsae4NpEUR8sPZjiKDIHumc6pUdixOm8SJ5Rs16SMR6+VYxFUjlVW+5CA3IILptmNBxgpfyqoK0qRpBDIhGk1KDEZ4zqQIDAQAB",
+  "name": "Feedback",
+  "version": "1.0",
+  "manifest_version": 2,
+  "description": "User feedback extension",
+  "permissions": [
+      "feedbackPrivate",
+      "chrome://resources/"
+  ],
+  "app": {
+    "background": {
+      "scripts": ["js/event_handler.js"]
+    },
+    "content_security_policy": "default-src 'none'; script-src 'self' chrome://resources; style-src 'unsafe-inline' *; img-src *; media-src 'self'"
+  }
+}
diff --git a/chrome/browser/resources/feedback_invalid.html b/chrome/browser/resources/feedback_invalid.html
deleted file mode 100644
index 330de1d..0000000
--- a/chrome/browser/resources/feedback_invalid.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-<meta charset="utf-8">
-</head>
-<title i18n-content="title"></title>
-<body style="-webkit-user-select: none">
-Invalid Feedback options - please check.
-</body>
-</html>
diff --git a/chrome/browser/resources/file_manager/css/common.css b/chrome/browser/resources/file_manager/css/common.css
index 2c6e080..5cff23a 100644
--- a/chrome/browser/resources/file_manager/css/common.css
+++ b/chrome/browser/resources/file_manager/css/common.css
@@ -263,8 +263,9 @@
   background-repeat: no-repeat;
   border: 5px solid transparent;
   border-image: -webkit-image-set(
-    url('../images/common/button.png') 1x,
-    url('../images/common/2x/button.png') 2x) 5 / 5px / 2px repeat;
+    url('chrome://resources/images/apps/button.png') 1x,
+    url('chrome://resources/images/2x/apps/button.png')
+        2x) 5 / 5px / 2px repeat;
   box-sizing: content-box;
   color: rgb(34, 34, 34);
   cursor: default;
@@ -288,8 +289,9 @@
 input[type='submit']:hover,
 select:hover {
   border-image: -webkit-image-set(
-    url('../images/common/button_hover.png') 1x,
-    url('../images/common/2x/button_hover.png') 2x) 5 / 5px / 2px repeat;
+    url('chrome://resources/images/apps/button_hover.png') 1x,
+    url('chrome://resources/images/2x/apps/button_hover.png')
+        2x) 5 / 5px / 2px repeat;
   color: #222;
 }
 
@@ -297,8 +299,9 @@
 input[type='button']:active,
 input[type='submit']:active {
   border-image: -webkit-image-set(
-    url('../images/common/button_pressed.png') 1x,
-    url('../images/common/2x/button_pressed.png') 2x) 5 / 5px / 2px repeat;
+    url('chrome://resources/images/apps/button_pressed.png') 1x,
+    url('chrome://resources/images/2x/apps/button_pressed.png')
+        2x) 5 / 5px / 2px repeat;
   color: #333;
 }
 
@@ -311,8 +314,9 @@
   background-color: rgb(250, 250, 250);
   background-image: none;
   border-image: -webkit-image-set(
-    url('../images/common/button.png') 1x,
-    url('../images/common/2x/button.png') 2x) 5 / 5px / 2px repeat;
+    url('chrome://resources/images/apps/button.png') 1x,
+    url('chrome://resources/images/2x/apps/button.png')
+        2x) 5 / 5px / 2px repeat;
   color: rgb(150, 150, 150);
 }
 
diff --git a/chrome/browser/resources/file_manager/css/file_manager.css b/chrome/browser/resources/file_manager/css/file_manager.css
index df84340..c06cbbc 100644
--- a/chrome/browser/resources/file_manager/css/file_manager.css
+++ b/chrome/browser/resources/file_manager/css/file_manager.css
@@ -4,12 +4,14 @@
 
 /* Special attribute used in HTML to hide elements. */
 body[type='folder'] [invisibleif~='folder'],
+body[type='upload-folder'] [invisibleif~='upload-folder'],
 body[type='saveas-file'] [invisibleif~='saveas-file'],
 body[type='open-file'] [invisibleif~='open-file'],
 body[type='open-multi-file'] [invisibleif~='open-multi-file'],
 body[type='full-page'] [invisibleif~='full-page'],
 
 body[type='folder'] [visibleif]:not([visibleif~='folder']),
+body[type='upload-folder'] [visibleif]:not([visibleif~='upload-folder']),
 body[type='saveas-file'] [visibleif]:not([visibleif~='saveas-file']),
 body[type='open-file'] [visibleif]:not([visibleif~='open-file']),
 body[type='open-multi-file'] [visibleif]:not([visibleif~='open-multi-file']),
@@ -420,29 +422,10 @@
 .dialog-header #search-box,
 .dialog-header #autocomplete-list,
 .dialog-header #search-icon,
-.dialog-header #search-clear-button,
-.dialog-header .buttonbar button {
+.dialog-header #search-clear-button {
   -webkit-app-region: no-drag;
 }
 
-.dialog-header #gear-button {
-  background-image: -webkit-image-set(
-    url('../images/files/ui/topbar_button_settings.png') 1x,
-    url('../images/files/ui/2x/topbar_button_settings.png') 2x);
-}
-
-.dialog-header #maximize-button {
-  background-image: -webkit-image-set(
-    url('../images/files/ui/topbar_button_maximize.png') 1x,
-    url('../images/files/ui/2x/topbar_button_maximize.png') 2x);
-}
-
-.dialog-header #close-button {
-  background-image: -webkit-image-set(
-    url('../images/files/ui/topbar_button_close.png') 1x,
-    url('../images/files/ui/2x/topbar_button_close.png') 2x);
-}
-
 /* Container for the detail and thumbnail list views. */
 .dialog-body {
   -webkit-box-flex: 1;
@@ -586,40 +569,6 @@
   width: 25px;
 }
 
-.dialog-header .buttonbar {
-  -webkit-box-align: center;
-  height: 100%;
-}
-
-.dialog-header .buttonbar button {
-  -webkit-box-shadow: none;
-  -webkit-margin-end: 10px;
-  -webkit-margin-start: 0;
-  background-color: transparent;
-  background-image: none;
-  background-position: center;
-  background-repeat: no-repeat;
-  border: 0 none transparent;
-  display: block;
-  height: 32px;
-  min-width: 0;
-  outline: none;
-  padding: 0;
-  width: 32px;
-}
-
-.dialog-header .buttonbar button:active {
-  -webkit-box-shadow:
-    0 1px 0 0 #c2c2c2 inset,
-    0 0 0 1px #dedede inset;
-}
-
-.dialog-header button:hover,
-.dialog-header button:focus,
-.dialog-header button:active {
-  background-color: #ededed;
-}
-
 #search-box,
 #filename-input-box input {
   border: 1px solid #c8c8c8;
@@ -1364,6 +1313,7 @@
     url('../images/files/ui/2x/onbutton_trash.png') 2x);
   background-position: center;
   background-repeat: no-repeat;
+  display: -webkit-box;
   min-width: 21px;  /* overrride */
   padding: 0;  /* overrride */
   width: 21px;
@@ -1378,6 +1328,10 @@
   padding-left: 32px;
 }
 
+#share-button {
+  min-width: 0;  /* overrride */
+}
+
 #preview-lines {
   -webkit-box-flex: 1;
   -webkit-margin-end: 10px;
@@ -1457,6 +1411,7 @@
 /* Dimmed items */
 
 body[type='folder'] .file,
+body[type='upload-folder'] .file,
 body[drive] .dialog-container[connection='offline'] .dim-offline {
   opacity: 0.4;
 }
@@ -1636,7 +1591,8 @@
   position: relative;
 }
 
-.buttonbar .tooltip {
+.buttonbar .tooltip,
+.topbutton-bar .tooltip {
   right: -12px;
   top: 35px;
 }
diff --git a/chrome/browser/resources/file_manager/css/gallery.css b/chrome/browser/resources/file_manager/css/gallery.css
index 1966f07..30aac69 100644
--- a/chrome/browser/resources/file_manager/css/gallery.css
+++ b/chrome/browser/resources/file_manager/css/gallery.css
@@ -1369,12 +1369,12 @@
 
 .gallery > .header > .maximize-button {
   background-image: -webkit-image-set(
-    url('../images/files/ui/topbar_button_maximize.png') 1x,
-    url('../images/files/ui/2x/topbar_button_maximize.png') 2x);
+    url('chrome://resources/images/apps/topbar_button_maximize.png') 1x,
+    url('chrome://resources/images/2x/apps/topbar_button_maximize.png') 2x);
 }
 
 .gallery > .header > .close-button {
   background-image: -webkit-image-set(
-    url('../images/files/ui/topbar_button_close.png') 1x,
-    url('../images/files/ui/2x/topbar_button_close.png') 2x);
+    url('chrome://resources/images/apps/topbar_button_close.png') 1x,
+    url('chrome://resources/images/2x/apps/topbar_button_close.png') 2x);
 }
diff --git a/chrome/browser/resources/file_manager/css/media_controls.css b/chrome/browser/resources/file_manager/css/media_controls.css
index 98745a0..ba167ee 100644
--- a/chrome/browser/resources/file_manager/css/media_controls.css
+++ b/chrome/browser/resources/file_manager/css/media_controls.css
@@ -41,6 +41,7 @@
   background: transparent;  /* Hide the standard slider bar */
   height: 100%;
   left: -2px;  /* Required to align the input element with the parent. */
+  outline: none;
   position: absolute;
   top: -2px;
   width: 100%;
diff --git a/chrome/browser/resources/file_manager/images/common/2x/button.png b/chrome/browser/resources/file_manager/images/common/2x/button.png
deleted file mode 100644
index 9fdd912..0000000
--- a/chrome/browser/resources/file_manager/images/common/2x/button.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/common/2x/button_hover.png b/chrome/browser/resources/file_manager/images/common/2x/button_hover.png
deleted file mode 100644
index 79f492e..0000000
--- a/chrome/browser/resources/file_manager/images/common/2x/button_hover.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/common/2x/button_pressed.png b/chrome/browser/resources/file_manager/images/common/2x/button_pressed.png
deleted file mode 100644
index 00cbed6..0000000
--- a/chrome/browser/resources/file_manager/images/common/2x/button_pressed.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/common/button.png b/chrome/browser/resources/file_manager/images/common/button.png
deleted file mode 100644
index 76bc97e..0000000
--- a/chrome/browser/resources/file_manager/images/common/button.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/common/button_hover.png b/chrome/browser/resources/file_manager/images/common/button_hover.png
deleted file mode 100644
index 34965e0..0000000
--- a/chrome/browser/resources/file_manager/images/common/button_hover.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/common/button_pressed.png b/chrome/browser/resources/file_manager/images/common/button_pressed.png
deleted file mode 100644
index 3c81b8b..0000000
--- a/chrome/browser/resources/file_manager/images/common/button_pressed.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/files/ui/2x/topbar_button_close.png b/chrome/browser/resources/file_manager/images/files/ui/2x/topbar_button_close.png
deleted file mode 100644
index 7a28084..0000000
--- a/chrome/browser/resources/file_manager/images/files/ui/2x/topbar_button_close.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/files/ui/2x/topbar_button_maximize.png b/chrome/browser/resources/file_manager/images/files/ui/2x/topbar_button_maximize.png
deleted file mode 100644
index 59377a4..0000000
--- a/chrome/browser/resources/file_manager/images/files/ui/2x/topbar_button_maximize.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/files/ui/2x/topbar_button_settings.png b/chrome/browser/resources/file_manager/images/files/ui/2x/topbar_button_settings.png
deleted file mode 100644
index b474c4c..0000000
--- a/chrome/browser/resources/file_manager/images/files/ui/2x/topbar_button_settings.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/files/ui/topbar_button_close.png b/chrome/browser/resources/file_manager/images/files/ui/topbar_button_close.png
deleted file mode 100644
index b902560..0000000
--- a/chrome/browser/resources/file_manager/images/files/ui/topbar_button_close.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/files/ui/topbar_button_maximize.png b/chrome/browser/resources/file_manager/images/files/ui/topbar_button_maximize.png
deleted file mode 100644
index 3bd557f..0000000
--- a/chrome/browser/resources/file_manager/images/files/ui/topbar_button_maximize.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/images/files/ui/topbar_button_settings.png b/chrome/browser/resources/file_manager/images/files/ui/topbar_button_settings.png
deleted file mode 100644
index ef0724a..0000000
--- a/chrome/browser/resources/file_manager/images/files/ui/topbar_button_settings.png
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/file_manager/js/butter_bar.js b/chrome/browser/resources/file_manager/js/butter_bar.js
index 72aa0a4..f372539 100644
--- a/chrome/browser/resources/file_manager/js/butter_bar.js
+++ b/chrome/browser/resources/file_manager/js/butter_bar.js
@@ -9,15 +9,13 @@
  * progress and other messages.
  * @param {HTMLElement} dialogDom FileManager top-level div.
  * @param {FileCopyManagerWrapper} copyManager The copy manager.
- * @param {MetadataCache} metadataCache The metadata cache.
  * @constructor
  */
-function ButterBar(dialogDom, copyManager, metadataCache) {
+function ButterBar(dialogDom, copyManager) {
   this.dialogDom_ = dialogDom;
   this.butter_ = this.dialogDom_.querySelector('#butter-bar');
   this.document_ = this.butter_.ownerDocument;
   this.copyManager_ = copyManager;
-  this.metadataCache_ = metadataCache;
   this.hideTimeout_ = null;
   this.showTimeout_ = null;
   this.lastShowTime_ = 0;
@@ -345,23 +343,16 @@
  * @private
  */
 ButterBar.prototype.onDelete_ = function(event) {
-  var urls = Array.prototype.slice.call(event.urls);
   switch (event.reason) {
     case 'BEGIN':
       if (this.currentMode_ != ButterBar.Mode.DELETE)
         this.totalDeleted_ = 0;
 
     case 'PROGRESS':
-      var props = [];
-      for (var i = 0; i < urls.length; i++) {
-        props[i] = {deleted: true};
-      }
-      this.metadataCache_.set(urls, 'internal', props);
-
-      this.totalDeleted_ += urls.length;
+      this.totalDeleted_ += event.urls.length;
       var title = strf('DELETED_MESSAGE_PLURAL', this.totalDeleted_);
       if (this.totalDeleted_ == 1) {
-        var fullPath = util.extractFilePath(urls[0]);
+        var fullPath = util.extractFilePath(event.urls[0]);
         var fileName = PathUtil.split(fullPath).pop();
         title = strf('DELETED_MESSAGE', fileName);
       }
@@ -376,12 +367,6 @@
       break;
 
     case 'ERROR':
-      var props = [];
-      for (var i = 0; i < urls.length; i++) {
-        props[i] = {deleted: false};
-      }
-      this.metadataCache_.set(urls, 'internal', props);
-
       this.showError_(str('DELETE_ERROR'));
       break;
 
diff --git a/chrome/browser/resources/file_manager/js/directory_contents.js b/chrome/browser/resources/file_manager/js/directory_contents.js
index de77b47..19f1874 100644
--- a/chrome/browser/resources/file_manager/js/directory_contents.js
+++ b/chrome/browser/resources/file_manager/js/directory_contents.js
@@ -170,13 +170,6 @@
 };
 
 /**
- * @return {string} The path.
- */
-DirectoryContents.prototype.getPath = function() {
-  throw 'Not implemented.';
-};
-
-/**
  * @return {boolean} If the scan is active.
  */
 DirectoryContents.prototype.isScanning = function() {
@@ -347,13 +340,6 @@
 };
 
 /**
- * @return {string} Current path.
- */
-DirectoryContentsBasic.prototype.getPath = function() {
-  return this.entry_.fullPath;
-};
-
-/**
  * @return {DirectoryEntry} DirectoryEntry of the current directory.
  */
 DirectoryContentsBasic.prototype.getDirectoryEntry = function() {
@@ -514,13 +500,6 @@
 };
 
 /**
- * @return {string} The path.
- */
-DirectoryContentsDriveSearch.prototype.getPath = function() {
-  return this.directoryEntry_.fullPath;
-};
-
-/**
  * Start directory scan.
  */
 DirectoryContentsDriveSearch.prototype.scan = function() {
@@ -599,13 +578,6 @@
 };
 
 /**
- * @return {string} The path.
- */
-DirectoryContentsLocalSearch.prototype.getPath = function() {
-  return this.directoryEntry_.fullPath;
-};
-
-/**
  * @return {boolean} True if search results (drive or local).
  */
 DirectoryContentsLocalSearch.prototype.isSearch = function() {
@@ -769,13 +741,6 @@
 };
 
 /**
- * @return {string} The path.
- */
-DirectoryContentsDriveSearchMetadata.prototype.getPath = function() {
-  return this.fakeDirEntry_.fullPath;
-};
-
-/**
  * Start directory scan/search operation. Either 'scan-completed' or
  * 'scan-failed' event will be fired upon completion.
  */
diff --git a/chrome/browser/resources/file_manager/js/directory_model.js b/chrome/browser/resources/file_manager/js/directory_model.js
index 4f9ff2c..77dda39 100644
--- a/chrome/browser/resources/file_manager/js/directory_model.js
+++ b/chrome/browser/resources/file_manager/js/directory_model.js
@@ -10,6 +10,57 @@
 // Used for operations that require almost instant rescan.
 var SHORT_RESCAN_INTERVAL = 100;
 
+function DirectoryModelUtil() {}
+
+/**
+ * Returns root entries asynchronously.
+ * @param {DirectoryEntry} root The root entry of the whole file system.
+ * @param {boolean} isDriveEnabled True if the drive is enabled.
+ * @param {function(Array.<Entry>)} callback Called when roots are resolved.
+ */
+DirectoryModelUtil.resolveRoots = function(root, isDriveEnabled, callback) {
+  var groups = {
+    drive: null,
+    downloads: null,
+    archives: null,
+    removables: null,
+  };
+
+  // Use a fake instead, to return a list as fast as possible.
+  groups.drive = (isDriveEnabled ? [DirectoryModel.fakeDriveEntry_] : []);
+
+  var addRootEntryList = function(key, rootEntryList) {
+    groups[key] = rootEntryList;
+
+    for (var key in groups) {
+      if (!groups[key]) {
+        // There is a pending task.
+        return;
+      }
+    }
+
+    // Concat the result in the order.
+    callback([].concat(
+        groups.drive, groups.downloads, groups.archives, groups.removables));
+  };
+
+  // Resolve download root directory.
+  root.getDirectory(
+      RootDirectory.DOWNLOADS.substring(1),  // Remove the leading '/'.
+      { create: false },
+      function(entry) { addRootEntryList('downloads', [entry]); },
+      function(err) {
+        console.error('Error resolving downloads root dir: ' + error);
+        addRootEntryList('downloads', []);
+      });
+
+  // Reads 'archives' and 'removables' roots.
+  util.readDirectory(root, RootDirectory.ARCHIVE.substring(1),
+                     addRootEntryList.bind(null, 'archives'));
+  util.readDirectory(root, RootDirectory.REMOVABLE.substring(1),
+                     addRootEntryList.bind(null, 'removables'));
+};
+
 /**
  * Data model of the file manager.
  *
@@ -38,7 +89,6 @@
   this.scanFailures_ = 0;
   this.driveEnabled_ = isDriveEnabled;
   this.showSpecialSearchRoots_ = showSpecialSearchRoots;
-  this.closureQueue_ = [];
 
   this.fileFilter_ = fileFilter;
   this.fileFilter_.addEventListener('changed',
@@ -49,7 +99,9 @@
   this.currentDirContents_ = new DirectoryContentsBasic(
       this.currentFileListContext_, root);
 
+  // TODO(hidehiko): Move this variable to VolumeManager.
   this.rootsList_ = new cr.ui.ArrayDataModel([]);
+  this.taskQueue_ = new AsyncUtil.Queue();
   this.volumeManager_ = volumeManager;
 
   this.fileWatcher_ = fileWatcher;
@@ -129,42 +181,19 @@
 DirectoryModel.prototype.__proto__ = cr.EventTarget.prototype;
 
 /**
- * Enqueues an asynchronous closure. Guarantees that the closured are called
- * sequentially in order they are enqueued. Each of the closures added to the
- * queue must call a callback once handling is completed.
- *
- * @param {function(function())} handler Closure.
- * @private
- */
-DirectoryModel.prototype.enqueueClosure_ = function(handler) {
-  var processQueue = function() {
-    if (this.closureQueue_.length == 0) {
-      this.closureQueueBusy_ = false;
-      return;
-    }
-    var handler = this.closureQueue_.shift();
-    this.closureQueueBusy_ = true;
-    handler(function() {
-      setTimeout(function() {
-        processQueue();
-      }.bind(this), 0);
-    }.bind(this));
-  }.bind(this);
-  this.closureQueue_.push(handler.bind(this));
-  if (!this.closureQueueBusy_)
-    processQueue();
-};
-
-/**
  * Fills the root list and starts tracking changes.
  */
 DirectoryModel.prototype.start = function() {
-  this.volumeManager_.addEventListener('change',
-      this.enqueueClosure_.bind(this, this.onMountChanged_));
-  this.volumeManager_.addEventListener('drive-status-changed',
-      this.enqueueClosure_.bind(this, this.onDriveStatusChanged_));
-
-  this.enqueueClosure_(this.updateRoots_);
+  // TODO(hidehiko): Integrate these callback into VolumeManager.
+  this.volumeManager_.addEventListener(
+      'change',
+      this.taskQueue_.run.bind(
+          this.taskQueue_, this.onMountChanged_.bind(this)));
+  this.volumeManager_.addEventListener(
+      'drive-status-changed',
+      this.taskQueue_.run.bind(
+          this.taskQueue_, this.onDriveStatusChanged_.bind(this)));
+  this.taskQueue_.run(this.updateRoots_.bind(this));
 };
 
 /**
@@ -189,8 +218,12 @@
 DirectoryModel.prototype.setDriveEnabled = function(enabled) {
   if (this.driveEnabled_ == enabled)
     return;
+  // Mount Drive if it was previously disabled and is now enabled.
+  if (enabled)
+    this.volumeManager_.mountDrive(function() {}, function() {});
+
   this.driveEnabled_ = enabled;
-  this.enqueueClosure_(this.updateRoots_);
+  this.taskQueue_.run(this.updateRoots_.bind(this));
   if (!enabled && (this.getCurrentRootType() == RootType.DRIVE ||
                    this.getCurrentRootType() == RootType.DRIVE_OFFLINE))
     this.changeDirectory(this.getDefaultDirectory());
@@ -217,7 +250,8 @@
  * @return {RootType} Root type of current root.
  */
 DirectoryModel.prototype.getCurrentRootType = function() {
-  return PathUtil.getRootType(this.currentDirContents_.getPath());
+  return PathUtil.getRootType(
+      this.currentDirContents_.getDirectoryEntry().fullPath);
 };
 
 /**
@@ -232,7 +266,8 @@
  * @return {string} Root name.
  */
 DirectoryModel.prototype.getCurrentRootPath = function() {
-  return PathUtil.getRootPath(this.currentDirContents_.getPath());
+  return PathUtil.getRootPath(
+      this.currentDirContents_.getDirectoryEntry().fullPath);
 };
 
 /**
@@ -379,7 +414,7 @@
  * @return {string} Path for the current directory.
  */
 DirectoryModel.prototype.getCurrentDirPath = function() {
-  return this.currentDirContents_.getPath();
+  return this.currentDirContents_.getDirectoryEntry().fullPath;
 };
 
 /**
@@ -675,45 +710,50 @@
 };
 
 /**
- * @param {string} name Filename.
+ * Callback when an entry is changed.
+ * @param {util.EntryChangedType} type How the entry is changed.
+ * @param {Entry} entry The changed entry.
  */
-DirectoryModel.prototype.onEntryChanged = function(name) {
-  var currentEntry = this.getCurrentDirEntry();
-  var fileList = this.getFileList();
-  var self = this;
+DirectoryModel.prototype.onEntryChanged = function(type, entry) {
+  // TODO(hidehiko): We should update directory model even the search result
+  // is shown.
+  var rootType = this.getCurrentRootType();
+  if ((rootType === RootType.DRIVE ||
+       rootType === RootType.DRIVE_SHARED_WITH_ME ||
+       rootType === RootType.DRIVE_RECENT ||
+       rootType === RootType.DRIVE_OFFLINE) &&
+      this.isSearching())
+    return;
 
-  var onEntryFound = function(entry) {
-    // Do nothing if current directory changed during async operations.
-    if (self.getCurrentDirEntry() != currentEntry)
-      return;
-    self.currentDirContents_.prefetchMetadata([entry], function() {
-      // Do nothing if current directory changed during async operations.
-      if (self.getCurrentDirEntry() != currentEntry)
+  if (type == util.EntryChangedType.CREATED) {
+    entry.getParent(function(parentEntry) {
+      if (this.getCurrentDirEntry().fullPath != parentEntry.fullPath) {
+        // Do nothing if current directory changed during async operations.
         return;
+      }
+      this.currentDirContents_.prefetchMetadata([entry], function() {
+        if (this.getCurrentDirEntry().fullPath != parentEntry.fullPath) {
+          // Do nothing if current directory changed during async operations.
+          return;
+        }
 
-      var index = self.findIndexByName_(name);
-      if (index >= 0)
-        fileList.splice(index, 1, entry);
-      else
-        fileList.splice(fileList.length, 0, entry);
-    });
-  };
-
-  var onError = function(err) {
-    if (err.code != FileError.NOT_FOUND_ERR) {
-      self.rescanLater();
-      return;
-    }
-
-    var index = self.findIndexByName_(name);
+        var index = this.findIndexByEntry_(entry);
+        if (index >= 0)
+          fileList.splice(index, 1, entry);
+        else
+          fileList.push(entry);
+      }.bind(this));
+    }.bind(this));
+  } else {
+    // This is the delete event.
+    var index = this.findIndexByEntry_(entry);
     if (index >= 0)
-      fileList.splice(index, 1);
-  };
-
-  util.resolvePath(currentEntry, name, onEntryFound, onError);
+      this.getFileList().splice(index, 1);
+  }
 };
 
 /**
+ * TODO(hidehiko): Migrate this method into findIndexByEntry_ defined below.
  * @param {string} name Filename.
  * @return {number} The index in the fileList.
  * @private
@@ -727,6 +767,19 @@
 };
 
 /**
+ * @param {Entry} entry The entry to be searched.
+ * @return {number} The index in the fileList, or -1 if not found.
+ * @private
+ */
+DirectoryModel.prototype.findIndexByEntry_ = function(entry) {
+  var fileList = this.getFileList();
+  for (var i = 0; i < fileList.length; i++)
+    if (fileList.item(i).fullPath == entry.fullPath)
+      return i;
+  return -1;
+};
+
+/**
  * Rename the entry in the filesystem and update the file list.
  * @param {Entry} entry Entry to rename.
  * @param {string} newName New name.
@@ -1130,86 +1183,24 @@
 };
 
 /**
- * Get root entries asynchronously.
- * @param {function(Array.<Entry>)} callback Called when roots are resolved.
- * @private
- */
-DirectoryModel.prototype.resolveRoots_ = function(callback) {
-  var groups = {
-    drive: null,
-    downloads: null,
-    archives: null,
-    removables: null
-  };
-  var self = this;
-
-  metrics.startInterval('Load.Roots');
-  var done = function() {
-    var roots = [];
-    for (var i in groups) {
-      if (!groups[i])
-        return;
-      roots = roots.concat(groups[i]);
-    }
-
-    callback(roots);
-    metrics.recordInterval('Load.Roots');
-  };
-
-  var append = function(index, values, opt_error) {
-    groups[index] = values;
-    done();
-  };
-
-  var appendSingle = function(index, entry) {
-    groups[index] = [entry];
-    done();
-  };
-
-  var onSingleError = function(index, defaultValue, error) {
-    groups[index] = defaultValue || [];
-    done();
-    console.error('Error resolving root dir ', index, 'error: ', error);
-  };
-
-  var root = this.root_;
-  var readSingle = function(dir, index, opt_defaultValue) {
-    root.getDirectory(dir, { create: false },
-                      appendSingle.bind(this, index),
-                      onSingleError.bind(this, index, opt_defaultValue));
-  };
-
-  readSingle(RootDirectory.DOWNLOADS.substring(1), 'downloads');
-  util.readDirectory(root, RootDirectory.ARCHIVE.substring(1),
-                     append.bind(this, 'archives'));
-  util.readDirectory(root, RootDirectory.REMOVABLE.substring(1),
-                     append.bind(this, 'removables'));
-
-  if (this.driveEnabled_) {
-    // Use a fake instead to return a list as fast as possible.
-    groups.drive = [DirectoryModel.fakeDriveEntry_];
-    done();
-  } else {
-    groups.drive = [];
-    done();
-  }
-};
-
-/**
  * Updates the roots list.
  *
  * @param {function()=} opt_callback Completion callback.
  * @private
  */
 DirectoryModel.prototype.updateRoots_ = function(opt_callback) {
-  var self = this;
-  this.resolveRoots_(function(rootEntries) {
-    var dm = self.rootsList_;
-    var args = [0, dm.length].concat(rootEntries);
-    dm.splice.apply(dm, args);
-    if (opt_callback)
-      opt_callback();
-  });
+  metrics.startInterval('Load.Roots');
+  DirectoryModelUtil.resolveRoots(
+      this.root_, this.driveEnabled_,
+      function(rootEntries) {
+        metrics.recordInterval('Load.Roots');
+
+        var rootsList = this.rootsList_;
+        rootsList.splice.apply(
+            rootsList, [0, rootsList.length].concat(rootEntries));
+        if (opt_callback)
+          opt_callback();
+      }.bind(this));
 };
 
 /**
@@ -1298,25 +1289,6 @@
 };
 
 /**
- * Finds the first entry in the roots list model which has the given root type.
- *
- * @param {RootType} rootType RootType of the desired root..
- * @return {DirectoryEntry} Root entry for with the given root type.
- * @private
- */
-DirectoryModel.prototype.findRootEntryByType_ = function(rootType) {
-  var root;
-  for (var index = 0; index < this.rootsList_.length; index++) {
-    if (PathUtil.getRootType(this.rootsList_.item(index).fullPath) ==
-        rootType) {
-      root = this.rootsList_.item(index);
-      break;
-    }
-  }
-  return root;
-};
-
-/**
  * @param {string} path Path.
  * @return {boolean} If current directory is system.
  */
diff --git a/chrome/browser/resources/file_manager/js/directory_tree.js b/chrome/browser/resources/file_manager/js/directory_tree.js
index 69710fb..0f84b62 100644
--- a/chrome/browser/resources/file_manager/js/directory_tree.js
+++ b/chrome/browser/resources/file_manager/js/directory_tree.js
@@ -44,20 +44,20 @@
  *     created items.
  * @param {function(number): DirectoryEntry} iterator Function which returns
  *     the n-th Entry in the directory.
- * @param {DirectoryModel} directoryModel Current DirectoryModel.
+ * @param {DirectoryTree} tree Current directory tree, which contains this item.
  * @param {boolean} recursive True if the all visible sub-directories are
  *     updated recursively including left arrows. If false, the update walks
  *     only immediate child directories without arrows.
  */
 DirectoryTreeUtil.updateSubElementsFromList = function(
-    parentElement, iterator, directoryModel, recursive) {
+    parentElement, iterator, tree, recursive) {
   var index = 0;
   while (iterator(index)) {
     var currentEntry = iterator(index);
     var currentElement = parentElement.items[index];
 
     if (index >= parentElement.items.length) {
-      var item = new DirectoryItem(currentEntry, parentElement, directoryModel);
+      var item = new DirectoryItem(currentEntry, parentElement, tree);
       parentElement.add(item);
       index++;
     } else if (currentEntry.fullPath == currentElement.fullPath) {
@@ -66,7 +66,7 @@
 
       index++;
     } else if (currentEntry.fullPath < currentElement.fullPath) {
-      var item = new DirectoryItem(currentEntry, parentElement, directoryModel);
+      var item = new DirectoryItem(currentEntry, parentElement, tree);
       parentElement.addAt(item, index);
       index++;
     } else if (currentEntry.fullPath > currentElement.fullPath) {
@@ -241,13 +241,13 @@
  *
  * @param {DirectoryEntry} dirEntry DirectoryEntry of this item.
  * @param {DirectoryItem|DirectoryTree} parentDirItem Parent of this item.
- * @param {DirectoryModel} directoryModel Current DirectoryModel.
+ * @param {DirectoryTree} tree Current tree, which contains this item.
  * @extends {cr.ui.TreeItem}
  * @constructor
  */
-function DirectoryItem(dirEntry, parentDirItem, directoryModel) {
+function DirectoryItem(dirEntry, parentDirItem, tree) {
   var item = cr.doc.createElement('div');
-  DirectoryItem.decorate(item, dirEntry, parentDirItem, directoryModel);
+  DirectoryItem.decorate(item, dirEntry, parentDirItem, tree);
   return item;
 }
 
@@ -255,13 +255,13 @@
  * @param {HTMLElement} el Element to be DirectoryItem.
  * @param {DirectoryEntry} dirEntry DirectoryEntry of this item.
  * @param {DirectoryItem|DirectoryTree} parentDirItem Parent of this item.
- * @param {DirectoryModel} directoryModel Current DirectoryModel.
+ * @param {DirectoryTree} tree Current tree, which contains this item.
  */
 DirectoryItem.decorate =
-    function(el, dirEntry, parentDirItem, directoryModel) {
+    function(el, dirEntry, parentDirItem, tree) {
   el.__proto__ = DirectoryItem.prototype;
   (/** @type {DirectoryItem} */ el).decorate(
-      dirEntry, parentDirItem, directoryModel);
+      dirEntry, parentDirItem, tree);
 };
 
 DirectoryItem.prototype = {
@@ -290,10 +290,10 @@
 /**
  * @param {DirectoryEntry} dirEntry DirectoryEntry of this item.
  * @param {DirectoryItem|DirectoryTree} parentDirItem Parent of this item.
- * @param {DirectoryModel} directoryModel Current DirectoryModel.
+ * @param {DirectoryTree} tree Current tree, which contains this item.
  */
 DirectoryItem.prototype.decorate = function(
-    dirEntry, parentDirItem, directoryModel) {
+    dirEntry, parentDirItem, tree) {
   var path = dirEntry.fullPath;
   var label;
   label = dirEntry.label ? dirEntry.label : dirEntry.name;
@@ -309,7 +309,8 @@
       '<div class="tree-children"></div>';
   this.setAttribute('role', 'treeitem');
 
-  this.directoryModel_ = directoryModel;
+  this.parentTree_ = tree;
+  this.directoryModel_ = tree.directoryModel;
   this.parent_ = parentDirItem;
   this.label = label;
   this.fullPath = path;
@@ -341,6 +342,13 @@
         volumeManager.unmount(path, function() {}, function() {});
       }.bind(this));
 
+  if (this.parentTree_.contextMenuForSubitems)
+    this.setContextMenu(this.parentTree_.contextMenuForSubitems);
+  // Adds handler for future change.
+  this.parentTree_.addEventListener(
+      'contextMenuForSubitemsChange',
+      function(e) { this.setContextMenu(e.newValue); }.bind(this));
+
   if (parentDirItem.expanded)
     this.updateSubDirectories(false /* recursive */);
 };
@@ -414,7 +422,7 @@
   DirectoryTreeUtil.updateSubElementsFromList(
       this,
       function(i) { return this.entries_[i]; }.bind(this),
-      this.directoryModel_,
+      this.parentTree_,
       recursive);
 };
 
@@ -452,6 +460,15 @@
     this.directoryModel_.changeDirectory(this.fullPath);
 };
 
+/**
+ * Sets the context menu for directory tree.
+ * @param {cr.ui.Menu} menu Menu to be set.
+ */
+DirectoryItem.prototype.setContextMenu = function(menu) {
+  if (this.entry && PathUtil.isEligibleForFolderShortcut(this.entry.fullPath))
+    cr.ui.contextMenuHandler.setContextMenu(this, menu);
+};
+
 ////////////////////////////////////////////////////////////////////////////////
 // DirectoryTree
 
@@ -492,9 +509,15 @@
    **/
   get entry() {
       return this.dirEntry_;
+  },
+
+  get directoryModel() {
+    return this.directoryModel_;
   }
 };
 
+cr.defineProperty(DirectoryTree, 'contextMenuForSubitems', cr.PropertyKind.JS);
+
 /**
  * Decorates an element.
  * @param {DirectoryModel} directoryModel Current DirectoryModel.
@@ -639,7 +662,7 @@
   DirectoryTreeUtil.updateSubElementsFromList(
       this,
       function(i) { return this.entries_[i]; }.bind(this),
-      this.directoryModel_,
+      this,
       recursive);
 };
 
diff --git a/chrome/browser/resources/file_manager/js/file_copy_manager.js b/chrome/browser/resources/file_manager/js/file_copy_manager.js
index d62889f..a34c76b 100644
--- a/chrome/browser/resources/file_manager/js/file_copy_manager.js
+++ b/chrome/browser/resources/file_manager/js/file_copy_manager.js
@@ -65,23 +65,21 @@
   event.reason = reason;
   event.status = status;
   if (opt_error)
-    event.error = opt.error;
+    event.error = opt_error;
   this.dispatchEvent(event);
 };
 
 /**
- * Dispatches an event to notify that the entries in affectedEntries are
- * changed (created or deleted) by operation reason.
- *
- * @param {string} reason Completed file operation. One of "moved", "copied"
- *     or "deleted". TODO(hidehiko): Use enum.
- * @param {Array.<Entry>} affectedEntries Entries which are created or deleted.
+ * Dispatches an event to notify that an entry is changed (created or deleted).
+ * @param {util.EntryChangedType} type The enum to represent if the entry
+ *     is created or deleted.
+ * @param {Entry} entry The changed entry.
  */
-FileCopyManager.EventRouter.prototype.sendOperationEvent = function(
-    reason, affectedEntries) {
-  var event = new cr.Event('copy-operation-complete');
-  event.reason = reason;
-  event.affectedEntries = affectedEntries;
+FileCopyManager.EventRouter.prototype.sendEntryChangedEvent = function(
+    type, entry) {
+  var event = new cr.Event('entry-changed');
+  event.type = type;
+  event.entry = entry;
   this.dispatchEvent(event);
 };
 
@@ -109,16 +107,11 @@
  * cancel operation cancels everything in the queue.
  *
  * @param {DirectoryEntry} targetDirEntry Target directory.
- * @param {boolean} sourceOnDrive True if the source entries are on Drive.
- * @param {boolean} targetOnDrive True if the target entry is on Drive.
  * @param {DirectoryEntry=} opt_zipBaseDirEntry Base directory dealt as a root
  *     in ZIP archive.
  * @constructor
  */
-FileCopyManager.Task = function(targetDirEntry,
-                                sourceOnDrive,
-                                targetOnDrive,
-                                opt_zipBaseDirEntry) {
+FileCopyManager.Task = function(targetDirEntry, opt_zipBaseDirEntry) {
   this.targetDirEntry = targetDirEntry;
   this.zipBaseDirEntry = opt_zipBaseDirEntry;
   this.originalEntries = null;
@@ -134,8 +127,6 @@
   this.deleteAfterCopy = false;
   this.move = false;
   this.zip = false;
-  this.sourceOnDrive = sourceOnDrive;
-  this.targetOnDrive = targetOnDrive;
 
   // If directory already exists, we try to make a copy named 'dir (X)',
   // where X is a number. When we do this, all subsequent copies from
@@ -538,12 +529,10 @@
  * @param {Array.<string>} directories Pathes of source directories.
  * @param {boolean} isCut If the source items are removed from original
  *     location.
- * @param {boolean} isOnDrive If the source items are on Google Drive.
  * @param {string} targetPath Target path.
- * @param {boolean} targetOnDrive If target is on Drive.
  */
-FileCopyManager.prototype.paste = function(files, directories, isCut, isOnDrive,
-                                           targetPath, targetOnDrive) {
+FileCopyManager.prototype.paste = function(
+    files, directories, isCut, targetPath) {
   var self = this;
   var entries = [];
   var added = 0;
@@ -592,11 +581,7 @@
     },
 
     onTargetEntryFound: function(targetEntry) {
-      self.queueCopy_(targetEntry,
-                      entries,
-                      isCut,
-                      isOnDrive,
-                      targetOnDrive);
+      self.queueCopy_(targetEntry, entries, isCut);
     },
 
     onPathError: function(err) {
@@ -632,20 +617,14 @@
  * @param {DirectoryEntry} targetDirEntry Target directory.
  * @param {Array.<Entry>} entries Entries to copy.
  * @param {boolean} deleteAfterCopy In case of move.
- * @param {boolean} sourceOnDrive Source directory on Drive.
- * @param {boolean} targetOnDrive Target directory on Drive.
  * @return {FileCopyManager.Task} Copy task.
  * @private
  */
-FileCopyManager.prototype.queueCopy_ = function(targetDirEntry,
-                                                entries,
-                                                deleteAfterCopy,
-                                                sourceOnDrive,
-                                                targetOnDrive) {
+FileCopyManager.prototype.queueCopy_ = function(
+    targetDirEntry, entries, deleteAfterCopy) {
   var self = this;
   // When copying files, null can be specified as source directory.
-  var copyTask = new FileCopyManager.Task(
-      targetDirEntry, sourceOnDrive, targetOnDrive);
+  var copyTask = new FileCopyManager.Task(targetDirEntry);
   if (deleteAfterCopy) {
     if (this.isMovable(entries[0], targetDirEntry)) {
       copyTask.move = true;
@@ -679,6 +658,14 @@
 FileCopyManager.prototype.serviceAllTasks_ = function() {
   var self = this;
 
+  var onTaskProgress = function() {
+    self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
+  };
+
+  var onEntryChanged = function(type, entry) {
+    self.eventRouter_.sendEntryChangedEvent(type, entry);
+  };
+
   var onTaskError = function(err) {
     if (self.maybeCancel_())
       return;
@@ -707,14 +694,16 @@
     // these continuous tasks.
     self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
 
-    self.serviceTask_(self.copyTasks_[0], onTaskSuccess, onTaskError);
+    self.serviceTask_(self.copyTasks_[0], onEntryChanged, onTaskProgress,
+                      onTaskSuccess, onTaskError);
   };
 
   // If the queue size is 1 after pushing our task, it was empty before,
   // so we need to kick off queue processing and dispatch BEGIN event.
 
   this.eventRouter_.sendProgressEvent('BEGIN', this.getStatus());
-  this.serviceTask_(this.copyTasks_[0], onTaskSuccess, onTaskError);
+  this.serviceTask_(this.copyTasks_[0], onEntryChanged, onTaskProgress,
+                    onTaskSuccess, onTaskError);
 };
 
 /**
@@ -725,18 +714,26 @@
  *     (crbug.com/246976).
  *
  * @param {FileCopyManager.Task} task A task to be run.
+ * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback
+ *     invoked when an entry is changed.
+ * @param {function()} progressCallback Callback invoked periodically during
+ *     the operation.
  * @param {function()} successCallback Callback run on success.
  * @param {function(FileCopyManager.Error)} errorCallback Callback run on error.
  * @private
  */
 FileCopyManager.prototype.serviceTask_ = function(
-    task, successCallback, errorCallback) {
+    task, entryChangedCallback, progressCallback,
+    successCallback, errorCallback) {
   if (task.zip)
-    this.serviceZipTask_(task, successCallback, errorCallback);
+    this.serviceZipTask_(task, entryChangedCallback, progressCallback,
+                         successCallback, errorCallback);
   else if (task.move)
-    this.serviceMoveTask_(task, successCallback, errorCallback);
+    this.serviceMoveTask_(task, entryChangedCallback, progressCallback,
+                          successCallback, errorCallback);
   else
-    this.serviceCopyTask_(task, successCallback, errorCallback);
+    this.serviceCopyTask_(task, entryChangedCallback, progressCallback,
+                          successCallback, errorCallback);
 };
 
 /**
@@ -745,19 +742,31 @@
  * reason.
  *
  * @param {FileCopyManager.Task} task A copy task to be run.
+ * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback
+ *     invoked when an entry is changed.
+ * @param {function()} progressCallback Callback invoked periodically during
+ *     the copying.
  * @param {function()} successCallback On success.
  * @param {function(FileCopyManager.Error)} errorCallback On error.
  * @private
  */
 FileCopyManager.prototype.serviceCopyTask_ = function(
-    task, successCallback, errorCallback) {
+    task, entryChangedCallback, progressCallback, successCallback,
+    errorCallback) {
+  // TODO(hidehiko): We should be able to share the code to iterate on entries
+  // with serviceMoveTask_().
+  if (task.pendingDirectories.length + task.pendingFiles.length == 0) {
+    successCallback();
+    return;
+  }
+
   var self = this;
 
   var deleteOriginals = function() {
     var count = task.originalEntries.length;
 
     var onEntryDeleted = function(entry) {
-      self.eventRouter_.sendOperationEvent('deleted', [entry]);
+      entryChangedCallback(util.EntryChangedType.DELETED, entry);
       count--;
       if (!count)
         successCallback();
@@ -787,11 +796,15 @@
       return;
     }
 
-    self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
-    self.serviceNextCopyTaskEntry_(task, onEntryServiced, errorCallback);
+    progressCallback();
+    self.processCopyEntry_(
+        task, task.getNextEntry(), entryChangedCallback, progressCallback,
+        onEntryServiced, errorCallback);
   };
 
-  this.serviceNextCopyTaskEntry_(task, onEntryServiced, errorCallback);
+  this.processCopyEntry_(
+      task, task.getNextEntry(), entryChangedCallback, progressCallback,
+      onEntryServiced, errorCallback);
 };
 
 /**
@@ -799,23 +812,22 @@
  * TODO(olege): Refactor this method into a separate class.
  *
  * @param {FileManager.Task} task A task.
+ * @param {Entry} sourceEntry An entry to be copied.
+ * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback
+ *     invoked when an entry is changed.
+ * @param {function()} progressCallback Callback invoked periodically during
+ *     the copying.
  * @param {function()} successCallback On success.
  * @param {function(FileCopyManager.Error)} errorCallback On error.
  * @private
  */
-FileCopyManager.prototype.serviceNextCopyTaskEntry_ = function(
-    task, successCallback, errorCallback) {
+FileCopyManager.prototype.processCopyEntry_ = function(
+    task, sourceEntry, entryChangedCallback, progressCallback, successCallback,
+    errorCallback) {
   if (this.maybeCancel_())
     return;
 
   var self = this;
-  var sourceEntry = task.getNextEntry();
-
-  if (!sourceEntry) {
-    // All entries in this task have been copied.
-    successCallback();
-    return;
-  }
 
   // |sourceEntry.originalSourcePath| is set in util.recurseAndResolveEntries.
   var sourcePath = sourceEntry.originalSourcePath;
@@ -832,154 +844,20 @@
   var originalPath = sourceEntry.fullPath.substr(sourcePath.length + 1);
   originalPath = task.applyRenames(originalPath);
 
-  var onCopyCompleteBase = function(entry, size) {
-    task.markEntryComplete(entry, size);
-    successCallback();
-  };
-
-  var onCopyComplete = function(entry, size) {
-    self.eventRouter_.sendOperationEvent('copied', [entry]);
-    onCopyCompleteBase(entry, size);
-  };
-
-  var onCopyProgress = function(entry, size) {
-    task.updateFileCopyProgress(entry, size);
-    self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
-  };
-
-  var onFilesystemCopyComplete = function(sourceEntry, targetEntry) {
-    // TODO(benchan): We currently do not know the size of data being
-    // copied by FileEntry.copyTo(), so task.completedBytes will not be
-    // increased. We will address this issue once we need to use
-    // task.completedBytes to track the progress.
-    self.eventRouter_.sendOperationEvent('copied', [sourceEntry, targetEntry]);
-    onCopyCompleteBase(targetEntry, 0);
-  };
-
-  var onFilesystemError = function(err) {
-    errorCallback(new FileCopyManager.Error(
-        util.FileOperationErrorType.FILESYSTEM_ERROR, err));
-  };
-
   var onDeduplicated = function(targetRelativePath) {
-    // TODO(benchan): drive::FileSystem has not implemented directory copy,
-    // and thus we only call FileEntry.copyTo() for files. Revisit this
-    // code when drive::FileSystem supports directory copy.
-    if (sourceEntry.isFile && (task.sourceOnDrive || task.targetOnDrive)) {
-      var sourceFileUrl = sourceEntry.toURL();
-      var targetFileUrl = targetDirEntry.toURL() + '/' +
-                          encodeURIComponent(targetRelativePath);
-      var sourceFilePath = util.extractFilePath(sourceFileUrl);
-      var targetFilePath = util.extractFilePath(targetFileUrl);
-      var transferedBytes = 0;
+    var onCopyComplete = function(entry, size) {
+      entryChangedCallback(util.EntryChangedType.CREATED, entry);
+      task.markEntryComplete(entry, size);
+      successCallback();
+    };
 
-      var onStartTransfer = function() {
-        chrome.fileBrowserPrivate.onFileTransfersUpdated.addListener(
-            onFileTransfersUpdated);
-      };
-
-      var onFailTransfer = function(err) {
-        chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener(
-            onFileTransfersUpdated);
-        console.error(
-            'Error copying ' + sourceFileUrl + ' to ' + targetFileUrl);
-        onFilesystemError(err);
-      };
-
-      var onSuccessTransfer = function(targetEntry) {
-        chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener(
-            onFileTransfersUpdated);
-
-        targetEntry.getMetadata(function(metadata) {
-          if (metadata.size > transferedBytes)
-            onCopyProgress(sourceEntry, metadata.size - transferedBytes);
-          onFilesystemCopyComplete(sourceEntry, targetEntry);
-        });
-      };
-
-      var downTransfer = 0;
-      var onFileTransfersUpdated = function(statusList) {
-        for (var i = 0; i < statusList.length; i++) {
-          var s = statusList[i];
-          // Comparing urls is unreliable, since they may use different
-          // url encoding schemes (eg. rfc2396 vs. rfc3986).
-          var filePath = util.extractFilePath(s.fileUrl);
-          if (filePath == sourceFilePath || filePath == targetFilePath) {
-            var processed = s.processed;
-
-            // It becomes tricky when both the sides are on Drive.
-            // Currently, it is implemented by download followed by upload.
-            // Note, however, download will not happen if the file is cached.
-            if (task.sourceOnDrive && task.targetOnDrive) {
-              if (filePath == sourceFilePath) {
-                // Download transfer is detected. Let's halve the progress.
-                downTransfer = processed = (s.processed >> 1);
-              } else {
-                // If download transfer has been detected, the upload transfer
-                // is stacked on top of it after halving. Otherwise, just use
-                // the upload transfer as-is.
-                processed = downTransfer > 0 ?
-                   downTransfer + (s.processed >> 1) : s.processed;
-              }
-            }
-
-            if (processed > transferedBytes) {
-              onCopyProgress(sourceEntry, processed - transferedBytes);
-              transferedBytes = processed;
-            }
-          }
-        }
-      };
-
-      if (task.sourceOnDrive && task.targetOnDrive) {
-        targetDirEntry.getDirectory(
-            PathUtil.dirname(targetRelativePath), {create: false},
-            function(dirEntry) {
-              onStartTransfer();
-              sourceEntry.copyTo(
-                  dirEntry, PathUtil.basename(targetRelativePath),
-                  onSuccessTransfer, onFailTransfer);
-            },
-            onFilesystemError);
-        return;
-      }
-
-      var onFileTransferCompleted = function() {
-        self.cancelCallback_ = null;
-        if (chrome.runtime.lastError) {
-          onFailTransfer({
-            code: chrome.runtime.lastError.message,
-            toDrive: task.targetOnDrive,
-            sourceFileUrl: sourceFileUrl
-          });
-        } else {
-          targetDirEntry.getFile(targetRelativePath, {}, onSuccessTransfer,
-                                                         onFailTransfer);
-        }
-      };
-
-      self.cancelCallback_ = function() {
-        self.cancelCallback_ = null;
-        chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener(
-            onFileTransfersUpdated);
-        if (task.sourceOnDrive) {
-          chrome.fileBrowserPrivate.cancelFileTransfers([sourceFileUrl],
-                                                        function() {});
-        } else {
-          chrome.fileBrowserPrivate.cancelFileTransfers([targetFileUrl],
-                                                        function() {});
-        }
-      };
-
-      // TODO(benchan): Until drive::FileSystem supports FileWriter, we use the
-      // transferFile API to copy files into or out from a drive file system.
-      onStartTransfer();
-      chrome.fileBrowserPrivate.transferFile(
-          sourceFileUrl, targetFileUrl, onFileTransferCompleted);
-      return;
-    }
+    var onFilesystemError = function(err) {
+      errorCallback(new FileCopyManager.Error(
+          util.FileOperationErrorType.FILESYSTEM_ERROR, err));
+    };
 
     if (sourceEntry.isDirectory) {
+      // Copying the directory means just creating a new directory.
       targetDirEntry.getDirectory(
           targetRelativePath,
           {create: true, exclusive: true},
@@ -991,16 +869,153 @@
           },
           util.flog('Error getting dir: ' + targetRelativePath,
                     onFilesystemError));
-    } else {
+      return;
+    }
+
+    var onCopyProgress = function(entry, size) {
+      task.updateFileCopyProgress(entry, size);
+      progressCallback();
+    };
+
+    // Hereafter copy a file.
+    var isSourceOnDrive = PathUtil.isDriveBasedPath(sourceEntry.fullPath);
+    var isTargetOnDrive = PathUtil.isDriveBasedPath(targetDirEntry.fullPath);
+
+    if (!isSourceOnDrive && !isTargetOnDrive) {
+      // Sending a file from local to local.
+      // To copy local file, we use File blob and FileWriter to take the
+      // progress.
       targetDirEntry.getFile(
           targetRelativePath,
           {create: true, exclusive: true},
           function(targetEntry) {
-            self.copyEntry_(sourceEntry, targetEntry,
-                            onCopyProgress, onCopyComplete, onFilesystemError);
+            self.copyFileEntry_(
+                sourceEntry, targetEntry,
+                onCopyProgress, onCopyComplete, onFilesystemError);
           },
           util.flog('Error getting file: ' + targetRelativePath,
                     onFilesystemError));
+      return;
+    }
+
+    // Sending a file from a) Drive to Drive, b) Drive to local or c) local to
+    // Drive.
+    var sourceFileUrl = sourceEntry.toURL();
+    var targetFileUrl =
+        targetDirEntry.toURL() + '/' + encodeURIComponent(targetRelativePath);
+    var sourceFilePath = util.extractFilePath(sourceFileUrl);
+    var targetFilePath = util.extractFilePath(targetFileUrl);
+
+    // Progress callback. TODO(hidehiko): Simplify this progress mechanism.
+    var transferedBytes = 0;
+    var downTransfer = 0;
+    var onFileTransfersUpdated = function(statusList) {
+      for (var i = 0; i < statusList.length; i++) {
+        var s = statusList[i];
+        // Comparing urls is unreliable, since they may use different
+        // url encoding schemes (eg. rfc2396 vs. rfc3986).
+        var filePath = util.extractFilePath(s.fileUrl);
+        if (filePath == sourceFilePath || filePath == targetFilePath) {
+          var processed = s.processed;
+
+          // It becomes tricky when both the sides are on Drive.
+          // Currently, it is implemented by download followed by upload.
+          // Note, however, download will not happen if the file is cached.
+          if (isSourceOnDrive && isTargetOnDrive) {
+            if (filePath == sourceFilePath) {
+              // Download transfer is detected. Let's halve the progress.
+              downTransfer = processed = (s.processed >> 1);
+            } else {
+              // If download transfer has been detected, the upload transfer
+              // is stacked on top of it after halving. Otherwise, just use
+              // the upload transfer as-is.
+              processed = downTransfer > 0 ?
+                  downTransfer + (s.processed >> 1) : s.processed;
+            }
+          }
+
+          if (processed > transferedBytes) {
+            onCopyProgress(sourceEntry, processed - transferedBytes);
+            transferedBytes = processed;
+          }
+        }
+      }
+    };
+
+    var onStartTransfer = function() {
+      chrome.fileBrowserPrivate.onFileTransfersUpdated.addListener(
+          onFileTransfersUpdated);
+    };
+
+    var onSuccessTransfer = function(targetEntry) {
+      self.cancelCallback_ = null;
+      chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener(
+          onFileTransfersUpdated);
+
+      targetEntry.getMetadata(function(metadata) {
+        if (metadata.size > transferedBytes)
+          onCopyProgress(sourceEntry, metadata.size - transferedBytes);
+        onCopyComplete(targetEntry, metadata.size);
+      });
+    };
+
+    var onFailTransfer = function(err) {
+      self.cancelCallback_ = null;
+      chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener(
+          onFileTransfersUpdated);
+      console.error(
+          'Error copying ' + sourceFileUrl + ' to ' + targetFileUrl);
+      onFilesystemError(err);
+    };
+
+    self.cancelCallback_ = function() {
+      self.cancelCallback_ = null;
+
+      // Currently, we do NOT upload the file during the copy operation.
+      // It will be done as a part of file system sync after copy operation.
+      // So, we can cancel only file downloading.
+      if (isSourceOnDrive) {
+        chrome.fileBrowserPrivate.cancelFileTransfers(
+            [sourceFileUrl], function() {});
+      }
+    };
+
+    // Note: for hosted documents, copyTo implementation of Drive file system
+    // and transferFile private API has special handling.
+    // See also drive::file_system::CopyOperation.
+    if (isSourceOnDrive && isTargetOnDrive) {
+      // If this is the copy operation inside Drive file system,
+      // we use copyTo method.
+      targetDirEntry.getDirectory(
+          PathUtil.dirname(targetRelativePath), {create: false},
+          function(dirEntry) {
+            onStartTransfer();
+            sourceEntry.copyTo(
+                dirEntry, PathUtil.basename(targetRelativePath),
+                onSuccessTransfer, onFailTransfer);
+          },
+          onFilesystemError);
+    } else {
+      // Sending a file either a) from local to Drive, or b) from Drive to
+      // local.
+      // TODO(hidehiko): Migrate this code into copyFileEntry_().
+      onStartTransfer();
+      chrome.fileBrowserPrivate.transferFile(
+          sourceFileUrl, targetFileUrl,
+          function() {
+            self.cancelCallback_ = null;
+            if (chrome.runtime.lastError) {
+              console.error(chrome.runtime.lastError.message);
+              onFailTransfer(Object.create(FileError.prototype, {
+                code: {
+                  get: function() { return FileError.INVALID_MODIFICATION_ERR; }
+                }
+              }));
+              return;
+            }
+            targetDirEntry.getFile(
+                targetRelativePath, {}, onSuccessTransfer, onFailTransfer);
+          });
     }
   };
 
@@ -1009,177 +1024,27 @@
 };
 
 /**
- * Moves all entries in the task.
+ * Copies the contents of sourceEntry into targetEntry.
  *
- * @param {FileCopyManager.Task} task A move task to be run.
- * @param {function()} successCallback On success.
- * @param {function(FileCopyManager.Error)} errorCallback On error.
- * @private
- */
-FileCopyManager.prototype.serviceMoveTask_ = function(
-    task, successCallback, errorCallback) {
-  this.serviceNextMoveTaskEntry_(
-      task,
-      (function onCompleted() {
-        // We should not dispatch a PROGRESS event when there is no pending
-        // items in the task.
-        if (task.pendingDirectories.length + task.pendingFiles.length == 0) {
-          successCallback();
-          return;
-        }
-
-        // Move the next entry.
-        this.eventRouter_.sendProgressEvent('PROGRESS', this.getStatus());
-        this.serviceNextMoveTaskEntry_(
-            task, onCompleted.bind(this), errorCallback);
-      }).bind(this),
-      errorCallback);
-};
-
-/**
- * Moves the next entry in a given task.
- *
- * Implementation note: This method can be simplified more. For example, in
- * Task.setEntries(), the flag to recurse is set to false for move task,
- * so that all the entries' originalSourcePath should be
- * dirname(sourceEntry.fullPath).
- * Thus, targetRelativePath should contain exact one component. Also we can
- * skip applyRenames, because the destination directory always should be
- * task.targetDirEntry.
- * The unnecessary complexity is due to historical reason.
- * TODO(hidehiko): Refactor this method.
- *
- * @param {FileManager.Task} task A move task.
- * @param {function()} successCallback On success.
- * @param {function(FileCopyManager.Error)} errorCallback On error.
- * @private
- */
-FileCopyManager.prototype.serviceNextMoveTaskEntry_ = function(
-    task, successCallback, errorCallback) {
-  if (this.maybeCancel_())
-    return;
-
-  var self = this;
-  var sourceEntry = task.getNextEntry();
-
-  if (!sourceEntry) {
-    // All entries in this task have been copied.
-    successCallback();
-    return;
-  }
-
-  // |sourceEntry.originalSourcePath| is set in util.recurseAndResolveEntries.
-  var sourcePath = sourceEntry.originalSourcePath;
-  if (sourceEntry.fullPath.substr(0, sourcePath.length) != sourcePath) {
-    // We found an entry in the list that is not relative to the base source
-    // path, something is wrong.
-    errorCallback(new FileCopyManager.Error(
-        util.FileOperationErrorType.UNEXPECTED_SOURCE_FILE,
-        sourceEntry.fullPath));
-    return;
-  }
-
-  FileCopyManager.Task.deduplicatePath(
-      task.targetDirEntry,
-      task.applyRenames(sourceEntry.fullPath.substr(sourcePath.length + 1)),
-      function(targetRelativePath) {
-        var onFilesystemError = function(err) {
-          errorCallback(new FileCopyManager.Error(
-              util.FileOperationErrorType.FILESYSTEM_ERROR,
-              err));
-        };
-
-        task.targetDirEntry.getDirectory(
-            PathUtil.dirname(targetRelativePath), {create: false},
-            function(dirEntry) {
-              sourceEntry.moveTo(
-                  dirEntry, PathUtil.basename(targetRelativePath),
-                  function(targetEntry) {
-                    self.eventRouter_.sendOperationEvent(
-                        'moved', [sourceEntry, targetEntry]);
-                    task.markEntryComplete(targetEntry, 0);
-                    successCallback();
-                  },
-                  onFilesystemError);
-            },
-            onFilesystemError);
-      },
-      errorCallback);
-};
-
-/**
- * Service a zip file creation task.
- *
- * @param {FileCopyManager.Task} task A zip task to be run.
- * @param {function()} successCallback On complete.
- * @param {function(FileCopyManager.Error)} errorCallback On error.
- * @private
- */
-FileCopyManager.prototype.serviceZipTask_ = function(
-    task, successCallback, errorCallback) {
-  var self = this;
-  var dirURL = task.zipBaseDirEntry.toURL();
-  var selectionURLs = [];
-  for (var i = 0; i < task.pendingDirectories.length; i++)
-    selectionURLs.push(task.pendingDirectories[i].toURL());
-  for (var i = 0; i < task.pendingFiles.length; i++)
-    selectionURLs.push(task.pendingFiles[i].toURL());
-
-  var destName = 'Archive';
-  if (task.originalEntries.length == 1) {
-    var entryPath = task.originalEntries[0].fullPath;
-    var i = entryPath.lastIndexOf('/');
-    var basename = (i < 0) ? entryPath : entryPath.substr(i + 1);
-    i = basename.lastIndexOf('.');
-    destName = ((i < 0) ? basename : basename.substr(0, i));
-  }
-
-  var onDeduplicated = function(destPath) {
-    var onZipSelectionComplete = function(success) {
-      if (success) {
-        self.eventRouter_.sendProgressEvent('SUCCESS', self.getStatus());
-        successCallback();
-      } else {
-        errorCallback(new FileCopyManager.Error(
-            util.FileOperationErrorType.FILESYSTEM_ERROR,
-            Object.create(FileError.prototype, {
-              code: {
-                get: function() { return FileError.INVALID_MODIFICATION_ERR; }
-              }
-            })));
-      }
-    };
-
-    self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
-    chrome.fileBrowserPrivate.zipSelection(dirURL, selectionURLs, destPath,
-        onZipSelectionComplete);
-  };
-
-  FileCopyManager.Task.deduplicatePath(
-      task.targetDirEntry, destName + '.zip', onDeduplicated, errorCallback);
-};
-
-/**
- * Copy the contents of sourceEntry into targetEntry.
- *
- * @private
- * @param {Entry} sourceEntry entry that will be copied.
- * @param {Entry} targetEntry entry to which sourceEntry will be copied.
- * @param {function(Entry, number)} progressCallback function that will be
+ * @param {FileEntry} sourceEntry The file entry that will be copied.
+ * @param {FileEntry} targetEntry The file entry to which sourceEntry will be
+ *     copied.
+ * @param {function(FileEntry, number)} progressCallback Function that will be
  *     called when a part of the source entry is copied. It takes |targetEntry|
  *     and size of the last copied chunk as parameters.
- * @param {function(Entry, number)} successCallback function that will be called
- *     the copy operation finishes. It takes |targetEntry| and size of the last
- *     (not previously reported) copied chunk as parameters.
- * @param {function(FileError)} errorCallback function that will be called
+ * @param {function(FileEntry, number)} successCallback Function that will be
+ *     called the copy operation finishes. It takes |targetEntry| and size of
+ *     the last (not previously reported) copied chunk as parameters.
+ * @param {function(FileError)} errorCallback Function that will be called
  *     if an error is encountered. Takes error type and additional error data
  *     as parameters.
+ * @private
  */
-FileCopyManager.prototype.copyEntry_ = function(sourceEntry,
-                                                targetEntry,
-                                                progressCallback,
-                                                successCallback,
-                                                errorCallback) {
+FileCopyManager.prototype.copyFileEntry_ = function(sourceEntry,
+                                                    targetEntry,
+                                                    progressCallback,
+                                                    successCallback,
+                                                    errorCallback) {
   if (this.maybeCancel_())
     return;
 
@@ -1223,6 +1088,179 @@
 };
 
 /**
+ * Moves all entries in the task.
+ *
+ * @param {FileCopyManager.Task} task A move task to be run.
+ * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback
+ *     invoked when an entry is changed.
+ * @param {function()} progressCallback Callback invoked periodically during
+ *     the moving.
+ * @param {function()} successCallback On success.
+ * @param {function(FileCopyManager.Error)} errorCallback On error.
+ * @private
+ */
+FileCopyManager.prototype.serviceMoveTask_ = function(
+    task, entryChangedCallback, progressCallback, successCallback,
+    errorCallback) {
+  if (task.pendingDirectories.length + task.pendingFiles.length == 0) {
+    successCallback();
+    return;
+  }
+
+  this.processMoveEntry_(
+      task, task.getNextEntry(), entryChangedCallback,
+      (function onCompleted() {
+        // We should not dispatch a PROGRESS event when there is no pending
+        // items in the task.
+        if (task.pendingDirectories.length + task.pendingFiles.length == 0) {
+          successCallback();
+          return;
+        }
+
+        // Move the next entry.
+        progressCallback();
+        this.processMoveEntry_(
+            task, task.getNextEntry(), entryChangedCallback,
+            onCompleted.bind(this), errorCallback);
+      }).bind(this),
+      errorCallback);
+};
+
+/**
+ * Moves the next entry in a given task.
+ *
+ * Implementation note: This method can be simplified more. For example, in
+ * Task.setEntries(), the flag to recurse is set to false for move task,
+ * so that all the entries' originalSourcePath should be
+ * dirname(sourceEntry.fullPath).
+ * Thus, targetRelativePath should contain exact one component. Also we can
+ * skip applyRenames, because the destination directory always should be
+ * task.targetDirEntry.
+ * The unnecessary complexity is due to historical reason.
+ * TODO(hidehiko): Refactor this method.
+ *
+ * @param {FileManager.Task} task A move task.
+ * @param {Entry} sourceEntry An entry to be moved.
+ * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback
+ *     invoked when an entry is changed.
+ * @param {function()} successCallback On success.
+ * @param {function(FileCopyManager.Error)} errorCallback On error.
+ * @private
+ */
+FileCopyManager.prototype.processMoveEntry_ = function(
+    task, sourceEntry, entryChangedCallback, successCallback, errorCallback) {
+  if (this.maybeCancel_())
+    return;
+
+  // |sourceEntry.originalSourcePath| is set in util.recurseAndResolveEntries.
+  var sourcePath = sourceEntry.originalSourcePath;
+  if (sourceEntry.fullPath.substr(0, sourcePath.length) != sourcePath) {
+    // We found an entry in the list that is not relative to the base source
+    // path, something is wrong.
+    errorCallback(new FileCopyManager.Error(
+        util.FileOperationErrorType.UNEXPECTED_SOURCE_FILE,
+        sourceEntry.fullPath));
+    return;
+  }
+
+  FileCopyManager.Task.deduplicatePath(
+      task.targetDirEntry,
+      task.applyRenames(sourceEntry.fullPath.substr(sourcePath.length + 1)),
+      function(targetRelativePath) {
+        var onFilesystemError = function(err) {
+          errorCallback(new FileCopyManager.Error(
+              util.FileOperationErrorType.FILESYSTEM_ERROR,
+              err));
+        };
+
+        task.targetDirEntry.getDirectory(
+            PathUtil.dirname(targetRelativePath), {create: false},
+            function(dirEntry) {
+              sourceEntry.moveTo(
+                  dirEntry, PathUtil.basename(targetRelativePath),
+                  function(targetEntry) {
+                    entryChangedCallback(
+                        util.EntryChangedType.CREATED, targetEntry);
+                    entryChangedCallback(
+                        util.EntryChangedType.DELETED, sourceEntry);
+                    task.markEntryComplete(targetEntry, 0);
+                    successCallback();
+                  },
+                  onFilesystemError);
+            },
+            onFilesystemError);
+      },
+      errorCallback);
+};
+
+/**
+ * Service a zip file creation task.
+ *
+ * @param {FileCopyManager.Task} task A zip task to be run.
+ * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback
+ *     invoked when an entry is changed.
+ * @param {function()} progressCallback Callback invoked periodically during
+ *     the moving.
+ * @param {function()} successCallback On complete.
+ * @param {function(FileCopyManager.Error)} errorCallback On error.
+ * @private
+ */
+FileCopyManager.prototype.serviceZipTask_ = function(
+    task, entryChangedCallback, progressCallback, successCallback,
+    errorCallback) {
+  var dirURL = task.zipBaseDirEntry.toURL();
+  var selectionURLs = [];
+  for (var i = 0; i < task.pendingDirectories.length; i++)
+    selectionURLs.push(task.pendingDirectories[i].toURL());
+  for (var i = 0; i < task.pendingFiles.length; i++)
+    selectionURLs.push(task.pendingFiles[i].toURL());
+
+  // TODO(hidehiko): we should localize the name.
+  var destName = 'Archive';
+  if (task.originalEntries.length == 1) {
+    var entryPath = task.originalEntries[0].fullPath;
+    var i = entryPath.lastIndexOf('/');
+    var basename = (i < 0) ? entryPath : entryPath.substr(i + 1);
+    i = basename.lastIndexOf('.');
+    destName = ((i < 0) ? basename : basename.substr(0, i));
+  }
+
+  var onDeduplicated = function(destPath) {
+    var onZipSelectionComplete = function(success) {
+      var onFilesystemError = function(err) {
+        errorCallback(new FileCopyManager.Error(
+            util.FileOperationErrorType.FILESYSTEM_ERROR,
+            err));
+      };
+
+      if (success) {
+        task.targetDirEntry.getFile(
+            destPath, {create: false},
+            function(entry) {
+              entryChangedCallback(util.EntryChangedType.CREATE, entry);
+              successCallback();
+            },
+            onFilesystemError);
+      } else {
+        onFilesystemError(
+            Object.create(FileError.prototype, {
+              code: {
+                get: function() { return FileError.INVALID_MODIFICATION_ERR; }
+              }
+            }));
+      }
+    };
+
+    progressCallback();
+    chrome.fileBrowserPrivate.zipSelection(dirURL, selectionURLs, destPath,
+        onZipSelectionComplete);
+  };
+
+  FileCopyManager.Task.deduplicatePath(
+      task.targetDirEntry, destName + '.zip', onDeduplicated, errorCallback);
+};
+
+/**
  * Timeout before files are really deleted (to allow undo).
  */
 FileCopyManager.DELETE_TIMEOUT = 30 * 1000;
@@ -1249,7 +1287,8 @@
 FileCopyManager.prototype.serviceAllDeleteTasks_ = function() {
   var self = this;
 
-  var onTaskSuccess = function(task) {
+  var onTaskSuccess = function() {
+    var task = self.deleteTasks_[0];
     self.deleteTasks_.shift();
     if (!self.deleteTasks_.length) {
       // All tasks have been serviced, clean up and exit.
@@ -1298,50 +1337,58 @@
  * Performs the deletion.
  *
  * @param {Object} task The delete task (see deleteEntries function).
- * @param {function(Object)} onComplete Completion callback with the task
- *     as an argument.
- * @param {function(Object)} onFailure Failure callback with the task as an
- *     argument.
+ * @param {function()} successCallback Callback run on success.
+ * @param {function(FileCopyManager.Error)} errorCallback Callback run on error.
  * @private
  */
 FileCopyManager.prototype.serviceDeleteTask_ = function(
-    task, onComplete, onFailure) {
+    task, successCallback, errorCallback) {
   var downcount = task.entries.length;
+  if (downcount == 0) {
+    successCallback();
+    return;
+  }
 
-  var onEntryComplete = function() {
-    if (--downcount == 0)
-      onComplete(task);
-  }.bind(this);
+  var filesystemError = null;
+  var onComplete = function() {
+    if (--downcount > 0)
+      return;
 
-  var onEntryFailure = function() {
-    if (--downcount == 0)
-      onFailure(task);
-  }.bind(this);
-
-  if (downcount == 0)
-    onComplete(task);
+    // All remove operations are processed. Run callback.
+    if (filesystemError) {
+      errorCallback(new FileCopyManager.Error(
+          util.FileOperationErrorType.FILESYSTEM_ERROR, filesystemError));
+    } else {
+      successCallback();
+    }
+  };
 
   for (var i = 0; i < task.entries.length; i++) {
     var entry = task.entries[i];
     util.removeFileOrDirectory(
         entry,
-        onEntryComplete,
-        onEntryFailure);
+        function(currentEntry) {
+          this.eventRouter_.sendEntryChangedEvent(
+              util.EntryChangedType.DELETED, currentEntry);
+          onComplete();
+        }.bind(this, entry),
+        function(error) {
+          if (!filesystemError)
+            filesystemError = error;
+          onComplete();
+        });
   }
 };
 
 /**
  * Creates a zip file for the selection of files.
  *
- * @param {Entry} dirEntry the directory containing the selection.
- * @param {boolean} isOnDrive If directory is on Drive.
- * @param {Array.<Entry>} selectionEntries the selected entries.
+ * @param {Entry} dirEntry The directory containing the selection.
+ * @param {Array.<Entry>} selectionEntries The selected entries.
  */
-FileCopyManager.prototype.zipSelection = function(dirEntry, isOnDrive,
-                                                  selectionEntries) {
+FileCopyManager.prototype.zipSelection = function(dirEntry, selectionEntries) {
   var self = this;
-  var zipTask = new FileCopyManager.Task(
-      dirEntry, isOnDrive, isOnDrive, dirEntry);
+  var zipTask = new FileCopyManager.Task(dirEntry, dirEntry);
   zipTask.zip = true;
   zipTask.setEntries(selectionEntries, function() {
     // TODO: per-entry zip progress update with accurate byte count.
diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js
index f16c187..f9280a9 100644
--- a/chrome/browser/resources/file_manager/js/file_manager.js
+++ b/chrome/browser/resources/file_manager/js/file_manager.js
@@ -62,6 +62,7 @@
  */
 var DialogType = {
   SELECT_FOLDER: 'folder',
+  SELECT_UPLOAD_FOLDER: 'upload-folder',
   SELECT_SAVEAS_FILE: 'saveas-file',
   SELECT_OPEN_FILE: 'open-file',
   SELECT_OPEN_MULTI_FILE: 'open-multi-file',
@@ -118,6 +119,7 @@
  */
 DialogType.isModal = function(type) {
   return type == DialogType.SELECT_FOLDER ||
+      type == DialogType.SELECT_UPLOAD_FOLDER ||
       type == DialogType.SELECT_SAVEAS_FILE ||
       type == DialogType.SELECT_OPEN_FILE ||
       type == DialogType.SELECT_OPEN_MULTI_FILE;
@@ -185,13 +187,13 @@
       sizeStatsResult, spaceInnerBar, spaceInfoLabel, spaceOuterBar) {
     spaceInnerBar.removeAttribute('pending');
     if (sizeStatsResult) {
-      var sizeStr = util.bytesToString(sizeStatsResult.remainingSizeKB * 1024);
+      var sizeStr = util.bytesToString(sizeStatsResult.remainingSize);
       spaceInfoLabel.textContent = strf('SPACE_AVAILABLE', sizeStr);
 
       var usedSpace =
-          sizeStatsResult.totalSizeKB - sizeStatsResult.remainingSizeKB;
+          sizeStatsResult.totalSize - sizeStatsResult.remainingSize;
       spaceInnerBar.style.width =
-          (100 * usedSpace / sizeStatsResult.totalSizeKB) + '%';
+          (100 * usedSpace / sizeStatsResult.totalSize) + '%';
 
       spaceOuterBar.hidden = false;
     } else {
@@ -244,15 +246,6 @@
       }.bind(this));
     }.bind(this));
 
-    // TODO(yoshiki): Remove this after launching folder shortcuts feature.
-    group.add(function(done) {
-      chrome.commandLinePrivate.hasSwitch(
-          'file-manager-enable-folder-shortcuts', function(flag) {
-        this.isFolderShortcutsEnabled_ = flag;
-        done();
-      }.bind(this));
-    }.bind(this));
-
     group.run(callback);
   };
 
@@ -392,6 +385,11 @@
         driveConnectionChangedHandler);
     driveConnectionChangedHandler();
 
+    // Set the initial focus and set it as a fallback.
+    this.document_.addEventListener('focusout', function(e) {
+      if (!e.relatedTarget)
+        this.refocus();
+    }.bind(this));
     this.refocus();
 
     this.initDataTransferOperations_();
@@ -423,8 +421,7 @@
   FileManager.prototype.initDataTransferOperations_ = function() {
     this.copyManager_ = new FileCopyManagerWrapper.getInstance();
 
-    this.butterBar_ = new ButterBar(this.dialogDom_, this.copyManager_,
-        this.metadataCache_);
+    this.butterBar_ = new ButterBar(this.dialogDom_, this.copyManager_);
 
     // CopyManager and ButterBar are required for 'Delete' operation in
     // Open and Save dialogs. But drag-n-drop and copy-paste are not needed.
@@ -436,11 +433,10 @@
     this.copyManager_.addEventListener(
         'copy-progress', this.onCopyProgressBound_);
 
-    this.onCopyManagerOperationCompleteBound_ =
-        this.onCopyManagerOperationComplete_.bind(this);
+    this.onCopyManagerEntryChangedBound_ =
+        this.onCopyManagerEntryChanged_.bind(this);
     this.copyManager_.addEventListener(
-        'copy-operation-complete',
-        this.onCopyManagerOperationCompleteBound_);
+        'entry-changed', this.onCopyManagerEntryChangedBound_);
 
     var controller = this.fileTransferController_ =
         new FileTransferController(this.document_,
@@ -478,9 +474,13 @@
     this.rootsContextMenu_ =
         this.dialogDom_.querySelector('#roots-context-menu');
     cr.ui.Menu.decorate(this.rootsContextMenu_);
-
     this.volumeList_.setContextMenu(this.rootsContextMenu_);
 
+    this.directoryTreeContextMenu_ =
+        this.dialogDom_.querySelector('#directory-tree-context-menu');
+    cr.ui.Menu.decorate(this.directoryTreeContextMenu_);
+    this.directoryTree_.contextMenuForSubitems = this.directoryTreeContextMenu_;
+
     this.textContextMenu_ =
         this.dialogDom_.querySelector('#text-context-menu');
     cr.ui.Menu.decorate(this.textContextMenu_);
@@ -612,8 +612,7 @@
         'create-folder-shortcut', Commands.createFolderShortcutCommand, this);
 
     CommandUtil.registerCommand(this.dialogContainer_,
-        'remove-folder-shortcut', Commands.removeFolderShortcutCommand, this,
-        this.volumeList_);
+        'remove-folder-shortcut', Commands.removeFolderShortcutCommand, this);
 
     CommandUtil.registerCommand(this.dialogContainer_, 'search',
         Commands.searchCommand, this,
@@ -633,8 +632,10 @@
     CommandUtil.registerCommand(doc, 'zoom-out', Commands.zoomOutCommand);
     CommandUtil.registerCommand(doc, 'zoom-reset', Commands.zoomResetCommand);
 
-    CommandUtil.registerCommand(doc, 'cut', Commands.defaultCommand, doc);
-    CommandUtil.registerCommand(doc, 'copy', Commands.defaultCommand, doc);
+    CommandUtil.registerCommand(this.dialogContainer_, 'cut',
+        Commands.defaultCommand, doc);
+    CommandUtil.registerCommand(this.dialogContainer_, 'copy',
+        Commands.defaultCommand, doc);
 
     var inputs = this.dialogDom_.querySelectorAll(
         'input[type=text], input[type=search], textarea');
@@ -772,6 +773,7 @@
     this.fileTypes_ = this.params_.typeList || [];
     metrics.recordEnum('Create', this.dialogType,
         [DialogType.SELECT_FOLDER,
+         DialogType.SELECT_UPLOAD_FOLDER,
          DialogType.SELECT_SAVEAS_FILE,
          DialogType.SELECT_OPEN_FILE,
          DialogType.SELECT_OPEN_MULTI_FILE,
@@ -1022,9 +1024,6 @@
     this.driveBuyMoreStorageCommand_ =
         this.dialogDom_.querySelector('#drive-buy-more-space');
 
-    this.newFolderCommand_ =
-        this.dialogDom_.querySelector('command#newfolder');
-
     this.defaultActionMenuItem_.addEventListener('activate',
         this.dispatchSelectionAction_.bind(this));
 
@@ -1063,6 +1062,7 @@
     var singleSelection =
         this.dialogType == DialogType.SELECT_OPEN_FILE ||
         this.dialogType == DialogType.SELECT_FOLDER ||
+        this.dialogType == DialogType.SELECT_UPLOAD_FOLDER ||
         this.dialogType == DialogType.SELECT_SAVEAS_FILE;
 
     var showSpecialSearchRoots =
@@ -1384,44 +1384,36 @@
   };
 
   /**
-   * Handler of file manager operations. Update directory model
-   * to reflect operation result immediatelly (not waiting directory
-   * update event). Also, preloads thumbnails for the copied images.
+   * Handler of file manager operations. Called when an entry has been
+   * changed.
+   * This updates directory model to reflect operation result immediately (not
+   * waiting for directory update event). Also, preloads thumbnails for the
+   * images of new entries.
+   * See also FileCopyManager.EventRouter.
    *
-   * @param {Event} Operation completion event.
+   * @param {cr.Event} event An event for the entry change.
    * @private
    */
-  FileManager.prototype.onCopyManagerOperationComplete_ = function(event) {
-    var currentPath = this.directoryModel_.getCurrentDirPath();
-    if (this.isOnDrive() && this.directoryModel_.isSearching())
-      return;
+  FileManager.prototype.onCopyManagerEntryChanged_ = function(event) {
+    var type = event.type;
+    var entry = event.entry;
+    this.directoryModel_.onEntryChanged(type, entry);
 
-    var inCurrentDirectory = function(entry) {
-      var fullPath = entry.fullPath;
-      var dirPath = fullPath.substr(0, fullPath.length -
-                                       entry.name.length - 1);
-      return dirPath == currentPath;
-    };
-    for (var i = 0; i < event.affectedEntries.length; i++) {
-      var entry = event.affectedEntries[i];
-      if (inCurrentDirectory(entry)) {
-        this.directoryModel_.onEntryChanged(entry.name);
-      } else if (event.reason == 'copied' && FileType.isImage(entry)) {
-        // Preload a thumbnail if the new copied entry an image.
-        var metadata = entry.getMetadata(function(metadata) {
-          var url = entry.toURL();
-          var thumbnailLoader_ = new ThumbnailLoader(
-              url,
-              ThumbnailLoader.LoaderType.CANVAS,
-              metadata,
-              undefined,  // Media type.
-              FileType.isOnDrive(url) ?
-                  ThumbnailLoader.UseEmbedded.USE_EMBEDDED :
-                  ThumbnailLoader.UseEmbedded.NO_EMBEDDED,
-              10);  // Very low priority.
-              thumbnailLoader_.loadDetachedImage(function(success) {});
-        });
-      }
+    if (type == util.EntryChangedType.CREATE && FileType.isImage(entry)) {
+      // Preload a thumbnail if the new copied entry an image.
+      var metadata = entry.getMetadata(function(metadata) {
+        var url = entry.toURL();
+        var thumbnailLoader_ = new ThumbnailLoader(
+            url,
+            ThumbnailLoader.LoaderType.CANVAS,
+            metadata,
+            undefined,  // Media type.
+            FileType.isOnDrive(url) ?
+                ThumbnailLoader.UseEmbedded.USE_EMBEDDED :
+                ThumbnailLoader.UseEmbedded.NO_EMBEDDED,
+            10);  // Very low priority.
+        thumbnailLoader_.loadDetachedImage(function(success) {});
+      });
     }
   };
 
@@ -1729,6 +1721,11 @@
         defaultTitle = str('SELECT_FOLDER_TITLE');
         break;
 
+      case DialogType.SELECT_UPLOAD_FOLDER:
+        defaultTitle = str('SELECT_UPLOAD_FOLDER_TITLE');
+        okLabel = str('UPLOAD_LABEL');
+        break;
+
       case DialogType.SELECT_OPEN_FILE:
         defaultTitle = str('SELECT_OPEN_FILE_TITLE');
         break;
@@ -1988,8 +1985,13 @@
 
   FileManager.prototype.onDriveConnectionChanged_ = function() {
     var connection = this.volumeManager_.getDriveConnectionState();
+    this.updateCommands();
     if (this.dialogContainer_)
       this.dialogContainer_.setAttribute('connection', connection.type);
+    if (this.shareDialog_.isShowing()) {
+      this.shareDialog_.hide();
+      this.error.show(str('SHARE_ERROR'));
+    }
   };
 
   /**
@@ -2079,7 +2081,6 @@
         this.filePopupCloseCallback_();
         this.filePopupCloseCallback_ = null;
       }
-      this.refocus();
 
       // These operations have to be in the end, otherwise v8 crashes on an
       // assert. See: crbug.com/224174.
@@ -2176,7 +2177,8 @@
    * @return {boolena} True if the flag is enabled.
    */
   FileManager.prototype.isFolderShortcutsEnabled = function() {
-    return this.isFolderShortcutsEnabled_;
+    // TODO(yoshiki): Remove this method in M31.
+    return true;
   };
 
   /**
@@ -2415,13 +2417,26 @@
       this.closeOnUnmount_ = false;
     }
 
-    this.newFolderCommand_.canExecuteChange();
-
+    this.updateCommands();
     this.updateUnformattedDriveStatus_();
     this.updateTitle_();
     this.updateGearMenu_();
   };
 
+  /**
+   * Updates commands' states by emiting canExecute events. Should be used
+   * only if there is need to reevaluate states without an user action, eg.
+   * external events.
+   */
+  FileManager.prototype.updateCommands = function() {
+    var commands = this.dialogDom_.querySelectorAll('command');
+    for (var i = 0; i < commands.length; i++) {
+      // Commands may not have been decorated yet.
+      if (commands[i].canExecuteChange)
+        commands[i].canExecuteChange();
+    }
+  };
+
   // TODO(haruki): Rename this method. "Drive" here does not refer
   // "Google Drive".
   FileManager.prototype.updateUnformattedDriveStatus_ = function() {
@@ -2440,8 +2455,7 @@
 
       // Update 'canExecute' for format command so the format button's disabled
       // property is properly set.
-      var formatCommand = this.dialogDom_.querySelector('command#format');
-      formatCommand.canExecuteChange(errorNode);
+      this.updateCommands();
     } else {
       this.dialogDom_.removeAttribute('unformatted');
     }
@@ -2478,10 +2492,9 @@
         this.copyManager_.removeEventListener(
             'copy-progress', this.onCopyProgressBound_);
       }
-      if (this.onCopyManagerOperationCompleteBound_) {
+      if (this.onCopyManagerEntryChangedBound_) {
         this.copyManager_.removeEventListener(
-            'copy-operation-complete',
-            this.onCopyManagerOperationCompleteBound_);
+            'entry-changed', this.onCopyManagerEntryChangedBound_);
       }
     }
   };
@@ -2612,7 +2625,6 @@
       parent.removeAttribute('renaming');
       parent.removeChild(this.renameInput_);
     }
-    this.refocus();
   };
 
   /**
@@ -2656,8 +2668,7 @@
       this.grid_.endBatchUpdates();
     }
 
-    this.newFolderCommand_.canExecuteChange();
-
+    this.updateCommands();
     this.table_.list.startBatchUpdates();
     this.grid_.startBatchUpdates();
     this.scanInProgress_ = true;
@@ -2690,8 +2701,7 @@
       return;
     }
 
-    this.newFolderCommand_.canExecuteChange();
-
+    this.updateCommands();
     this.hideSpinnerLater_();
     this.refreshCurrentDirectoryMetadata_();
 
@@ -2756,8 +2766,7 @@
       return;
     }
 
-    this.newFolderCommand_.canExecuteChange();
-
+    this.updateCommands();
     this.hideSpinnerLater_();
     if (this.scanCompletedTimer_) {
       clearTimeout(this.scanCompletedTimer_);
@@ -2966,7 +2975,8 @@
         var selection = this.getSelection();
         if (selection.totalCount == 1 &&
             selection.entries[0].isDirectory &&
-            this.dialogType != DialogType.SELECT_FOLDER) {
+            this.dialogType != DialogType.SELECT_FOLDER &&
+            this.dialogType != DialogType.SELECT_UPLOAD_FOLDER) {
           event.preventDefault();
           this.onDirectoryAction(selection.entries[0]);
         } else if (this.dispatchSelectionAction_()) {
@@ -3285,7 +3295,8 @@
     var files = [];
     var selectedIndexes = this.currentList_.selectionModel.selectedIndexes;
 
-    if (this.dialogType == DialogType.SELECT_FOLDER &&
+    if ((this.dialogType == DialogType.SELECT_FOLDER ||
+         this.dialogType == DialogType.SELECT_UPLOAD_FOLDER) &&
         selectedIndexes.length == 0) {
       var url = this.getCurrentDirectoryURL();
       var singleSelection = {
@@ -3330,7 +3341,8 @@
 
     var selectedEntry = dm.item(selectedIndexes[0]);
 
-    if (this.dialogType == DialogType.SELECT_FOLDER) {
+    if (this.dialogType == DialogType.SELECT_FOLDER ||
+        this.dialogType == DialogType.SELECT_UPLOAD_FOLDER) {
       if (!selectedEntry.isDirectory)
         throw new Error('Selected entry is not a folder!');
     } else if (this.dialogType == DialogType.SELECT_OPEN_FILE) {
@@ -3786,7 +3798,6 @@
         this.dialogDom_.querySelector('#default-action-separator');
 
     this.openWithCommand_.canExecuteChange();
-
     this.openWithCommand_.setHidden(!(defaultItem && isMultiple));
     this.defaultActionMenuItem_.hidden = !defaultItem;
     defaultActionSeparator.hidden = !defaultItem;
diff --git a/chrome/browser/resources/file_manager/js/file_manager_commands.js b/chrome/browser/resources/file_manager/js/file_manager_commands.js
index 1cd279e..0dbf706 100644
--- a/chrome/browser/resources/file_manager/js/file_manager_commands.js
+++ b/chrome/browser/resources/file_manager/js/file_manager_commands.js
@@ -7,37 +7,49 @@
 var CommandUtil = {};
 
 /**
- * Extracts root on which command event was dispatched.
+ * Extracts path on which command event was dispatched.
  *
- * @param {Event} event Command event for which to retrieve root to operate on.
- * @param {DirectoryTree|VolumeList} list Directory tree or volume list to
- *     extract root node.
- * @return {string} Path of the found root.
+ * @param {DirectoryTree|DirectoryItem|VolumeList|HTMLLIElement|cr.ui.List}
+ *     element Directory to extract a path from.
+ * @return {?string} Path of the found node.
  */
-CommandUtil.getCommandRoot = function(event, list) {
-  if (list instanceof VolumeList) {
-    var result = list.dataModel.item(
-                     list.getIndexOfListItem(event.target)) ||
-                 list.selectedItem;
-    return result;
-  } else {
-    var entry = list.selectedItem;
+CommandUtil.getCommandPath = function(element) {
+  if (element instanceof VolumeList) {
+    // element is a VolumeList.
+    return element.selectedItem;
+  } else if (element instanceof VolumeItem) {
+    // element is a subitem of VolumeList.
+    var volumeList = element.parentElement;
+    var index = volumeList.getIndexOfListItem(element);
+    return (index != -1) ? volumeList.dataModel.item(index) : null;
+  } else if (element instanceof DirectoryTree) {
+    // element is a DirectoryTree.
+    var item = element.selectedItem;
+    return item && item.fullPath;
+  } else if (element instanceof DirectoryItem) {
+    // element is a sub item in DirectoryTree.
 
-    if (entry && PathUtil.isRootPath(entry.fullPath))
-      return entry.fullPath;
-    else
-      return null;
+    // DirectoryItem.fullPath is set on initialization, but entry is lazily.
+    // We may use fullPath just in case that the entry has not been set yet.
+    return element.entry && element.entry.fullPath ||
+           element.fullPath;
+  } else if (cr.ui.List) {
+    // element is a normal List (eg. the file list on the right panel).
+    var entry = element.selectedItem;
+    return entry && entry.fullPath;
+  } else {
+    console.warn('Unsupported element');
+    return null;
   }
 };
 
 /**
- * @param {Event} event Command event for which to retrieve root type.
- * @param {DirectoryTree} directoryTree Directory tree to extract root node.
- * @return {?string} Found root.
+ * @param {VolumeList} volumeList Volume list to extract root node.
+ * @return {?RootType} Type of the found root.
  */
-CommandUtil.getCommandRootType = function(event, directoryTree) {
-  var root = CommandUtil.getCommandRoot(event, directoryTree);
-  return root && PathUtil.getRootType(root);
+CommandUtil.getCommandRootType = function(volumeList) {
+  var root = CommandUtil.getCommandPath(volumeList);
+  return root && PathUtil.isRootPath(root) && PathUtil.getRootType(root);
 };
 
 /**
@@ -159,19 +171,19 @@
 Commands.unmountCommand = {
   /**
    * @param {Event} event Command event.
-   * @param {DirectoryTree} directoryTree Target directory tree.
+   * @param {VolumeList} volumeList Target volume list.
    */
-  execute: function(event, directoryTree, fileManager) {
-    var root = CommandUtil.getCommandRoot(event, directoryTree);
+  execute: function(event, volumeList, fileManager) {
+    var root = CommandUtil.getCommandPath(volumeList);
     if (root)
       fileManager.unmountVolume(PathUtil.getRootPath(root));
   },
   /**
    * @param {Event} event Command event.
-   * @param {DirectoryTree} directoryTree Target directory tree.
+   * @param {VolumeList} volumeList Target volume list.
    */
-  canExecute: function(event, directoryTree) {
-    var rootType = CommandUtil.getCommandRootType(event, directoryTree);
+  canExecute: function(event, volumeList) {
+    var rootType = CommandUtil.getCommandRootType(volumeList);
 
     event.canExecute = (rootType == RootType.ARCHIVE ||
                         rootType == RootType.REMOVABLE);
@@ -188,10 +200,11 @@
 Commands.formatCommand = {
   /**
    * @param {Event} event Command event.
-   * @param {DirectoryTree} directoryTree Target directory tree.
+   * @param {VolumeList} volumeList Target volume list.
+   * @param {FileManager} fileManager The file manager instance.
    */
-  execute: function(event, directoryTree, fileManager) {
-    var root = CommandUtil.getCommandRoot(event, directoryTree);
+  execute: function(event, volumeList, fileManager) {
+    var root = CommandUtil.getCommandPath(volumeList);
 
     if (root) {
       var url = util.makeFilesystemUrl(PathUtil.getRootPath(root));
@@ -202,10 +215,12 @@
   },
   /**
    * @param {Event} event Command event.
-   * @param {DirectoryTree} directoryTree Target directory tree.
+   * @param {VolumeList} volumeList Target volume list.
+   * @param {FileManager} fileManager The file manager instance.
+   * @param {DirectoryModel} directoryModel The directory model instance.
    */
-  canExecute: function(event, directoryTree, fileManager, directoryModel) {
-    var root = CommandUtil.getCommandRoot(event, directoryTree);
+  canExecute: function(event, volumeList, fileManager, directoryModel) {
+    var root = CommandUtil.getCommandPath(volumeList);
     var removable = root &&
                     PathUtil.getRootType(root) == RootType.REMOVABLE;
     var isReadOnly = root && directoryModel.isPathReadOnly(root);
@@ -220,10 +235,10 @@
 Commands.importCommand = {
   /**
    * @param {Event} event Command event.
-   * @param {DirectoryTree} directoryTree Target directory tree.
+   * @param {VolumeList} volumeList Target volume list.
    */
-  execute: function(event, directoryTree) {
-    var root = CommandUtil.getCommandRoot(event, directoryTree);
+  execute: function(event, volumeList) {
+    var root = CommandUtil.getCommandPath(volumeList);
     if (!root)
       return;
 
@@ -231,10 +246,10 @@
   },
   /**
    * @param {Event} event Command event.
-   * @param {DirectoryTree} directoryTree Target directory tree.
+   * @param {VolumeList} volumeList Target volume list.
    */
-  canExecute: function(event, directoryTree) {
-    var rootType = CommandUtil.getCommandRootType(event, directoryTree);
+  canExecute: function(event, volumeList) {
+    var rootType = CommandUtil.getCommandRootType(volumeList);
     event.canExecute = (rootType != RootType.DRIVE);
   }
 };
@@ -516,8 +531,7 @@
   execute: function(event, fileManager, directoryModel) {
     var dirEntry = directoryModel.getCurrentDirEntry();
     var selectionEntries = fileManager.getSelection().entries;
-    fileManager.copyManager_.zipSelection(dirEntry, fileManager.isOnDrive(),
-                                          selectionEntries);
+    fileManager.copyManager_.zipSelection(dirEntry, selectionEntries);
   },
   canExecute: function(event, fileManager) {
     var selection = fileManager.getSelection();
@@ -552,26 +566,38 @@
    * @param {FileManager} fileManager The file manager instance.
    */
   execute: function(event, fileManager) {
-    var entries = fileManager.getSelection().entries;
-    fileManager.createFolderShortcut(entries[0].fullPath);
+    var path = CommandUtil.getCommandPath(event.target);
+    if (path)
+      fileManager.createFolderShortcut(path);
   },
+
   /**
    * @param {Event} event Command event.
    * @param {FileManager} fileManager The file manager instance.
    */
   canExecute: function(event, fileManager) {
+    var target = event.target;
     // TODO(yoshiki): remove this after launching folder shortcuts feature.
-    if (!fileManager.isFolderShortcutsEnabled()) {
+    if (!fileManager.isFolderShortcutsEnabled() ||
+        !target instanceof VolumeItem && !target instanceof DirectoryItem) {
       event.command.setHidden(true);
       return;
     }
 
-    var selection = fileManager.getSelection();
-    var selectionEntries = selection.entries;
-    var onlyOneFolderSelected =
-        selection && selection.directoryCount == 1 && selection.fileCount == 0;
-    event.canExecute = onlyOneFolderSelected &&
-        !fileManager.folderShortcutExists(selectionEntries[0].fullPath);
+    var path = CommandUtil.getCommandPath(event.target);
+    var folderShortcutExists = path && fileManager.folderShortcutExists(path);
+
+    var onlyOneFolderSelected = true;
+    // Only on list, user can select multiple files. The command is enabled only
+    // when a single file is selected.
+    if (event.target instanceof cr.ui.List) {
+      var items = event.target.selectedItems;
+      onlyOneFolderSelected = (items.length == 1 && items[0].isDirectory);
+    }
+
+    var eligible = path && PathUtil.isEligibleForFolderShortcut(path);
+    event.canExecute =
+        eligible && onlyOneFolderSelected && !folderShortcutExists;
     event.command.setHidden(!onlyOneFolderSelected);
   }
 };
@@ -583,29 +609,30 @@
   /**
    * @param {Event} event Command event.
    * @param {FileManager} fileManager The file manager instance.
-   * @param {DirectoryTree} directoryTree Target directory tree.
    */
-  execute: function(event, fileManager, directoryTree) {
-    var path = CommandUtil.getCommandRoot(event, directoryTree);
-
+  execute: function(event, fileManager) {
+    var path = CommandUtil.getCommandPath(event.target);
     if (path)
       fileManager.removeFolderShortcut(path);
   },
+
   /**
    * @param {Event} event Command event.
    * @param {FileManager} fileManager The file manager instance.
-   * @param {DirectoryTree} directoryTree Target directory tree.
    */
-  canExecute: function(event, fileManager, directoryTree) {
+  canExecute: function(event, fileManager) {
+    var target = event.target;
     // TODO(yoshiki): remove this after launching folder shortcut feature.
-    if (!fileManager.isFolderShortcutsEnabled()) {
+    if (!fileManager.isFolderShortcutsEnabled() ||
+        !target instanceof VolumeItem && !target instanceof DirectoryItem) {
       event.command.setHidden(true);
       return;
     }
 
-    var path = CommandUtil.getCommandRoot(event, directoryTree);
+    var path = CommandUtil.getCommandPath(target);
+    var eligible = path && PathUtil.isEligibleForFolderShortcut(path);
     var isShortcut = path && fileManager.folderShortcutExists(path);
-    event.canExecute = isShortcut;
+    event.canExecute = isShortcut && eligible;
     event.command.setHidden(!isShortcut);
   }
 };
diff --git a/chrome/browser/resources/file_manager/js/file_selection.js b/chrome/browser/resources/file_manager/js/file_selection.js
index bf6ce06..6d159d9 100644
--- a/chrome/browser/resources/file_manager/js/file_selection.js
+++ b/chrome/browser/resources/file_manager/js/file_selection.js
@@ -264,7 +264,8 @@
   var selectable;
   var dialogType = this.fileManager_.dialogType;
 
-  if (dialogType == DialogType.SELECT_FOLDER) {
+  if (dialogType == DialogType.SELECT_FOLDER ||
+      dialogType == DialogType.SELECT_UPLOAD_FOLDER) {
     // In SELECT_FOLDER mode, we allow to select current directory
     // when nothing is selected.
     selectable = this.selection.directoryCount <= 1 &&
@@ -533,11 +534,8 @@
   }
 
   // Sync the commands availability.
-  if (selection.totalCount != 0) {
-    var commands = this.fileManager_.dialogDom_.querySelectorAll('command');
-    for (var i = 0; i < commands.length; i++)
-      commands[i].canExecuteChange();
-  }
+  if (selection.totalCount != 0)
+    this.fileManager_.updateCommands();
 
   // Update context menu.
   this.fileManager_.updateContextMenuActionItems(null, false);
diff --git a/chrome/browser/resources/file_manager/js/file_transfer_controller.js b/chrome/browser/resources/file_manager/js/file_transfer_controller.js
index d2a6294..510f302 100644
--- a/chrome/browser/resources/file_manager/js/file_transfer_controller.js
+++ b/chrome/browser/resources/file_manager/js/file_transfer_controller.js
@@ -150,7 +150,6 @@
 
     // Tag to check it's filemanager data.
     dataTransfer.setData('fs/tag', 'filemanager-data');
-    dataTransfer.setData('fs/isOnDrive', this.isOnDrive);
     dataTransfer.setData('fs/sourceRoot',
                          this.directoryModel_.getCurrentRootPath());
     dataTransfer.setData('fs/directories', directories.join('\n'));
@@ -213,14 +212,10 @@
         dataTransfer.effectAllowed : dataTransfer.getData('fs/effectallowed');
     var toMove = effectAllowed == 'move' ||
         (effectAllowed == 'copyMove' && opt_effect == 'move');
-    var targetOnDrive = (PathUtil.getRootType(destinationPath) ===
-                         RootType.DRIVE);
     this.copyManager_.paste(files,
                             directories,
                             toMove,
-                            dataTransfer.getData('fs/isOnDrive') == 'true',
-                            destinationPath,
-                            targetOnDrive);
+                            destinationPath);
     return toMove ? 'move' : 'copy';
   },
 
diff --git a/chrome/browser/resources/file_manager/js/folder_shortcuts_data_model.js b/chrome/browser/resources/file_manager/js/folder_shortcuts_data_model.js
index 9cf8355..6c02fb2 100644
--- a/chrome/browser/resources/file_manager/js/folder_shortcuts_data_model.js
+++ b/chrome/browser/resources/file_manager/js/folder_shortcuts_data_model.js
@@ -16,6 +16,17 @@
   this.array_ = [];
   this.storage_ = (type == 'sync') ? chrome.storage.sync : chrome.storage.local;
 
+  /**
+   * Eliminate unsupported folders from the list.
+   *
+   * @param {Array.<string>} array Folder array which may contain the
+   *     unsupported folders.
+   * @return {Array.<string>} Folder list without unsupported folder.
+   */
+  var filter = function(array) {
+    return array.filter(PathUtil.isEligibleForFolderShortcut);
+  };
+
   // Loads the contents from the storage to initialize the array.
   this.storage_.get(name, function(value) {
     if (!(name in value))
@@ -25,6 +36,8 @@
     // case.
     var list = value[name];
     if (list instanceof Array) {
+      list = filter(list);
+
       var permutedEvent = new Event('permuted');
       permutedEvent.permutation = this.createPermutation_(this.array_, list);
       this.array_ = list;
@@ -41,6 +54,8 @@
     // Since the value comes from outer resource, we have to check it just in
     // case.
     if (list instanceof Array) {
+      list = filter(list);
+
       // If the list is not changed, do nothing and just return.
       if (this.array_.length == list.length) {
         var changed = false;
diff --git a/chrome/browser/resources/file_manager/js/path_util.js b/chrome/browser/resources/file_manager/js/path_util.js
index d6561d4..c22013b 100644
--- a/chrome/browser/resources/file_manager/js/path_util.js
+++ b/chrome/browser/resources/file_manager/js/path_util.js
@@ -332,3 +332,14 @@
 
   return directoryPath;
 };
+
+/**
+ * Returns if the given path can be a target path of folder shortcut.
+ *
+ * @param {string} directoryPath Diretcoty path to be checked.
+ * @return {boolean} True if the path can be a target path of the shortcut.
+ */
+PathUtil.isEligibleForFolderShortcut = function(directoryPath) {
+  return !PathUtil.isSpecialSearchRoot(directoryPath) &&
+         !PathUtil.isRootPath(directoryPath);
+};
diff --git a/chrome/browser/resources/file_manager/js/share_dialog.js b/chrome/browser/resources/file_manager/js/share_dialog.js
index e05e0c5..15ba6c8 100644
--- a/chrome/browser/resources/file_manager/js/share_dialog.js
+++ b/chrome/browser/resources/file_manager/js/share_dialog.js
@@ -199,6 +199,9 @@
     this.webViewWrapper_.style.height = '';
 
     var onError = function() {
+      // Already closed, therefore ignore.
+      if (!this.onQueueTaskFinished_)
+        return;
       onFailure();
       this.hide();
     }.bind(this);
@@ -250,6 +253,9 @@
         onError();
         return;
       }
+      // Already closed, therefore ignore.
+      if (!this.onQueueTaskFinished_)
+        return;
       this.shareClient_ = new ShareClient(this.webView_,
                                           shareUrl,
                                           this);
@@ -257,3 +263,11 @@
     }.bind(this));
   }.bind(this));
 };
+
+/**
+ * Tells whether the share dialog is being shown or not.
+ * @return {boolean} True if shown, false otherwise.
+ */
+ShareDialog.prototype.isShowing = function() {
+  return this.container_.classList.contains('shown');
+};
diff --git a/chrome/browser/resources/file_manager/js/test_util.js b/chrome/browser/resources/file_manager/js/test_util.js
index b589210..07005a3 100644
--- a/chrome/browser/resources/file_manager/js/test_util.js
+++ b/chrome/browser/resources/file_manager/js/test_util.js
@@ -338,7 +338,13 @@
     if (!button)
       return false;
     button.click();
-    callback();
+    // Wait until the dialog is removed from the DOM.
+    test.util.repeatUntilTrue_(function() {
+      if (contentWindow.document.querySelector('.cr-dialog-container'))
+        return false;
+      callback();
+      return true;
+    });
     return true;
   });
 };
diff --git a/chrome/browser/resources/file_manager/js/util.js b/chrome/browser/resources/file_manager/js/util.js
index 9f5a564..b31f404 100644
--- a/chrome/browser/resources/file_manager/js/util.js
+++ b/chrome/browser/resources/file_manager/js/util.js
@@ -1149,3 +1149,12 @@
   TARGET_EXISTS: 1,
   FILESYSTEM_ERROR: 2,
 };
+
+/**
+ * The type of an entry changed event.
+ * @enum {number}
+ */
+util.EntryChangedType = {
+  CREATED: 0,
+  DELETED: 1,
+};
diff --git a/chrome/browser/resources/file_manager/js/volume_list.js b/chrome/browser/resources/file_manager/js/volume_list.js
index a0e55f3..e27301c 100644
--- a/chrome/browser/resources/file_manager/js/volume_list.js
+++ b/chrome/browser/resources/file_manager/js/volume_list.js
@@ -129,7 +129,24 @@
 };
 
 /**
+ * A volume item.
+ * @constructor
+ * @extends {HTMLLIElement}
+ */
+var VolumeItem = cr.ui.define('li');
+
+VolumeItem.prototype = {
+  __proto__: HTMLLIElement.prototype,
+
+  decorate: function() {
+    // Nothing to do.
+  },
+};
+
+/**
  * A volume list.
+ * @constructor
+ * @extends {cr.ui.List}
  */
 function VolumeList() {
 }
@@ -195,11 +212,13 @@
  * Creates an element of a volume. This method is called from cr.ui.List
  * internally.
  * @param {string} path Path of the directory to be rendered.
- * @return {HTMLElement} Rendered element.
+ * @return {VolumeItem} Rendered element.
  * @private
  */
 VolumeList.prototype.renderRoot_ = function(path) {
-  var li = cr.doc.createElement('li');
+  var li = new VolumeItem;
+  // TODO(yoshiki): Move the following initialization code to the constructor
+  // of VolumeItem.
   li.className = 'root-item';
   li.setAttribute('role', 'option');
   var dm = this.directoryModel_;
diff --git a/chrome/browser/resources/file_manager/main.html b/chrome/browser/resources/file_manager/main.html
index 9bd4c52..8c80ecb 100644
--- a/chrome/browser/resources/file_manager/main.html
+++ b/chrome/browser/resources/file_manager/main.html
@@ -14,6 +14,8 @@
 
     <meta name="google" value="notranslate">
 
+    <link rel="stylesheet" href="chrome://resources/css/apps/topbutton_bar.css"></link>
+
     <link rel="stylesheet" href="css/list.css"></link>
     <link rel="stylesheet" href="css/table.css"></link>
     <link rel="stylesheet" href="css/tree.css"></link>
@@ -202,6 +204,7 @@
       <menuitem command="#newfolder"
                 visibleif="saveas-file full-page"></menuitem>
     </menu>
+
     <menu id="roots-context-menu" class="chrome-menu" >
       <menuitem command="#import-photos"></menuitem>
       <menuitem command="#unmount"></menuitem>
@@ -209,6 +212,10 @@
       <menuitem command="#remove-folder-shortcut"></menuitem>
     </menu>
 
+    <menu id="directory-tree-context-menu" class="chrome-menu" >
+      <menuitem command="#create-folder-shortcut"></menuitem>
+    </menu>
+
     <menu id="gear-menu" class="chrome-menu" showShortcuts>
       <menuitem command="#newwindow"></menuitem>
       <menuitem command="#newfolder"></menuitem>
@@ -287,13 +294,16 @@
             <div id="search-icon"></div>
           </div>
           <div id="search-clear-button"></div>
-          <div class="buttonbar">
-            <button class="menubutton" id="gear-button" tabindex="10"
-                    menu="#gear-menu">
+          <div class="topbutton-bar">
+            <button class="menubutton gear-button" id="gear-button" tabindex="10"
+                    menu="#gear-menu"
+                    i18n-values="aria-label:GEAR_BUTTON_TOOLTIP">
             </button>
-            <button id="maximize-button" visibleif="full-page" tabindex="-1">
+            <button class="maximize-button" id="maximize-button"
+                    visibleif="full-page" tabindex="-1">
             </button>
-            <button id="close-button" visibleif="full-page" tabindex="-1">
+            <button class="close-button" id="close-button"
+                    visibleif="full-page" tabindex="-1">
             </button>
           </div>
         </div>
@@ -340,10 +350,11 @@
               </div>
             </div>
             <div class="right buttonbar" visibleif="full-page">
+              <button id="share-button" command="#share" tabindex="2"
+                      i18n-values="aria-label:SHARE_BUTTON_LABEL"></button>
               <button id="tasks" class="combobutton" menu="#tasks-menu"
-                      tabindex="2">
-              </button>
-              <button id="delete-button" command="#delete" tabindex="3"
+                      tabindex="3"></button>
+              <button id="delete-button" command="#delete" tabindex="4"
                       i18n-values="aria-label:DELETE_BUTTON_LABEL"></button>
             </div>
             <div class=preparing-label i18n-content=PREPARING_LABEL></div>
@@ -352,8 +363,8 @@
             </div>
             <div class="right buttonbar" id="open-panel" visibleif="open-file open-multi-file">
               <select class="file-type"></select>
-              <button class=ok disabled tabindex="6"></button>
-              <button class=cancel i18n-content=CANCEL_LABEL tabindex="7"></button>
+              <button class=ok disabled tabindex="7"></button>
+              <button class=cancel i18n-content=CANCEL_LABEL tabindex="8"></button>
             </div>
           </div>
           <div id="unmounted-panel"></div>
@@ -368,11 +379,11 @@
       <div class="left">
         <button id="new-folder" i18n-content=NEW_FOLDER_BUTTON_LABEL
                 visibleif="saveas-file folder" command="#newfolder"
-                tabindex="4">
+                tabindex="5">
         </button>
         <div id="filename-input-box">
           <div class=filename-label i18n-content=FILENAME_LABEL></div>
-          <input type=text spellcheck=false tabindex="5">
+          <input type=text spellcheck=false tabindex="6">
         </div>
         <div class=preparing-label i18n-content=PREPARING_LABEL></div>
         <div class=progress-bar>
@@ -381,8 +392,8 @@
       </div>
       <div class="right buttonbar">
         <select class="file-type"></select>
-        <button class=ok disabled tabindex="6"></button>
-        <button class=cancel i18n-content=CANCEL_LABEL tabindex="7"></button>
+        <button class=ok disabled tabindex="7"></button>
+        <button class=cancel i18n-content=CANCEL_LABEL tabindex="8"></button>
       </div>
     </div>
     <div id="drag-container"></div>
diff --git a/chrome/browser/resources/google_now/background.js b/chrome/browser/resources/google_now/background.js
index 06540f5..6695154 100644
--- a/chrome/browser/resources/google_now/background.js
+++ b/chrome/browser/resources/google_now/background.js
@@ -75,6 +75,7 @@
 var UPDATE_CARDS_TASK_NAME = 'update-cards';
 var DISMISS_CARD_TASK_NAME = 'dismiss-card';
 var RETRY_DISMISS_TASK_NAME = 'retry-dismiss';
+var STATE_CHANGED_TASK_NAME = 'state-changed';
 
 var LOCATION_WATCH_NAME = 'location-watch';
 
@@ -87,12 +88,6 @@
 var ToastButtonIndex = {YES: 0, NO: 1};
 
 /**
- * The action that the user performed on the welcome toast.
- * @enum {number}
- */
-var ToastOptionResponse = {CHOSE_YES: 1, CHOSE_NO: 2};
-
-/**
  * Checks if a new task can't be scheduled when another task is already
  * scheduled.
  * @param {string} newTaskName Name of the new task.
@@ -119,11 +114,12 @@
   return false;
 }
 
+var googleGeolocationAccessEnabledPref =
+    chrome.preferencesPrivate.googleGeolocationAccessEnabled;
+
 var tasks = buildTaskManager(areTasksConflicting);
 
 // Add error processing to API calls.
-tasks.instrumentApiFunction(chrome.identity, 'getAuthToken', 1);
-tasks.instrumentApiFunction(chrome.identity, 'removeCachedAuthToken', 1);
 tasks.instrumentApiFunction(chrome.location.onLocationUpdate, 'addListener', 0);
 tasks.instrumentApiFunction(chrome.notifications, 'create', 2);
 tasks.instrumentApiFunction(chrome.notifications, 'update', 2);
@@ -132,6 +128,14 @@
     chrome.notifications.onButtonClicked, 'addListener', 0);
 tasks.instrumentApiFunction(chrome.notifications.onClicked, 'addListener', 0);
 tasks.instrumentApiFunction(chrome.notifications.onClosed, 'addListener', 0);
+tasks.instrumentApiFunction(
+    googleGeolocationAccessEnabledPref,
+    'get',
+    1);
+tasks.instrumentApiFunction(
+    googleGeolocationAccessEnabledPref.onChange,
+    'addListener',
+    0);
 tasks.instrumentApiFunction(chrome.runtime.onInstalled, 'addListener', 0);
 tasks.instrumentApiFunction(chrome.runtime.onStartup, 'addListener', 0);
 tasks.instrumentApiFunction(chrome.tabs, 'create', 1);
@@ -149,11 +153,13 @@
     MAXIMUM_RETRY_DISMISS_PERIOD_SECONDS);
 var cardSet = buildCardSet();
 
+var authenticationManager = buildAuthenticationManager();
+
 /**
- * Diagnostic event identifier.
+ * Google Now UMA event identifier.
  * @enum {number}
  */
-var DiagnosticEvent = {
+var GoogleNowEvent = {
   REQUEST_FOR_CARDS_TOTAL: 0,
   REQUEST_FOR_CARDS_SUCCESS: 1,
   CARDS_PARSE_SUCCESS: 2,
@@ -163,21 +169,23 @@
   LOCATION_UPDATE: 6,
   EXTENSION_START: 7,
   SHOW_WELCOME_TOAST: 8,
-  EVENTS_TOTAL: 9  // EVENTS_TOTAL is not an event; all new events need to be
-                   // added before it.
+  STOPPED: 9,
+  USER_SUPPRESSED: 10,
+  EVENTS_TOTAL: 11  // EVENTS_TOTAL is not an event; all new events need to be
+                    // added before it.
 };
 
 /**
- * Records a diagnostic event.
- * @param {DiagnosticEvent} event Event identifier.
+ * Records a Google Now Event.
+ * @param {GoogleNowEvent} event Event identifier.
  */
 function recordEvent(event) {
   var metricDescription = {
     metricName: 'GoogleNow.Event',
     type: 'histogram-linear',
     min: 1,
-    max: DiagnosticEvent.EVENTS_TOTAL,
-    buckets: DiagnosticEvent.EVENTS_TOTAL + 1
+    max: GoogleNowEvent.EVENTS_TOTAL,
+    buckets: GoogleNowEvent.EVENTS_TOTAL + 1
   };
 
   chrome.metricsPrivate.recordValue(metricDescription, event);
@@ -190,13 +198,9 @@
  *     parameter.
  */
 function setAuthorization(request, callbackBoolean) {
-  tasks.debugSetStepName('setAuthorization-getAuthToken');
-  chrome.identity.getAuthToken({interactive: false}, function(token) {
-    var errorMessage =
-        chrome.runtime.lastError && chrome.runtime.lastError.message;
-    console.log('setAuthorization: error=' + errorMessage +
-                ', token=' + (token && 'non-empty'));
-    if (chrome.runtime.lastError || !token) {
+  tasks.debugSetStepName('setAuthorization-isSignedIn');
+  authenticationManager.isSignedIn(function(token) {
+    if (!token) {
       callbackBoolean(false);
       return;
     }
@@ -208,11 +212,8 @@
     request.onloadend = tasks.wrapCallback(function(event) {
       if (request.status == HTTP_FORBIDDEN ||
           request.status == HTTP_UNAUTHORIZED) {
-        tasks.debugSetStepName('setAuthorization-removeCachedAuthToken');
-        chrome.identity.removeCachedAuthToken({token: token}, function() {
-          // After purging the token cache, call getAuthToken() again to let
-          // Chrome know about the problem with the token.
-          chrome.identity.getAuthToken({interactive: false}, function() {});
+        tasks.debugSetStepName('setAuthorization-removeToken');
+        authenticationManager.removeToken(token, function() {
           originalOnLoadEnd(event);
         });
       } else {
@@ -301,7 +302,7 @@
         }
       }
 
-      recordEvent(DiagnosticEvent.CARDS_PARSE_SUCCESS);
+      recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS);
 
       // Create/update notifications and store their new properties.
       var newNotificationsData = {};
@@ -330,6 +331,25 @@
 }
 
 /**
+ * Removes all cards and card state on Google Now close down.
+ * For example, this occurs when the geolocation preference is unchecked in the
+ * content settings.
+ */
+function removeAllCards() {
+  console.log('removeAllCards');
+
+  // TODO(robliao): Once Google Now clears its own checkbox in the
+  // notifications center and bug 260376 is fixed, the below clearing
+  // code is no longer necessary.
+  chrome.notifications.getAll(function(notifications) {
+    for (var notificationId in notifications) {
+      chrome.notifications.clear(notificationId, function() {});
+    }
+    storage.set({notificationsData: {}});
+  });
+}
+
+/**
  * Requests notification cards from the server.
  * @param {Location} position Location of this computer.
  * @param {function()} callback Completion callback.
@@ -343,7 +363,7 @@
     return;
   }
 
-  recordEvent(DiagnosticEvent.REQUEST_FOR_CARDS_TOTAL);
+  recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL);
 
   // TODO(vadimt): Should we use 'q' as the parameter name?
   var requestParameters =
@@ -357,7 +377,7 @@
   request.onloadend = function(event) {
     console.log('requestNotificationCards-onloadend ' + request.status);
     if (request.status == HTTP_OK) {
-      recordEvent(DiagnosticEvent.REQUEST_FOR_CARDS_SUCCESS);
+      recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS);
       parseAndShowNotificationCards(request.response, callback);
     } else {
       callback();
@@ -379,11 +399,22 @@
  */
 function requestLocation() {
   console.log('requestLocation');
-  recordEvent(DiagnosticEvent.LOCATION_REQUEST);
-  // TODO(vadimt): Figure out location request options.
-  chrome.location.watchLocation(LOCATION_WATCH_NAME, {});
+  recordEvent(GoogleNowEvent.LOCATION_REQUEST);
+  // TODO(vadimt): Figure out location request options. Use experiments
+  // framework to enable setting these parameters remotely.
+  chrome.location.watchLocation(LOCATION_WATCH_NAME, {
+    minDistanceInMeters: 100,
+    minTimeInMilliseconds: 180000  // 3 minutes.
+  });
 }
 
+/**
+ * Stops getting the location.
+ */
+function stopRequestLocation() {
+  console.log('stopRequestLocation');
+  chrome.location.clearWatch(LOCATION_WATCH_NAME);
+}
 
 /**
  * Obtains new location; requests and shows notification cards based on this
@@ -395,15 +426,19 @@
       ' @' + new Date());
   tasks.add(UPDATE_CARDS_TASK_NAME, function(callback) {
     console.log('updateNotificationsCards-task-begin');
-    updateCardsAttempts.planForNext(function() {
-      processPendingDismissals(function(success) {
-        if (success) {
-          // The cards are requested only if there are no unsent dismissals.
-          requestNotificationCards(position, callback);
-        } else {
-          callback();
-        }
-      });
+    updateCardsAttempts.isRunning(function(running) {
+      if (running) {
+        updateCardsAttempts.planForNext(function() {
+          processPendingDismissals(function(success) {
+            if (success) {
+              // The cards are requested only if there are no unsent dismissals.
+              requestNotificationCards(position, callback);
+            } else {
+              callback();
+            }
+          });
+        });
+      }
     });
   });
 }
@@ -429,12 +464,12 @@
     return;
   }
 
-  recordEvent(DiagnosticEvent.DISMISS_REQUEST_TOTAL);
+  recordEvent(GoogleNowEvent.DISMISS_REQUEST_TOTAL);
   var request = buildServerRequest('dismiss', 'application/json');
   request.onloadend = function(event) {
     console.log('requestDismissingCard-onloadend ' + request.status);
     if (request.status == HTTP_OK)
-      recordEvent(DiagnosticEvent.DISMISS_REQUEST_SUCCESS);
+      recordEvent(GoogleNowEvent.DISMISS_REQUEST_SUCCESS);
 
     // A dismissal doesn't require further retries if it was successful or
     // doesn't have a chance for successful completion.
@@ -566,21 +601,17 @@
  * @param {number} buttonIndex The index of the button which was clicked.
  */
 function onToastNotificationClicked(buttonIndex) {
+  storage.set({userRespondedToToast: true});
+
   if (buttonIndex == ToastButtonIndex.YES) {
     chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastClickedYes');
-    storage.set({toastState: ToastOptionResponse.CHOSE_YES});
-
-    // TODO(zturner): Update chrome geolocation setting once the settings
-    // API is in place.
-    startPollingCards();
+    googleGeolocationAccessEnabledPref.set({value: true});
+    // The googlegeolocationaccessenabled preference change callback
+    // will take care of starting the poll for cards.
   } else {
     chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastClickedNo');
-    storage.set({toastState: ToastOptionResponse.CHOSE_NO});
+    onStateChange();
   }
-
-  chrome.notifications.clear(
-      WELCOME_TOAST_NOTIFICATION_ID,
-      function(wasCleared) {});
 }
 
 /**
@@ -596,7 +627,7 @@
     // Even though they only closed the notification without clicking no, treat
     // it as though they clicked No anwyay, and don't show the toast again.
     chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastDismissed');
-    storage.set({toastState: ToastOptionResponse.CHOSE_NO});
+    storage.set({userRespondedToToast: true});
     return;
   }
 
@@ -642,26 +673,156 @@
 }
 
 /**
+ * Stops all machinery in the polling system.
+ */
+function stopPollingCards() {
+  stopRequestLocation();
+
+  updateCardsAttempts.stop();
+
+  removeAllCards();
+}
+
+/**
  * Initializes the event page on install or on browser startup.
  */
 function initialize() {
-  recordEvent(DiagnosticEvent.EXTENSION_START);
-  storage.get('toastState', function(items) {
-    // The toast state might be undefined (e.g. not in storage yet) if this is
-    // the first time ever being prompted.
+  recordEvent(GoogleNowEvent.EXTENSION_START);
 
-    // TODO(zturner): Get the value of isGeolocationEnabled from the settings
-    // api and additionally make sure it is true.
-    if (!items.toastState) {
-      if (NOTIFICATION_CARDS_URL) {
-        chrome.identity.getAuthToken({interactive: false}, function(token) {
-          if (!chrome.runtime.lastError && token)
-            showWelcomeToast();
-        });
-      }
-    } else if (items.toastState == ToastOptionResponse.CHOSE_YES) {
-      startPollingCards();
+  // Alarms persist across chrome restarts. This is undesirable since it
+  // prevents us from starting up everything (alarms are a heuristic to
+  // determine if we are already running). To mitigate this, we will
+  // shut everything down on initialize before starting everything up.
+  stopPollingCards();
+  onStateChange();
+}
+
+/**
+ * Starts or stops the polling of cards.
+ * @param {boolean} shouldPollCardsRequest true to start and
+ *     false to stop polling cards.
+ * @param {function} onSuccess Called on completion.
+ */
+function setShouldPollCards(shouldPollCardsRequest, onSuccess) {
+  tasks.debugSetStepName(
+        'setShouldRun-shouldRun-updateCardsAttemptsIsRunning');
+  updateCardsAttempts.isRunning(function(currentValue) {
+    if (shouldPollCardsRequest != currentValue) {
+      console.log('Action Taken setShouldPollCards=' + shouldPollCardsRequest);
+      if (shouldPollCardsRequest)
+        startPollingCards();
+      else
+        stopPollingCards();
     }
+    onSuccess();
+  });
+}
+
+/**
+ * Shows or hides the toast.
+ * @param {boolean} visibleRequest true to show the toast and
+ *     false to hide the toast.
+ * @param {function} onSuccess Called on completion.
+ */
+function setToastVisible(visibleRequest, onSuccess) {
+  tasks.debugSetStepName(
+      'setToastVisible-shouldSetToastVisible-getAllNotifications');
+  chrome.notifications.getAll(function(notifications) {
+    // TODO(vadimt): Figure out what to do when notifications are disabled for
+    // our extension.
+    notifications = notifications || {};
+
+    if (visibleRequest != !!notifications[WELCOME_TOAST_NOTIFICATION_ID]) {
+      console.log('Action Taken setToastVisible=' + visibleRequest);
+      if (visibleRequest)
+        showWelcomeToast();
+      else
+        hideWelcomeToast();
+    }
+
+    onSuccess();
+  });
+}
+
+/**
+ * Does the actual work of deciding what Google Now should do
+ * based off of the current state of Chrome.
+ * @param {boolean} signedIn true if the user is signed in.
+ * @param {boolean} geolocationEnabled true if
+ *     the geolocation option is enabled.
+ * @param {boolean} userRespondedToToast true if
+ *     the user has responded to the toast.
+ * @param {function()} callback Call this function on completion.
+ */
+function updateRunningState(
+    signedIn,
+    geolocationEnabled,
+    userRespondedToToast,
+    callback) {
+
+  console.log(
+      'State Update signedIn=' + signedIn + ' ' +
+      'geolocationEnabled=' + geolocationEnabled + ' ' +
+      'userRespondedToToast=' + userRespondedToToast);
+
+  var shouldSetToastVisible = false;
+  var shouldPollCards = false;
+
+  if (signedIn) {
+    if (geolocationEnabled) {
+      if (!userRespondedToToast) {
+        // If the user enabled geolocation independently of Google Now,
+        // the user has implicitly responded to the toast.
+        // We do not want to show it again.
+        storage.set({userRespondedToToast: true});
+      }
+
+      shouldPollCards = true;
+    } else {
+      if (userRespondedToToast) {
+        recordEvent(GoogleNowEvent.USER_SUPPRESSED);
+      } else {
+        shouldSetToastVisible = true;
+      }
+    }
+  } else {
+    recordEvent(GoogleNowEvent.STOPPED);
+  }
+
+  console.log(
+      'Requested Actions setToastVisible=' + shouldSetToastVisible + ' ' +
+      'setShouldPollCards=' + shouldPollCards);
+
+  setToastVisible(shouldSetToastVisible, function() {
+    setShouldPollCards(shouldPollCards, callback);
+  });
+}
+
+/**
+ * Coordinates the behavior of Google Now for Chrome depending on
+ * Chrome and extension state.
+ */
+function onStateChange() {
+  tasks.add(STATE_CHANGED_TASK_NAME, function(callback) {
+    tasks.debugSetStepName('onStateChange-isSignedIn');
+    authenticationManager.isSignedIn(function(token) {
+      var signedIn = !!token && !!NOTIFICATION_CARDS_URL;
+      tasks.debugSetStepName(
+          'onStateChange-get-googleGeolocationAccessEnabledPref');
+      googleGeolocationAccessEnabledPref.get({}, function(prefValue) {
+        var geolocationEnabled = !!prefValue.value;
+        tasks.debugSetStepName(
+          'onStateChange-get-userRespondedToToast');
+        storage.get('userRespondedToToast', function(items) {
+          var userRespondedToToast = !!items.userRespondedToToast;
+          updateRunningState(
+              signedIn,
+              geolocationEnabled,
+              userRespondedToToast,
+              callback);
+        });
+      });
+    });
   });
 }
 
@@ -670,7 +831,7 @@
  * Google Now cards.
  */
 function showWelcomeToast() {
-  recordEvent(DiagnosticEvent.SHOW_WELCOME_TOAST);
+  recordEvent(GoogleNowEvent.SHOW_WELCOME_TOAST);
   // TODO(zturner): Localize this once the component extension localization
   // api is complete.
   // TODO(zturner): Add icons.
@@ -687,6 +848,15 @@
       function(notificationId) {});
 }
 
+/**
+ * Hides the welcome toast.
+ */
+function hideWelcomeToast() {
+  chrome.notifications.clear(
+      WELCOME_TOAST_NOTIFICATION_ID,
+      function() {});
+}
+
 chrome.runtime.onInstalled.addListener(function(details) {
   console.log('onInstalled ' + JSON.stringify(details));
   if (details.reason != 'chrome_update') {
@@ -699,6 +869,16 @@
   initialize();
 });
 
+googleGeolocationAccessEnabledPref.onChange.addListener(function(prefValue) {
+  console.log('googleGeolocationAccessEnabledPref onChange ' + prefValue.value);
+  onStateChange();
+});
+
+authenticationManager.addListener(function() {
+  console.log('signIn State Change');
+  onStateChange();
+});
+
 chrome.notifications.onClicked.addListener(
     function(notificationId) {
       chrome.metricsPrivate.recordUserAction('GoogleNow.MessageClicked');
@@ -726,7 +906,7 @@
 chrome.notifications.onClosed.addListener(onNotificationClosed);
 
 chrome.location.onLocationUpdate.addListener(function(position) {
-  recordEvent(DiagnosticEvent.LOCATION_UPDATE);
+  recordEvent(GoogleNowEvent.LOCATION_UPDATE);
   updateNotificationsCards(position);
 });
 
diff --git a/chrome/browser/resources/google_now/background_test_util.js b/chrome/browser/resources/google_now/background_test_util.js
index a2309c1..3e52dd7 100644
--- a/chrome/browser/resources/google_now/background_test_util.js
+++ b/chrome/browser/resources/google_now/background_test_util.js
@@ -5,9 +5,20 @@
 // Mocks for globals needed for loading background.js.
 
 function emptyMock() {}
+
 function buildTaskManager() {
-  return {instrumentApiFunction: emptyMock};
+  return {
+    debugSetStepName: emptyMock,
+    instrumentApiFunction: emptyMock,
+  };
 }
+
+function buildAuthenticationManager() {
+  return {
+    addListener: emptyMock
+  };
+}
+
 var instrumentApiFunction = emptyMock;
 var buildAttemptManager = emptyMock;
 var buildCardSet = emptyMock;
@@ -19,6 +30,11 @@
   onClosed: emptyListener
 };
 chrome['omnibox'] = {onInputEntered: emptyListener};
+chrome['preferencesPrivate'] = {
+  googleGeolocationAccessEnabled: {
+    onChange: emptyListener
+  }
+};
 chrome['runtime'] = {
   onInstalled: emptyListener,
   onStartup: emptyListener
diff --git a/chrome/browser/resources/google_now/background_unittest.gtestjs b/chrome/browser/resources/google_now/background_unittest.gtestjs
index a556c3f..5ad7050 100644
--- a/chrome/browser/resources/google_now/background_unittest.gtestjs
+++ b/chrome/browser/resources/google_now/background_unittest.gtestjs
@@ -30,14 +30,22 @@
   testTaskPair(UPDATE_CARDS_TASK_NAME, UPDATE_CARDS_TASK_NAME, true);
   testTaskPair(UPDATE_CARDS_TASK_NAME, DISMISS_CARD_TASK_NAME, false);
   testTaskPair(UPDATE_CARDS_TASK_NAME, RETRY_DISMISS_TASK_NAME, false);
+  testTaskPair(UPDATE_CARDS_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
 
   testTaskPair(DISMISS_CARD_TASK_NAME, UPDATE_CARDS_TASK_NAME, false);
   testTaskPair(DISMISS_CARD_TASK_NAME, DISMISS_CARD_TASK_NAME, false);
   testTaskPair(DISMISS_CARD_TASK_NAME, RETRY_DISMISS_TASK_NAME, false);
+  testTaskPair(DISMISS_CARD_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
 
   testTaskPair(RETRY_DISMISS_TASK_NAME, UPDATE_CARDS_TASK_NAME, true);
   testTaskPair(RETRY_DISMISS_TASK_NAME, DISMISS_CARD_TASK_NAME, true);
   testTaskPair(RETRY_DISMISS_TASK_NAME, RETRY_DISMISS_TASK_NAME, true);
+  testTaskPair(RETRY_DISMISS_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
+
+  testTaskPair(STATE_CHANGED_TASK_NAME, UPDATE_CARDS_TASK_NAME, false);
+  testTaskPair(STATE_CHANGED_TASK_NAME, DISMISS_CARD_TASK_NAME, false);
+  testTaskPair(STATE_CHANGED_TASK_NAME, RETRY_DISMISS_TASK_NAME, false);
+  testTaskPair(STATE_CHANGED_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
 });
 
 /**
@@ -46,35 +54,133 @@
  */
 function mockInitializeDependencies(fixture) {
   fixture.makeAndRegisterMockGlobals([
-      'recordEvent',
-      'showWelcomeToast',
-      'startPollingCards']);
-  fixture.makeAndRegisterMockApis(
-      ['storage.get', 'chrome.identity.getAuthToken']);
+    'recordEvent',
+    'showWelcomeToast',
+    'startPollingCards'
+  ]);
+  fixture.makeAndRegisterMockApis([
+    'authenticationManager.isSignedIn',
+    'chrome.location.clearWatch',
+    'chrome.notifications.getAll',
+    'chrome.preferencesPrivate.googleGeolocationAccessEnabled.get',
+    'storage.get',
+    'storage.set',
+    'tasks.add',
+    'updateCardsAttempts.isRunning',
+    'updateCardsAttempts.stop'
+  ]);
+}
+
+/**
+ * Sets up the test to expect the state machine calls and send
+ * the specified state machine state. Currently used to test initialize().
+ * Note that this CAN NOT be used if any of the methods below are called
+ * outside of this context with the same argument matchers.
+ * expects() calls cannot be chained with the same argument matchers.
+ * @param {object} mockApisObj Mock APIs Object.
+ * @param {string} testIdentityToken getAuthToken callback token.
+ * @param {boolean} testGeolocationPref Geolocation Preference callback value.
+ * @param {boolean} testUserRespondedToToast User Response to toast
+ &     callback value.
+ */
+function expectStateMachineCalls(
+    mockApisObj,
+    testIdentityToken,
+    testGeolocationPref,
+    testUserRespondedToToast) {
+  var authenticationManagerIsSignedInSavedArgs = new SaveMockArguments();
+  mockApisObj.expects(once()).
+      authenticationManager_isSignedIn(
+          authenticationManagerIsSignedInSavedArgs.match(ANYTHING)).
+      will(invokeCallback(
+          authenticationManagerIsSignedInSavedArgs,
+          0,
+          testIdentityToken));
+
+  var googleGeolocationPrefGetSavedArgs = new SaveMockArguments();
+  mockApisObj.expects(once()).
+      chrome_preferencesPrivate_googleGeolocationAccessEnabled_get(
+          googleGeolocationPrefGetSavedArgs.match(eqJSON({})),
+          googleGeolocationPrefGetSavedArgs.match(ANYTHING)).
+      will(invokeCallback(
+          googleGeolocationPrefGetSavedArgs, 1, {value: testGeolocationPref}));
+
+  var storageGetSavedArgs = new SaveMockArguments();
+  mockApisObj.expects(once()).
+      storage_get(
+          storageGetSavedArgs.match(eq('userRespondedToToast')),
+          storageGetSavedArgs.match(ANYTHING)).
+      will(invokeCallback(storageGetSavedArgs, 1, testUserRespondedToToast));
+}
+
+/**
+ * Sets up the test to expect the initialization calls that
+ * initialize() invokes.
+ * Note that this CAN NOT be used if any of the methods below are called
+ * outside of this context with the same argument matchers.
+ * expects() calls cannot be chained with the same argument matchers.
+ */
+function expectInitialization(mockApisObj) {
+  mockApisObj.expects(once()).
+      chrome_location_clearWatch(ANYTHING);
+  mockApisObj.expects(once()).
+      updateCardsAttempts_stop();
+  mockApisObj.expects(once()).
+      storage_set(eqJSON({notificationsData: {}}));
+  var tasksAddSavedArgs = new SaveMockArguments();
+  mockApisObj.expects(once()).
+      tasks_add(
+          tasksAddSavedArgs.match(ANYTHING),
+          tasksAddSavedArgs.match(ANYTHING)).
+      will(invokeCallback(tasksAddSavedArgs, 1, function() {}));
+  var updateCardsAttemptsIsRunningSavedArgs = new SaveMockArguments();
+  mockApisObj.expects(once()).
+      updateCardsAttempts_isRunning(
+          updateCardsAttemptsIsRunningSavedArgs.match(ANYTHING)).
+      will(
+          invokeCallback(
+             updateCardsAttemptsIsRunningSavedArgs, 0, false));
 }
 
 TEST_F(
     'GoogleNowBackgroundUnitTest',
     'Initialize_ToastStateEmpty1',
     function() {
-      // Tests the case when toast state is empty and NOTIFICATION_CARDS_URL is
-      // not set. In this case, the function should quietly exit after finding
-      // out that NOTIFICATION_CARDS_URL is empty.
+      // Tests the case when the user isn't signed in and NOTIFICATION_CARDS_URL
+      // is not set. Since NOTIFICATION_CARDS_URL is empty,
+      // nothing should start.
 
       // Setup and expectations.
-      var testToastState = {};
       NOTIFICATION_CARDS_URL = undefined;
+      var testIdentityToken = undefined;
+      var testGeolocationPref = false;
+      var testUserRespondedToToast = {};
 
       mockInitializeDependencies(this);
 
       this.mockGlobals.expects(once()).recordEvent(
-          DiagnosticEvent.EXTENSION_START);
-      var storageGetSavedArgs = new SaveMockArguments();
-      this.mockApis.expects(once()).
-          storage_get(
-              storageGetSavedArgs.match(eq('toastState')),
-              storageGetSavedArgs.match(ANYTHING)).
-          will(invokeCallback(storageGetSavedArgs, 1, testToastState));
+          GoogleNowEvent.EXTENSION_START);
+
+      this.mockGlobals.expects(once()).recordEvent(
+          GoogleNowEvent.STOPPED);
+
+      expectInitialization(this.mockApis);
+
+      expectStateMachineCalls(
+          this.mockApis,
+          testIdentityToken,
+          testGeolocationPref,
+          testUserRespondedToToast);
+
+      var chromeNotificationGetAllSavedArgs = new SaveMockArguments();
+      this.mockApis.expects(exactly(2)).
+          chrome_notifications_getAll(
+              chromeNotificationGetAllSavedArgs.match(ANYTHING)).
+          will(
+              invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}),
+              invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}));
+
+      // TODO(robliao,vadimt): Determine the granularity of testing to perform.
 
       // Invoking the tested function.
       initialize();
@@ -84,34 +190,39 @@
     'GoogleNowBackgroundUnitTest',
     'Initialize_ToastStateEmpty2',
     function() {
-      // Tests the case when toast state is empty and NOTIFICATION_CARDS_URL is
-      // set, but getAuthToken fails most likely because the user is not signed
-      // in. In this case, the function should quietly exit after finding out
-      // that getAuthToken fails.
+      // Tests the case when NOTIFICATION_CARDS_URL is but getAuthToken fails
+      // most likely because the user is not signed in. In this case, the
+      // function should quietly exit after finding out that getAuthToken fails.
 
       // Setup and expectations.
-      var testToastState = {};
       NOTIFICATION_CARDS_URL = 'https://some.server.url.com';
       var testIdentityToken = undefined;
+      var testGeolocationPref = false;
+      var testUserRespondedToToast = {};
 
       mockInitializeDependencies(this);
 
       this.mockGlobals.expects(once()).recordEvent(
-          DiagnosticEvent.EXTENSION_START);
-      var storageGetSavedArgs = new SaveMockArguments();
-      this.mockApis.expects(once()).
-          storage_get(
-              storageGetSavedArgs.match(eq('toastState')),
-              storageGetSavedArgs.match(ANYTHING)).
-          will(invokeCallback(storageGetSavedArgs, 1, testToastState));
-      var chromeIdentityGetAuthTokenSavedArgs = new SaveMockArguments();
-      this.mockApis.expects(once()).
-          chrome_identity_getAuthToken(
-              chromeIdentityGetAuthTokenSavedArgs.match(
-                  eqJSON({interactive: false})),
-              chromeIdentityGetAuthTokenSavedArgs.match(ANYTHING)).
-          will(invokeCallback(
-              chromeIdentityGetAuthTokenSavedArgs, 1, testIdentityToken));
+          GoogleNowEvent.EXTENSION_START);
+
+      this.mockGlobals.expects(once()).recordEvent(
+          GoogleNowEvent.STOPPED);
+
+      expectInitialization(this.mockApis);
+
+      expectStateMachineCalls(
+          this.mockApis,
+          testIdentityToken,
+          testGeolocationPref,
+          testUserRespondedToToast);
+
+      var chromeNotificationGetAllSavedArgs = new SaveMockArguments();
+      this.mockApis.expects(exactly(2)).
+          chrome_notifications_getAll(
+              chromeNotificationGetAllSavedArgs.match(ANYTHING)).
+          will(
+              invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}),
+              invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}));
 
       // Invoking the tested function.
       initialize();
@@ -121,77 +232,113 @@
     'GoogleNowBackgroundUnitTest',
     'Initialize_ToastStateEmpty3',
     function() {
-      // Tests the case when toast state is empty and NOTIFICATION_CARDS_URL is
-      // set, and getAuthToken succeeds. In this case, the function should
-      // invoke showWelcomeToast().
+      // Tests the case when NOTIFICATION_CARDS_URL is set, getAuthToken
+      // succeeds, and the user has never responded to the toast.
+      // In this case, the function should invoke showWelcomeToast().
 
       // Setup and expectations.
-      var testToastState = {};
       NOTIFICATION_CARDS_URL = 'https://some.server.url.com';
       var testIdentityToken = 'some identity token';
+      var testGeolocationPref = false;
+      var testUserRespondedToToast = {};
 
       mockInitializeDependencies(this);
 
       this.mockGlobals.expects(once()).recordEvent(
-          DiagnosticEvent.EXTENSION_START);
-      var storageGetSavedArgs = new SaveMockArguments();
-      this.mockApis.expects(once()).
-          storage_get(
-              storageGetSavedArgs.match(eq('toastState')),
-              storageGetSavedArgs.match(ANYTHING)).
-          will(invokeCallback(storageGetSavedArgs, 1, testToastState));
-      var chromeIdentityGetAuthTokenSavedArgs = new SaveMockArguments();
-      this.mockApis.expects(once()).
-          chrome_identity_getAuthToken(
-              chromeIdentityGetAuthTokenSavedArgs.match(
-                  eqJSON({interactive: false})),
-              chromeIdentityGetAuthTokenSavedArgs.match(ANYTHING)).
-          will(invokeCallback(
-              chromeIdentityGetAuthTokenSavedArgs, 1, testIdentityToken));
+          GoogleNowEvent.EXTENSION_START);
+
+      expectInitialization(this.mockApis);
+
+      expectStateMachineCalls(
+          this.mockApis,
+          testIdentityToken,
+          testGeolocationPref,
+          testUserRespondedToToast);
+
+      var chromeNotificationGetAllSavedArgs = new SaveMockArguments();
+      this.mockApis.expects(exactly(2)).
+          chrome_notifications_getAll(
+              chromeNotificationGetAllSavedArgs.match(ANYTHING)).
+          will(
+              invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}),
+              invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}));
+
       this.mockGlobals.expects(once()).showWelcomeToast();
 
       // Invoking the tested function.
       initialize();
     });
 
-TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_ToastStateYes', function() {
-  // Tests the case when the user has answered "yes" to the toast in the past.
-  // In this case, the function should invoke startPollingCards().
+TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_RunGoogleNow', function() {
+  // Tests if Google Now will invoke startPollingCards when all
+  // of the required state is fulfilled.
 
   // Setup and expectations.
-  var testToastState = {toastState: ToastOptionResponse.CHOSE_YES};
+  NOTIFICATION_CARDS_URL = 'https://some.server.url.com';
+  var testIdentityToken = 'some identity token';
+  var testGeolocationPref = true;
+  var testUserRespondedToToast = {userRespondedToToast: true};
 
   mockInitializeDependencies(this);
 
-  this.mockGlobals.expects(once()).recordEvent(DiagnosticEvent.EXTENSION_START);
-  var storageGetSavedArgs = new SaveMockArguments();
-  this.mockApis.expects(once()).
-      storage_get(
-          storageGetSavedArgs.match(eq('toastState')),
-          storageGetSavedArgs.match(ANYTHING)).
-      will(invokeCallback(storageGetSavedArgs, 1, testToastState));
+  this.mockGlobals.expects(once()).recordEvent(
+        GoogleNowEvent.EXTENSION_START);
+
+  expectInitialization(this.mockApis);
+
+  expectStateMachineCalls(
+      this.mockApis,
+      testIdentityToken,
+      testGeolocationPref,
+      testUserRespondedToToast);
+
+  var chromeNotificationGetAllSavedArgs = new SaveMockArguments();
+  this.mockApis.expects(exactly(2)).
+      chrome_notifications_getAll(
+          chromeNotificationGetAllSavedArgs.match(ANYTHING)).
+      will(
+          invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}),
+          invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}));
+
   this.mockGlobals.expects(once()).startPollingCards();
 
   // Invoking the tested function.
   initialize();
 });
 
-TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_ToastStateNo', function() {
-  // Tests the case when the user has answered "no" to the toast in the past.
-  // In this case, the function should do nothing.
+TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_NoGeolocation', function() {
+  // Tests the case where everything is in place except for the
+  // Geolocation Preference after the user responded to the toast.
 
   // Setup and expectations.
-  var testToastState = {toastState: ToastOptionResponse.CHOSE_NO};
+  NOTIFICATION_CARDS_URL = 'https://some.server.url.com';
+  var testIdentityToken = 'some identity token';
+  var testGeolocationPref = false;
+  var testUserRespondedToToast = {userRespondedToToast: true};
 
   mockInitializeDependencies(this);
 
-  this.mockGlobals.expects(once()).recordEvent(DiagnosticEvent.EXTENSION_START);
-  var storageGetSavedArgs = new SaveMockArguments();
-  this.mockApis.expects(once()).
-      storage_get(
-          storageGetSavedArgs.match(eq('toastState')),
-          storageGetSavedArgs.match(ANYTHING)).
-      will(invokeCallback(storageGetSavedArgs, 1, testToastState));
+  this.mockGlobals.expects(once()).recordEvent(
+        GoogleNowEvent.EXTENSION_START);
+
+  this.mockGlobals.expects(once()).recordEvent(
+      GoogleNowEvent.USER_SUPPRESSED);
+
+  expectInitialization(this.mockApis);
+
+  expectStateMachineCalls(
+      this.mockApis,
+      testIdentityToken,
+      testGeolocationPref,
+      testUserRespondedToToast);
+
+  var chromeNotificationGetAllSavedArgs = new SaveMockArguments();
+  this.mockApis.expects(exactly(2)).
+      chrome_notifications_getAll(
+          chromeNotificationGetAllSavedArgs.match(ANYTHING)).
+      will(
+          invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}),
+          invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}));
 
   // Invoking the tested function.
   initialize();
diff --git a/chrome/browser/resources/google_now/utility.js b/chrome/browser/resources/google_now/utility.js
index 7a7585d..cdabb78 100644
--- a/chrome/browser/resources/google_now/utility.js
+++ b/chrome/browser/resources/google_now/utility.js
@@ -82,6 +82,26 @@
   var isInTask = false;
 
   /**
+   * True if currently executed code runs in an instrumented callback.
+   * @type {boolean}
+   */
+  var isInInstrumentedCallback = false;
+
+  /**
+   * Checks that we run in an instrumented callback.
+   */
+  function checkInInstrumentedCallback() {
+    if (!isInInstrumentedCallback) {
+      // Cannot use verify() since no one will catch the exception.
+      // This alert will detect bugs at the development stage, and is very
+      // unlikely to be seen by users.
+      var error = 'Not in instrumented callback: ' + new Error().stack;
+      console.error(error);
+      alert(error);
+    }
+  }
+
+  /**
    * Starts the first queued task.
    */
   function startFirst() {
@@ -130,9 +150,10 @@
    * task. Otherwise, stores the task for future execution.
    * @param {string} taskName Name of the task.
    * @param {function(function())} task Function to run. Takes a callback
-   *     parameter.
+   *     parameter. Call this callback on completion.
    */
   function add(taskName, task) {
+    checkInInstrumentedCallback();
     console.log('Adding task ' + taskName);
     if (!canQueue(taskName))
       return;
@@ -207,18 +228,21 @@
   /**
    * Adds error processing to an API callback.
    * @param {Function} callback Callback to instrument.
-   * @param {boolean=} opt_dontRequire True if the callback is not required to
-   *     be invoked.
+   * @param {boolean=} opt_isEventListener True if the callback is an event
+   *      listener.
    * @return {Function} Instrumented callback.
    */
-  function wrapCallback(callback, opt_dontRequire) {
-    verify(!(opt_dontRequire && isInTask), 'Unrequired callback in a task.');
+  function wrapCallback(callback, opt_isEventListener) {
+    verify(!(opt_isEventListener && isInTask),
+           'Unrequired callback in a task.');
     var callbackId = nextCallbackId++;
     var isTaskCallback = isInTask;
     if (isTaskCallback)
       ++taskPendingCallbackCount;
-    if (!opt_dontRequire)
+    if (!opt_isEventListener) {
+      checkInInstrumentedCallback();
       pendingCallbacks[callbackId] = new Error().stack;
+    }
 
     return function() {
       // This is the wrapper for the callback.
@@ -227,11 +251,16 @@
           verify(!isInTask, 'wrapCallback: already in task');
           isInTask = true;
         }
-        if (!opt_dontRequire)
+        if (!opt_isEventListener)
           delete pendingCallbacks[callbackId];
 
         // Call the original callback.
+        verify(!isInInstrumentedCallback, 'Re-entering instrumented callback');
+        isInInstrumentedCallback = true;
         callback.apply(null, arguments);
+        verify(isInInstrumentedCallback,
+               'Instrumented callback is not instrumented upon exit');
+        isInInstrumentedCallback = false;
 
         if (isTaskCallback) {
           verify(isInTask, 'wrapCallback: not in task at exit');
@@ -282,7 +311,9 @@
     };
   }
 
+  instrumentApiFunction(chrome.alarms, 'get', 1);
   instrumentApiFunction(chrome.alarms.onAlarm, 'addListener', 0);
+  instrumentApiFunction(chrome.identity, 'getAuthToken', 1);
   instrumentApiFunction(chrome.runtime.onSuspend, 'addListener', 0);
 
   chrome.runtime.onSuspend.addListener(function() {
@@ -332,6 +363,17 @@
   }
 
   /**
+   * Indicates if this attempt manager has started.
+   * @param {function(boolean)} callback The function's boolean parameter is
+   *     true if the attempt manager has started, false otherwise.
+   */
+  function isRunning(callback) {
+    chrome.alarms.get(alarmName, function(alarmInfo) {
+      callback(!!alarmInfo);
+    });
+  }
+
+  /**
    * Schedules next attempt.
    * @param {number=} opt_previousDelaySeconds Previous delay in a sequence of
    *     retry attempts, if specified. Not specified for scheduling first retry
@@ -388,12 +430,98 @@
 
   chrome.alarms.onAlarm.addListener(function(alarm) {
     if (alarm.name == alarmName)
-      attempt();
+      isRunning(function(running) {
+        if (running)
+          attempt();
+      });
   });
 
   return {
     start: start,
     planForNext: planForNext,
-    stop: stop
+    stop: stop,
+    isRunning: isRunning
+  };
+}
+
+// TODO(robliao): Ideally, the authentication watcher infrastructure
+// below would be an API change to chrome.identity.
+// When this happens, remove the code below.
+
+/**
+ * Wraps chrome.identity to provide limited listening support for
+ * the sign in state by polling periodically for the auth token.
+ * @return {Object} The Authentication Manager interface.
+ */
+function buildAuthenticationManager() {
+  var alarmName = 'sign-in-alarm';
+
+  /**
+   * Determines if the user is signed in and provides a token if signed in.
+   * @param {function(string=)} callback Called on completion.
+   *     If the user is signed in, the string contains the token.
+   */
+  function isSignedIn(callback) {
+    chrome.identity.getAuthToken({interactive: false}, function(token) {
+      token = chrome.runtime.lastError ? undefined : token;
+      callback(token);
+      checkAndNotifyListeners(!!token);
+    });
+  }
+
+  /**
+   * Removes the specified cached token.
+   * @param {string} token Authentication Token to remove from the cache.
+   * @param {function} onSuccess Called on completion.
+   */
+  function removeToken(token, onSuccess) {
+    chrome.identity.removeCachedAuthToken({token: token}, function() {
+      // Removing the token from the cache will change the sign in state.
+      // Repoll now to check the state and notify listeners.
+      // This also lets Chrome now about a possible problem with the token.
+      isSignedIn(function() {});
+      onSuccess();
+    });
+  }
+
+  var listeners = [];
+
+  /**
+   * Registers a listener that gets called back when the signed in state
+   * is found to be changed.
+   * @param {function} callback Called when the answer to isSignedIn changes.
+   */
+  function addListener(callback) {
+    listeners.push(callback);
+  }
+
+  // Tracks the last answer of isSignedIn. checkAndNotifyListeners will not
+  // notify the listeners if this is null because technically, no sign in
+  // state change occurred.
+  var lastReturnedSignedInState = null;
+
+  function checkAndNotifyListeners(currentSignedInState) {
+    if ((lastReturnedSignedInState !== currentSignedInState) &&
+        (lastReturnedSignedInState !== null)) {
+      for (var listenerIndex in listeners) {
+        listeners[listenerIndex]();
+      }
+    }
+    lastReturnedSignedInState = currentSignedInState;
+  }
+
+  chrome.alarms.onAlarm.addListener(function(alarm) {
+    if (alarm.name == alarmName)
+      isSignedIn(function() {});
+  });
+
+  // Poll for the sign in state every hour.
+  // One hour is just an arbitrary amount of time chosen.
+  chrome.alarms.create(alarmName, {periodInMinutes: 60});
+
+  return {
+    addListener: addListener,
+    isSignedIn: isSignedIn,
+    removeToken: removeToken
   };
 }
diff --git a/chrome/browser/resources/history/history.js b/chrome/browser/resources/history/history.js
index 637dc78..1f688e5 100644
--- a/chrome/browser/resources/history/history.js
+++ b/chrome/browser/resources/history/history.js
@@ -1777,9 +1777,16 @@
   // Do nothing if a bookmark star is clicked.
   if (event.defaultPrevented)
     return;
-  var tagName = event.target.tagName;
-  if (tagName == 'BUTTON' || tagName == 'INPUT' || tagName == 'A')
-    return;
+  var element = event.target;
+  // Do nothing if the event happened in an interactive element.
+  for (; element != event.currentTarget; element = element.parentNode) {
+    switch (element.tagName) {
+      case 'A':
+      case 'BUTTON':
+      case 'INPUT':
+        return;
+    }
+  }
   var checkbox = event.currentTarget.control;
   checkbox.checked = !checkbox.checked;
   handleCheckboxStateChange(checkbox, event.shiftKey);
diff --git a/chrome/browser/resources/image_loader/worker.js b/chrome/browser/resources/image_loader/worker.js
index 7dba926..fe1f2f1 100644
--- a/chrome/browser/resources/image_loader/worker.js
+++ b/chrome/browser/resources/image_loader/worker.js
@@ -131,25 +131,19 @@
  * @private
  */
 Worker.prototype.continue_ = function() {
-  var index = 0;
-  while (index < this.pendingRequests_.length) {
-    var request = this.pendingRequests_[index];
-
-    // Run only up to MAXIMUM_IN_PARALLEL in the same time.
-    if (this.activeRequests_.length == Worker.MAXIMUM_IN_PARALLEL)
-      return;
-
-    this.pendingRequests_.splice(index, 1);
+  // Run only up to MAXIMUM_IN_PARALLEL in the same time.
+  while (this.pendingRequests_.length &&
+         this.activeRequests_.length < Worker.MAXIMUM_IN_PARALLEL) {
+    var request = this.pendingRequests_.shift();
     this.activeRequests_.push(request);
 
     // Try to load from cache. If doesn't exist, then download.
-    var currentRequest = request;
-    currentRequest.loadFromCacheAndProcess(
-        this.finish_.bind(this, currentRequest),
-        function() {
+    request.loadFromCacheAndProcess(
+        this.finish_.bind(this, request),
+        function(currentRequest) {
           currentRequest.downloadAndProcess(
               this.finish_.bind(this, currentRequest));
-        }.bind(this));
+        }.bind(this, request));
   }
 };
 
diff --git a/chrome/browser/resources/inspect/inspect.js b/chrome/browser/resources/inspect/inspect.js
index a0faf0e..12bff1c 100644
--- a/chrome/browser/resources/inspect/inspect.js
+++ b/chrome/browser/resources/inspect/inspect.js
@@ -75,76 +75,70 @@
     addToWorkersList(data[i]);
 }
 
-function populateDeviceLists(pages) {
-  var pagesDigest = JSON.stringify(pages);
-  if (!pages || pagesDigest == window.pagesDigest)
+function populateDeviceLists(devices) {
+  var devicesDigest = JSON.stringify(devices);
+  if (!devices || devicesDigest == window.devicesDigest)
     return;
 
-  window.pagesDigest = pagesDigest;
+  window.devicesDigest = devicesDigest;
 
-  // Clear existing entries
-  var deviceElements = document.querySelectorAll('.device');
-  for (var i = 0; i < deviceElements.length; i++)
-    deviceElements[i].remove();
   var containerElement = $('devices');
+  containerElement.textContent = '';
 
   // Populate with new entries
-  for (var i = 0; pages && i < pages.length; i++) {
-    var page = pages[i];
+  for (var d = 0; d < devices.length; d++) {
+    var device = devices[d];
 
-    var listId = 'device-' + page.adbSerial;
-    var listElement = $(listId);
-    if (!listElement) {
-      var sectionElement = document.createElement('div');
-      sectionElement.className = 'section device';
-      sectionElement.textContent = page.adbModel;
+    var deviceHeader = document.createElement('div');
+    deviceHeader.className = 'section';
+    deviceHeader.textContent = device.adbModel;
+    containerElement.appendChild(deviceHeader);
 
-      listElement = document.createElement('div');
-      listElement.className = 'list device';
-      listElement.id = listId;
-      containerElement.appendChild(sectionElement);
-      containerElement.appendChild(listElement);
+    var deviceContent = document.createElement('div');
+    deviceContent.className = 'list';
+    containerElement.appendChild(deviceContent);
+
+    for (var b = 0; b < device.browsers.length; b++) {
+      var browser = device.browsers[b];
+
+      var browserHeader = document.createElement('div');
+      browserHeader.className = 'small-section';
+      browserHeader.textContent = browser.adbBrowserName;
+      deviceContent.appendChild(browserHeader);
+
+      var browserPages = document.createElement('div');
+      browserPages.className = 'list package';
+      deviceContent.appendChild(browserPages);
+
+      for (var p = 0; p < browser.pages.length; p++) {
+        addTargetToList(
+            browser.pages[p], browserPages, ['faviconUrl', 'name', 'url']);
+      }
     }
-
-    var packageId = 'package-' + page.adbModel + '-' + page.adbPackage;
-    var packageElement = $(packageId);
-    if (!packageElement) {
-      var sectionElement = document.createElement('div');
-      sectionElement.className = 'small-section package';
-      sectionElement.textContent = page.adbPackage;
-
-      packageElement = document.createElement('div');
-      packageElement.className = 'list package';
-      packageElement.id = packageId;
-      listElement.appendChild(sectionElement);
-      listElement.appendChild(packageElement);
-    }
-
-    addTargetToList(page, packageId, ['faviconUrl', 'name', 'url']);
   }
 }
 
 function addToPagesList(data) {
-  addTargetToList(data, 'pages', ['faviconUrl', 'name', 'url']);
+  addTargetToList(data, $('pages'), ['faviconUrl', 'name', 'url']);
 }
 
 function addToExtensionsList(data) {
-  addTargetToList(data, 'extensions', ['name', 'url']);
+  addTargetToList(data, $('extensions'), ['name', 'url']);
 }
 
 function addToAppsList(data) {
-  addTargetToList(data, 'apps', ['name', 'url']);
+  addTargetToList(data, $('apps'), ['name', 'url']);
 }
 
 function addToWorkersList(data) {
   addTargetToList(data,
-                  'workers',
+                  $('workers'),
                   ['name', 'url', 'pid'],
                   true);
 }
 
 function addToOthersList(data) {
-  addTargetToList(data, 'others', ['url']);
+  addTargetToList(data, $('others'), ['url']);
 }
 
 function formatValue(data, property) {
@@ -170,8 +164,7 @@
   return span;
 }
 
-function addTargetToList(data, listId, properties, canTerminate) {
-  var list = $(listId);
+function addTargetToList(data, list, properties, canTerminate) {
   var row = document.createElement('div');
   row.className = 'row';
   for (var j = 0; j < properties.length; j++)
diff --git a/chrome/browser/resources/memory_internals/extension_view.css b/chrome/browser/resources/memory_internals/extension_view.css
index 98463bc..24d2a25 100644
--- a/chrome/browser/resources/memory_internals/extension_view.css
+++ b/chrome/browser/resources/memory_internals/extension_view.css
@@ -2,26 +2,6 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-#extension-view {
-  border-spacing: 2px;
-  width: 100%;
-}
-
-#extension-view tr:nth-child(odd):not([class='header']) {
-  background: rgb(239, 243, 255);
-}
-
-#extension-view td {
-  padding: 0.35em 0.5em 0;
-  vertical-align: top;
-}
-
-#extension-view .header th {
-  border-bottom: 1px solid rgb(181, 198, 222);
-  padding: 0.35em 0.5em 0;
-  vertical-align: bottom;
-}
-
 #extension-view .extension-id {
   text-align: right;
   width: 4em;
@@ -37,6 +17,6 @@
   width: 7em;
 }
 
-#extension-view #extension-template {
+#extension-template {
   display: none;
 }
diff --git a/chrome/browser/resources/memory_internals/extension_view.html b/chrome/browser/resources/memory_internals/extension_view.html
index d142f99..154f12b 100644
--- a/chrome/browser/resources/memory_internals/extension_view.html
+++ b/chrome/browser/resources/memory_internals/extension_view.html
@@ -2,8 +2,8 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <h2>Extensions</h2>
-<table id="extension-view">
-  <tr class="header">
+<table class="list" id="extension-view">
+  <tr class="header bottom">
     <th class="extension-id">PID
     <th class="extension-info">Name
     <th class="extension-memory">Private Memory [KB]
diff --git a/chrome/browser/resources/memory_internals/list.css b/chrome/browser/resources/memory_internals/list.css
new file mode 100644
index 0000000..5c9a126
--- /dev/null
+++ b/chrome/browser/resources/memory_internals/list.css
@@ -0,0 +1,27 @@
+/* Copyright 2013 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. */
+
+table.list {
+  border-spacing: 2px;
+  width: 100%;
+}
+
+table.list tr:nth-child(odd) td {
+  background: rgb(239, 243, 255);
+}
+
+table.list td {
+  padding: 0.35em 0.5em 0;
+  vertical-align: top;
+}
+
+table.list .header th {
+  padding: 0.35em 0.5em 0;
+  vertical-align: bottom;
+}
+
+table.list .bottom th,
+table.list th.bottom {
+  border-bottom: 1px solid rgb(181, 198, 222);
+}
diff --git a/chrome/browser/resources/memory_internals/memory_internals.html b/chrome/browser/resources/memory_internals/memory_internals.html
index 3f8ac97..0e65917 100644
--- a/chrome/browser/resources/memory_internals/memory_internals.html
+++ b/chrome/browser/resources/memory_internals/memory_internals.html
@@ -3,6 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <link rel="stylesheet" href="memory_internals.css">
+<link rel="stylesheet" href="list.css">
 <link rel="stylesheet" href="extension_view.css">
 <link rel="stylesheet" href="snapshot_view.css">
 
@@ -14,8 +15,8 @@
   <button id="button-update">Update</button>
 </div>
 
-<include src="snapshot_view.html">
-<include src="extension_view.html">
-
 <h2>JSON</h2>
 <textarea id="json" readonly></textarea>
+
+<include src="snapshot_view.html">
+<include src="extension_view.html">
diff --git a/chrome/browser/resources/memory_internals/memory_internals.js b/chrome/browser/resources/memory_internals/memory_internals.js
index c425997..eff4702 100644
--- a/chrome/browser/resources/memory_internals/memory_internals.js
+++ b/chrome/browser/resources/memory_internals/memory_internals.js
@@ -62,7 +62,16 @@
             value = process['pid'];
             break;
           case 'process-info':
-            value = process['type'] + '<br>' + process['titles'].join('<br>');
+            value = process['type'];
+            if (process['type'].match(/^Tab/)) {
+              // Append each tab's history.
+              for (var j = 0; j < process['history'].length; ++j) {
+                value += '<dl><dt>Hisotry ' + j + ':' +
+                    JoinLinks(process['history'][j]);
+              }
+            } else {
+              value += '<br>' + process['titles'].join('<br>');
+            }
             break;
           case 'process-memory-private':
             value = process['memory_private'];
@@ -93,7 +102,6 @@
       }
 
       var template = $('extension-template').childNodes;
-      // Add information of each extension.
       for (var id in extensions) {
         var extension = extensions[id];
 
@@ -121,6 +129,21 @@
     }
   };
 
+  function JoinLinks(tab) {
+    var line = '';
+    for (var l in tab['history']) {
+      var history = tab['history'][l];
+      var title = (history['title'] == '') ? history['url'] : history['title'];
+      var url = '<a href="' + history['url'] + '">' + title + '</a> (' +
+          history['time'] + ' sec. ago)';
+      if (l == tab['index']) {
+        url = '<strong>' + url + '</strong>';
+      }
+      line += '<dd>' + url;
+    }
+    return line;
+  };
+
   return MainView;
 })();
 
diff --git a/chrome/browser/resources/memory_internals/snapshot_view.css b/chrome/browser/resources/memory_internals/snapshot_view.css
index 8defc42..db86eec 100644
--- a/chrome/browser/resources/memory_internals/snapshot_view.css
+++ b/chrome/browser/resources/memory_internals/snapshot_view.css
@@ -2,29 +2,6 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-#snapshot-view {
-  border-spacing: 2px;
-  width: 100%;
-}
-
-#snapshot-view tr:nth-child(odd):not([class='header']) {
-  background: rgb(239, 243, 255);
-}
-
-#snapshot-view td {
-  padding: 0.35em 0.5em 0;
-  vertical-align: top;
-}
-
-#snapshot-view .header th {
-  padding: 0.35em 0.5em 0;
-  vertical-align: bottom;
-}
-
-#snapshot-view .bottom th {
-  border-bottom: 1px solid rgb(181, 198, 222);
-}
-
 #snapshot-view .process-id {
   text-align: right;
   width: 4em;
@@ -34,6 +11,15 @@
   text-align: left;
 }
 
+#snapshot-view dl {
+  margin: 0;
+  padding: 0;
+}
+
+#snapshot-view dl dd {
+  margin-left: 1em;
+}
+
 #snapshot-view .process-memory {
   border-left: 1px solid rgb(181, 198, 222);
   width: 13em;
@@ -50,6 +36,6 @@
   width: 7em;
 }
 
-#snapshot-view #process-template {
+#process-template {
   display: none;
 }
diff --git a/chrome/browser/resources/memory_internals/snapshot_view.html b/chrome/browser/resources/memory_internals/snapshot_view.html
index 3d2471e..b6f3198 100644
--- a/chrome/browser/resources/memory_internals/snapshot_view.html
+++ b/chrome/browser/resources/memory_internals/snapshot_view.html
@@ -5,10 +5,10 @@
 <div id="os-view">OS: <span id="os-value">unknown</span></div>
 <div id="uptime-view">Uptime: <span id="uptime-value">unknown</span></div>
 
-<table id="snapshot-view">
+<table class="list" id="snapshot-view">
   <tr class="header">
-    <th rowspan="2" class="process-id">PID
-    <th rowspan="2" class="process-info">Name
+    <th rowspan="2" class="process-id bottom">PID
+    <th rowspan="2" class="process-info bottom">Name
     <th colspan="2" class="process-memory">Memory [KB]
   <tr class="header bottom">
     <th class="process-memory-private">Private
diff --git a/chrome/browser/resources/options/chromeos/display_options.js b/chrome/browser/resources/options/chromeos/display_options.js
index 0d0124f..7769fb0 100644
--- a/chrome/browser/resources/options/chromeos/display_options.js
+++ b/chrome/browser/resources/options/chromeos/display_options.js
@@ -170,8 +170,14 @@
         chrome.send('setPrimary', [this.displays_[this.focusedIndex_].id]);
       }.bind(this);
       $('display-options-resolution-selection').onchange = function(ev) {
-        chrome.send('setUIScale', [this.displays_[this.focusedIndex_].id,
-                                   ev.target.value]);
+        var display = this.displays_[this.focusedIndex_];
+        var resolution = display.resolutions[ev.target.value];
+        if (resolution.scale) {
+          chrome.send('setUIScale', [display.id, resolution.scale]);
+        } else {
+          chrome.send('setResolution',
+                      [display.id, resolution.width, resolution.height]);
+        }
       }.bind(this);
       $('display-options-orientation-selection').onchange = function(ev) {
         chrome.send('setOrientation', [this.displays_[this.focusedIndex_].id,
@@ -606,7 +612,7 @@
       $('selected-display-name').textContent = display.name;
 
       var resolution = $('display-options-resolution-selection');
-      if (display.uiScales.length <= 1) {
+      if (display.resolutions.length <= 1) {
         var option = document.createElement('option');
         option.value = 'default';
         option.textContent = display.width + 'x' + display.height;
@@ -614,19 +620,19 @@
         resolution.appendChild(option);
         resolution.disabled = true;
       } else {
-        for (var i = 0; i < display.uiScales.length; i++) {
+        for (var i = 0; i < display.resolutions.length; i++) {
           var option = document.createElement('option');
-          option.value = display.uiScales[i].scale;
-          option.textContent =
-              display.uiScales[i].width + 'x' + display.uiScales[i].height;
-          if (display.uiScales[i].scale == 1.0) {
+          option.value = i;
+          option.textContent = display.resolutions[i].width + 'x' +
+              display.resolutions[i].height;
+          if (display.resolutions[i].isBest) {
             option.textContent += ' ' +
                 loadTimeData.getString('annotateBest');
           }
-          option.selected = display.uiScales[i].selected;
+          option.selected = display.resolutions[i].selected;
           resolution.appendChild(option);
         }
-        resolution.disabled = !display.isInternal;
+        resolution.disabled = (display.resolutions.length <= 1);
       }
     },
 
diff --git a/chrome/browser/resources/options/chromeos/internet_detail.js b/chrome/browser/resources/options/chromeos/internet_detail.js
index edf7944..15998820 100644
--- a/chrome/browser/resources/options/chromeos/internet_detail.js
+++ b/chrome/browser/resources/options/chromeos/internet_detail.js
@@ -155,8 +155,10 @@
         if (data.providerApnList.value.length > 0) {
           var iApn = 0;
           data.apn.apn = data.providerApnList.value[iApn].apn;
-          data.apn.username = data.providerApnList.value[iApn].username;
-          data.apn.password = data.providerApnList.value[iApn].password;
+          var username = data.providerApnList.value[iApn].username;
+          var password = data.providerApnList.value[iApn].password;
+          data.apn.username = username ? username : '';
+          data.apn.password = password ? password : '';
           chrome.send('setApn', [data.servicePath,
                                  String(data.apn.apn),
                                  String(data.apn.username),
@@ -231,8 +233,10 @@
           data.selectedApn = apnSelector.selectedIndex;
         } else {
           $('cellular-apn').value = data.apn.apn;
-          $('cellular-apn-username').value = data.apn.username;
-          $('cellular-apn-password').value = data.apn.password;
+          var username = data.apn.username;
+          var password = data.apn.password;
+          $('cellular-apn-username').value = username ? username : '';
+          $('cellular-apn-password').value = password ? password : '';
 
           updateHidden('.apn-list-view', true);
           updateHidden('.apn-details-view', false);
@@ -1078,20 +1082,20 @@
         var apnList = data.providerApnList.value;
         for (var i = 0; i < apnList.length; i++) {
           var option = document.createElement('option');
-          var name = apnList[i].localizedName;
-          if (name == '' && apnList[i].name != '')
-            name = apnList[i].name;
-          if (name == '')
-            name = apnList[i].apn;
-          else
-            name = name + ' (' + apnList[i].apn + ')';
-          option.textContent = name;
+          var localizedName = apnList[i].localizedName;
+          var name = localizedName ? localizedName : apnList[i].name;
+          var apn = apnList[i].apn;
+          option.textContent = name ? (name + ' (' + apn + ')') : apn;
           option.value = i;
-          if ((data.apn.apn == apnList[i].apn &&
+          // data.apn and data.lastGoodApn will always be defined, however
+          // data.apn.apn and data.lastGoodApn.apn may not be. This is not a
+          // problem, as apnList[i].apn will always be defined and the
+          // comparisons below will work as expected.
+          if ((data.apn.apn == apn &&
                data.apn.username == apnList[i].username &&
                data.apn.password == apnList[i].password) ||
-              (data.apn.apn == '' &&
-               data.lastGoodApn.apn == apnList[i].apn &&
+              (!data.apn.apn &&
+               data.lastGoodApn.apn == apn &&
                data.lastGoodApn.username == apnList[i].username &&
                data.lastGoodApn.password == apnList[i].password)) {
             data.selectedApn = i;
@@ -1099,7 +1103,7 @@
           // Insert new option before "other" option.
           apnSelector.add(option, otherOption);
         }
-        if (data.selectedApn == -1 && data.apn.apn != '') {
+        if (data.selectedApn == -1 && data.apn.apn) {
           var option = document.createElement('option');
           option.textContent = data.apn.apn;
           option.value = -1;
diff --git a/chrome/browser/resources/options/content_settings.html b/chrome/browser/resources/options/content_settings.html
index 87aa0a0..253e9ec 100644
--- a/chrome/browser/resources/options/content_settings.html
+++ b/chrome/browser/resources/options/content_settings.html
@@ -504,6 +504,7 @@
             i18n-content="manageGalleriesButton"></button>
       </div>
     </section>
+    <!-- Automatic Downloads filter -->
     <section>
       <h3 i18n-content="multiple-automatic-downloads_header"></h3>
       <div>
@@ -556,6 +557,34 @@
         </div>
       </div>
     </section>
+    <!-- MIDI system exclusive messages filter -->
+    <section id="experimental-web-midi-settings" hidden="true">
+      <h3 i18n-content="midi-sysex_header"></h3>
+      <div>
+        <div class="radio">
+          <label>
+            <input type="radio" name="midi-sysex" value="allow">
+            <span i18n-content="midiSysExAllow"></span>
+          </label>
+        </div>
+        <div class="radio">
+          <label>
+            <input type="radio" name="midi-sysex" value="ask">
+            <span i18n-content="midiSysExAsk"></span>
+          </label>
+        </div>
+        <div class="radio">
+          <label>
+            <input type="radio" name="midi-sysex" value="block">
+            <span i18n-content="midiSysExBlock"></span>
+          </label>
+        </div>
+        <div class="settings-row">
+          <button class="exceptions-list-button" contentType="midi-sysex"
+              i18n-content="manageExceptions"></button>
+        </div>
+      </div>
+    </section>
   </div>
   <div class="action-area">
     <div class="button-strip">
diff --git a/chrome/browser/resources/options/content_settings.js b/chrome/browser/resources/options/content_settings.js
index 81f111d..f481473 100644
--- a/chrome/browser/resources/options/content_settings.js
+++ b/chrome/browser/resources/options/content_settings.js
@@ -234,6 +234,14 @@
   }
 
   /**
+   * Shows/hides the whole Web MIDI settings.
+   * @param {bool} show Wether to show the whole Web MIDI settings.
+   */
+  ContentSettings.showExperimentalWebMIDISettings = function(show) {
+    $('experimental-web-midi-settings').hidden = !show;
+  }
+
+  /**
    * Updates the microphone/camera devices menu with the given entries.
    * @param {string} type The device type.
    * @param {Array} devices List of available devices.
diff --git a/chrome/browser/resources/options/content_settings_exceptions_area.html b/chrome/browser/resources/options/content_settings_exceptions_area.html
index 0f6127d..54e0271 100644
--- a/chrome/browser/resources/options/content_settings_exceptions_area.html
+++ b/chrome/browser/resources/options/content_settings_exceptions_area.html
@@ -107,6 +107,14 @@
     <div contentType="multiple-automatic-downloads">
       <list mode="normal"></list>
     </div>
+    <div contentType="midi-sysex">
+      <list mode="normal"></list>
+      <div>
+        <span class="otr-explanation" i18n-content="otr_exceptions_explanation">
+        </span>
+        <list mode="otr"></list>
+      </div>
+    </div>
   </div>
   <div class="action-area">
     <div class="hbox stretch">
diff --git a/chrome/browser/resources/options/content_settings_exceptions_area.js b/chrome/browser/resources/options/content_settings_exceptions_area.js
index fa013b7..8314ffa 100644
--- a/chrome/browser/resources/options/content_settings_exceptions_area.js
+++ b/chrome/browser/resources/options/content_settings_exceptions_area.js
@@ -106,6 +106,8 @@
 
       this.contentElement.appendChild(select);
       select.className = 'exception-setting';
+      select.setAttribute('aria-labelledby', 'exception-behavior-column');
+
       if (this.pattern)
         select.setAttribute('displaymode', 'edit');
 
diff --git a/chrome/browser/resources/options/language_options.js b/chrome/browser/resources/options/language_options.js
index c54c76f..8b3c7a6 100644
--- a/chrome/browser/resources/options/language_options.js
+++ b/chrome/browser/resources/options/language_options.js
@@ -333,6 +333,7 @@
         if (inputMethod.optionsPage) {
           var button = document.createElement('button');
           button.textContent = loadTimeData.getString('configure');
+          button.inputMethodId = inputMethod.id;
           button.onclick = function(optionsPage, e) {
             window.open(optionsPage);
           }.bind(this, inputMethod.optionsPage);
@@ -1075,6 +1076,11 @@
         if (!checkboxes[i].inputMethodId.match(/^_ext_ime_/))
           checkboxes[i].checked = (checkboxes[i].inputMethodId in dictionary);
       }
+      var configureButtons = inputMethodList.querySelectorAll('button');
+      for (var i = 0; i < configureButtons.length; i++) {
+        configureButtons[i].hidden =
+            !(configureButtons[i].inputMethodId in dictionary);
+      }
     },
 
     /**
diff --git a/chrome/browser/resources/options/manage_profile_overlay.js b/chrome/browser/resources/options/manage_profile_overlay.js
index f98f64a..f3eb886 100644
--- a/chrome/browser/resources/options/manage_profile_overlay.js
+++ b/chrome/browser/resources/options/manage_profile_overlay.js
@@ -58,9 +58,8 @@
         CreateProfileOverlay.cancelCreateProfile();
       };
 
-      if (loadTimeData.getBoolean('managedUsersEnabled')) {
-        $('create-profile-managed-container').hidden = false;
-      }
+      $('create-profile-managed-container').hidden =
+          !loadTimeData.getBoolean('managedUsersEnabled');
 
       $('manage-profile-cancel').onclick =
           $('delete-profile-cancel').onclick = function(event) {
@@ -68,6 +67,8 @@
       };
       $('delete-profile-ok').onclick = function(event) {
         OptionsPage.closeOverlay();
+        if (BrowserOptions.getCurrentProfile().isManaged)
+          return;
         chrome.send('deleteProfile', [self.profileInfo_.filePath]);
       };
       $('add-shortcut-button').onclick = function(event) {
@@ -171,6 +172,7 @@
     },
 
     /**
+     * Set an array of default icon URLs. These will be added to the grid that
      * the user will use to choose their profile icon.
      * @param {Array.<string>} iconURLs An array of icon URLs.
      * @private
@@ -302,6 +304,11 @@
      * @private
      */
     submitCreateProfile_: function() {
+      // This is visual polish: the UI to access this should be disabled for
+      // managed users, and the back end will prevent user creation anyway.
+      if (this.profileInfo_ && this.profileInfo_.isManaged)
+        return;
+
       this.hideErrorBubble_('create');
       CreateProfileOverlay.updateCreateInProgress(true);
 
@@ -365,6 +372,9 @@
      * @private
      */
     showDeleteDialog_: function(profileInfo) {
+      if (BrowserOptions.getCurrentProfile().isManaged)
+        return;
+
       ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
       $('manage-profile-overlay-create').hidden = true;
       $('manage-profile-overlay-manage').hidden = true;
@@ -423,6 +433,11 @@
     // not signed in.
     signedInEmail_: '',
 
+    /** @override */
+    canShowPage: function() {
+      return !BrowserOptions.getCurrentProfile().isManaged;
+    },
+
     /**
      * Configures the overlay to the "create user" mode.
      * @override
@@ -447,6 +462,8 @@
       $('create-profile-name-label').hidden = true;
       $('create-profile-name').hidden = true;
       $('create-profile-ok').disabled = true;
+
+      $('create-profile-managed').checked = false;
       $('create-profile-managed-signed-in').disabled = true;
       $('create-profile-managed-signed-in').hidden = true;
       $('create-profile-managed-not-signed-in').hidden = true;
@@ -545,6 +562,8 @@
     /**
      * Updates the signed-in or not-signed-in UI when in create mode. Called by
      * the handler in response to the 'requestCreateProfileUpdate' message.
+     * updateManagedUsersAllowed_ is expected to be called after this is, and
+     * will update additional UI elements.
      * @param {string} email The email address of the currently signed-in user.
      *     An empty string indicates that the user is not signed in.
      * @private
@@ -559,8 +578,6 @@
         $('create-profile-managed-signed-in-label').textContent =
             loadTimeData.getStringF(
                 'manageProfilesManagedSignedInLabel', email);
-      } else {
-        $('create-profile-managed').checked = false;
       }
     },
 
@@ -579,7 +596,6 @@
 
       $('create-profile-managed-not-signed-in-link').hidden = !allowed;
       if (!allowed) {
-        $('create-profile-managed').checked = false;
         $('create-profile-managed-indicator').setAttribute('controlled-by',
                                                            'policy');
       } else {
diff --git a/chrome/browser/resources/options/options.html b/chrome/browser/resources/options/options.html
index d78ee8a..7e394cd 100644
--- a/chrome/browser/resources/options/options.html
+++ b/chrome/browser/resources/options/options.html
@@ -71,6 +71,7 @@
 <script src="chrome://resources/js/cr/ui/bubble.js"></script>
 <script src="chrome://resources/js/cr/ui/bubble_button.js"></script>
 <script src="chrome://resources/js/cr/ui/focus_manager.js"></script>
+<script src="chrome://resources/js/cr/ui/focus_outline_manager.js"></script>
 <script src="chrome://resources/js/cr/ui/list_selection_model.js"></script>
 <script src="chrome://resources/js/cr/ui/list_selection_controller.js"></script>
 <script src="chrome://resources/js/cr/ui/list_single_selection_model.js">
diff --git a/chrome/browser/resources/options/options.js b/chrome/browser/resources/options/options.js
index fca1a26..e860fff 100644
--- a/chrome/browser/resources/options/options.js
+++ b/chrome/browser/resources/options/options.js
@@ -138,8 +138,7 @@
                                $('manage-languages')]);
   OptionsPage.registerOverlay(ManageProfileOverlay.getInstance(),
                               BrowserOptions.getInstance());
-  if (loadTimeData.getBoolean('managedUsersEnabled') &&
-      !cr.isChromeOS) {
+  if (loadTimeData.getBoolean('managedUsersEnabled') && !cr.isChromeOS) {
     OptionsPage.registerOverlay(ManagedUserCreateConfirmOverlay.getInstance(),
                                 BrowserOptions.getInstance());
     OptionsPage.registerOverlay(ManagedUserLearnMoreOverlay.getInstance(),
diff --git a/chrome/browser/resources/options/options_page.css b/chrome/browser/resources/options/options_page.css
index c3a1926..936ee37 100644
--- a/chrome/browser/resources/options/options_page.css
+++ b/chrome/browser/resources/options/options_page.css
@@ -436,6 +436,12 @@
   background-color: rgb(187, 206, 233);
 }
 
+html:not(.focus-outline-visible)
+:enabled:focus:-webkit-any(input[type='checkbox'], input[type='radio']) {
+  /* Cancel border-color for :focus specified in widgets.css. */
+  border-color: rgba(0, 0, 0, 0.25);
+}
+
 html:not([hasFlashPlugin]) .flash-plugin-area,
 /* If the Flash plug-in supports the NPP_ClearSiteData API, we don't need to
  * show the link to the Flash storage settings manager:
diff --git a/chrome/browser/resources/options/options_page.js b/chrome/browser/resources/options/options_page.js
index 0db5148..025eba0 100644
--- a/chrome/browser/resources/options/options_page.js
+++ b/chrome/browser/resources/options/options_page.js
@@ -162,9 +162,12 @@
     // Update tab title.
     this.setTitle_(targetPage.title);
 
-    // Update focus if any other control was focused before.
-    if (document.activeElement != document.body)
+    // Update focus if any other control was focused on the previous page,
+    // or the previous page is not known.
+    if (document.activeElement != document.body &&
+        (!rootPage || rootPage.pageDiv.contains(document.activeElement))) {
       targetPage.focus();
+    }
 
     // Notify pages if they were shown.
     for (var i = 0; i < allPageNames.length; ++i) {
@@ -273,8 +276,11 @@
     // Update tab title.
     this.setTitle_(overlay.title);
 
-    // Change focus to the overlay if any other control was focused before.
-    if (document.activeElement != document.body)
+    // Change focus to the overlay if any other control was focused by keyboard
+    // before.
+    if (document.activeElement != document.body &&
+        document.documentElement.classList.contains(
+            cr.ui.FocusOutlineManager.CLASS_NAME))
       overlay.focus();
 
     $('searchBox').setAttribute('aria-hidden', true);
@@ -638,7 +644,7 @@
   OptionsPage.initialize = function() {
     chrome.send('coreOptionsInitialize');
     uber.onContentFrameLoaded();
-
+    cr.ui.FocusOutlineManager.forDocument(document);
     document.addEventListener('scroll', this.handleScroll_.bind(this));
 
     // Trigger the scroll handler manually to set the initial state.
@@ -883,6 +889,9 @@
         container.classList.remove('transparent');
         this.onVisibilityChanged_();
       } else {
+        // Kick change events for text fields.
+        if (pageDiv.contains(document.activeElement))
+          document.activeElement.blur();
         var self = this;
         // TODO: Use an event delegate to avoid having to subscribe and
         // unsubscribe for webkitTransitionEnd events.
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 bbea9b0..848c4df 100644
--- a/chrome/browser/resources/options/search_engine_manager_engine_list.js
+++ b/chrome/browser/resources/options/search_engine_manager_engine_list.js
@@ -135,9 +135,11 @@
           makeDefaultButtonEl.onclick = function(e) {
             chrome.send('managerSetDefaultSearchEngine', [engine.modelIndex]);
           };
-          // Don't select the row when clicking the button.
           makeDefaultButtonEl.onmousedown = function(e) {
+            // Don't select the row when clicking the button.
             e.stopPropagation();
+            // Don't focus on the button.
+            e.preventDefault();
           };
           urlWithButtonEl.appendChild(makeDefaultButtonEl);
         }
diff --git a/chrome/browser/resources/options/search_page.js b/chrome/browser/resources/options/search_page.js
index 234399d..21602da 100644
--- a/chrome/browser/resources/options/search_page.js
+++ b/chrome/browser/resources/options/search_page.js
@@ -194,8 +194,10 @@
       if (active) {
         var hash = location.hash;
         if (hash) {
-          this.searchField.value =
-              decodeURIComponent(hash.slice(1).replace(/\+/g, ' '));
+          if (this.searchField != document.activeElement) {
+            this.searchField.value =
+                decodeURIComponent(hash.slice(1).replace(/\+/g, ' '));
+          }
         } else if (!this.searchField.value) {
           // This should only happen if the user goes directly to
           // chrome://settings-frame/search
diff --git a/chrome/browser/resources/policy.css b/chrome/browser/resources/policy.css
index 37ffa55..530b8be 100644
--- a/chrome/browser/resources/policy.css
+++ b/chrome/browser/resources/policy.css
@@ -169,3 +169,10 @@
   white-space: pre;
   word-wrap: break-word;
 }
+
+html:not(.focus-outline-visible)
+:enabled:focus:-webkit-any(input[type='checkbox'],
+                           input[type='radio'], button) {
+  /* Cancel border-color for :focus specified in widgets.css. */
+  border-color: rgba(0, 0, 0, 0.25);
+}
diff --git a/chrome/browser/resources/policy.html b/chrome/browser/resources/policy.html
index 3718f4d..0416151 100644
--- a/chrome/browser/resources/policy.html
+++ b/chrome/browser/resources/policy.html
@@ -11,6 +11,7 @@
 
 <script src="chrome://resources/js/cr.js"></script>
 <script src="chrome://resources/js/cr/ui.js"></script>
+<script src="chrome://resources/js/cr/ui/focus_outline_manager.js"></script>
 <script src="chrome://resources/js/load_time_data.js"></script>
 <script src="chrome://resources/js/util.js"></script>
 <script src="chrome://policy/uber_utils.js"></script>
diff --git a/chrome/browser/resources/policy.js b/chrome/browser/resources/policy.js
index 5f38e21..fd58297 100644
--- a/chrome/browser/resources/policy.js
+++ b/chrome/browser/resources/policy.js
@@ -408,6 +408,7 @@
      */
     initialize: function() {
       uber.onContentFrameLoaded();
+      cr.ui.FocusOutlineManager.forDocument(document);
 
       this.mainSection = $('main-section');
       this.policyTables = {};
diff --git a/chrome/browser/resources/print_preview/print_preview.css b/chrome/browser/resources/print_preview/print_preview.css
index 9369e1a..df354a6 100644
--- a/chrome/browser/resources/print_preview/print_preview.css
+++ b/chrome/browser/resources/print_preview/print_preview.css
@@ -297,7 +297,13 @@
 }
 
 html:not(.focus-outline-visible)
-:enabled:focus:-webkit-any(input[type='checkbox'],input[type='radio'],button) {
+:enabled:focus:-webkit-any(input[type='checkbox'],
+                           input[type='radio'],
+                           button):not(.link-button) {
   /* Cancel border-color for :focus specified in widgets.css. */
   border-color: rgba(0,0,0,0.25);
 }
+
+html:not(.focus-outline-visible) button:focus.link-button {
+  outline: none;
+}
diff --git a/chrome/browser/resources/sync_setup_overlay.html b/chrome/browser/resources/sync_setup_overlay.html
index fba395b..b1e8006 100644
--- a/chrome/browser/resources/sync_setup_overlay.html
+++ b/chrome/browser/resources/sync_setup_overlay.html
@@ -12,9 +12,10 @@
       <div>
         <div class="action-area">
           <div class="action-area-link-container">
-            <a id="customize-link" href="#" i18n-content="customizeLinkLabel"
-                class="reset-opaque reset-shown">
-            </a>
+            <button id="customize-link" type="button"
+                i18n-content="customizeLinkLabel"
+                class="link-button">
+            </button>
           </div>
           <div id="confirm-everything-throbber" class="throbber"></div>
           <div class="button-strip">
@@ -188,9 +189,10 @@
         </div>
         <div class="action-area">
           <div class="action-area-link-container">
-            <a id="use-default-link" href="#" i18n-content="useDefaultSettings"
-                class="reset-opaque reset-shown">
-            </a>
+            <button id="use-default-link" type="button"
+                i18n-content="useDefaultSettings"
+                class="link-button">
+            </button>
           </div>
           <div id="choose-datatypes-throbber" class="throbber"></div>
           <div class="button-strip">
diff --git a/chrome/browser/resources/sync_setup_overlay.js b/chrome/browser/resources/sync_setup_overlay.js
index 80ca474..74d4767 100644
--- a/chrome/browser/resources/sync_setup_overlay.js
+++ b/chrome/browser/resources/sync_setup_overlay.js
@@ -81,9 +81,6 @@
         chrome.send('SyncSetupStopSyncing');
         self.closeOverlay_();
       };
-      $('sync-configure-content').onclick = function() {
-        self.onConfigurationClicked_();
-      };
     },
 
     showOverlay_: function() {
@@ -113,29 +110,6 @@
       $('sync-custom-passphrase').hidden = !visible;
     },
 
-    onConfigurationClicked_: function() {
-      // Avoid to foucus on labeled checkboxes and radio buttons.
-      // We don't want to show focus rings for them when labels are clicked.
-      if (event.target.tagName == 'INPUT')
-        return;
-      for (var node = event.target; node; node = node.parentNode) {
-        if (node.tagName != 'LABEL')
-          continue;
-        var control = node.control;
-        if (!control || control.disabled)
-          return;
-        if (control.type == 'checkbox') {
-          control.checked = !control.checked;
-          event.preventDefault();
-        } else if (control.type == 'radio') {
-          control.checked = !control.checked;
-          this.onEncryptionRadioChanged_();
-          event.preventDefault();
-        }
-        return;
-      }
-    },
-
     /**
      * Sets the checked state of the individual sync data type checkboxes in the
      * advanced sync settings dialog.
@@ -277,7 +251,9 @@
       // Don't allow the user to tweak the settings once we send the
       // configuration to the backend.
       this.setInputElementsDisabledState_(true);
-      this.animateDisableLink_($('use-default-link'), true, null);
+      $('use-default-link').hidden = true;
+      $('use-default-link').disabled = true;
+      $('use-default-link').onclick = null;
 
       // These values need to be kept in sync with where they are read in
       // SyncSetupFlow::GetDataTypeChoiceData().
@@ -320,40 +296,16 @@
         configureElements[i].disabled = disabled;
       $('sync-select-datatypes').disabled = disabled;
 
-      var self = this;
-      this.animateDisableLink_($('customize-link'), disabled, function() {
-        self.showCustomizePage_(null, DataTypeSelection.SYNC_EVERYTHING);
+      $('customize-link').hidden = disabled;
+      $('customize-link').disabled = disabled;
+      $('customize-link').onclick = (disabled ? null : function() {
+        SyncSetupOverlay.showCustomizePage(null,
+                                           DataTypeSelection.SYNC_EVERYTHING);
+        return false;
       });
     },
 
     /**
-     * Animate a link being enabled/disabled. The link is hidden by animating
-     * its opacity, but to ensure the user doesn't click it during that time,
-     * its onclick handler is changed to null as well.
-     * @param {HTMLElement} elt The anchor element to enable/disable.
-     * @param {boolean} disabled True if the link should be disabled.
-     * @param {function} enabledFunction The onclick handler when the link is
-     *     enabled.
-     * @private
-     */
-    animateDisableLink_: function(elt, disabled, enabledFunction) {
-      if (disabled) {
-        elt.classList.add('transparent');
-        elt.onclick = null;
-        elt.addEventListener('webkitTransitionEnd', function f(e) {
-          if (e.propertyName != 'opacity')
-            return;
-          elt.removeEventListener('webkitTransitionEnd', f);
-          elt.classList.remove('transparent');
-          elt.hidden = true;
-        });
-      } else {
-        elt.hidden = false;
-        elt.onclick = enabledFunction;
-      }
-    },
-
-    /**
      * Shows or hides the sync data type checkboxes in the advanced sync
      * settings dialog. Also initializes |dataTypeBoxes_| with their values, and
      * makes their onclick handlers update |dataTypeBoxes_|.
@@ -564,6 +516,8 @@
 
       // Hide the "use default settings" link.
       $('use-default-link').hidden = true;
+      $('use-default-link').disabled = true;
+      $('use-default-link').onclick = null;
     },
 
     /**
@@ -578,6 +532,8 @@
       // Once we require a passphrase, we prevent the user from returning to
       // the Sync Everything pane.
       $('use-default-link').hidden = true;
+      $('use-default-link').disabled = true;
+      $('use-default-link').onclick = null;
       $('sync-custom-passphrase-container').hidden = true;
       $('sync-existing-passphrase-container').hidden = false;
 
@@ -635,10 +591,12 @@
       } else {
         // We only show the 'Use Default' link if we're not prompting for an
         // existing passphrase.
-        var self = this;
-        this.animateDisableLink_($('use-default-link'), false, function() {
-          self.showSyncEverythingPage_();
-        });
+        $('use-default-link').hidden = false;
+        $('use-default-link').disabled = false;
+        $('use-default-link').onclick = function() {
+          SyncSetupOverlay.showSyncEverythingPage();
+          return false;
+        };
       }
     },
 
@@ -795,6 +753,14 @@
     SyncSetupOverlay.getInstance().showSyncSetupPage_(page, args);
   };
 
+  SyncSetupOverlay.showCustomizePage = function(args, index) {
+    SyncSetupOverlay.getInstance().showCustomizePage_(args, index);
+  };
+
+  SyncSetupOverlay.showSyncEverythingPage = function() {
+    SyncSetupOverlay.getInstance().showSyncEverythingPage_();
+  };
+
   SyncSetupOverlay.showStopSyncingUI = function() {
     SyncSetupOverlay.getInstance().showStopSyncingUI_();
   };
diff --git a/chrome/browser/resources/uber/uber.js b/chrome/browser/resources/uber/uber.js
index e0e225b..44609c0 100644
--- a/chrome/browser/resources/uber/uber.js
+++ b/chrome/browser/resources/uber/uber.js
@@ -44,6 +44,8 @@
         params.path.indexOf('search') != 0) {
       backgroundNavigation();
     }
+
+    ensureNonSelectedFrameContainersAreHidden();
   }
 
   /**
@@ -267,8 +269,23 @@
     if (lastSelected === container)
       return;
 
-    if (lastSelected)
+    if (lastSelected) {
       lastSelected.classList.remove('selected');
+      // Setting aria-hidden hides the container from assistive technology
+      // immediately. The 'hidden' attribute is set after the transition
+      // finishes - that ensures it's not possible to accidentally focus
+      // an element in an unselected container.
+      lastSelected.setAttribute('aria-hidden', 'true');
+    }
+
+    // Containers that aren't selected have to be hidden so that their
+    // content isn't focusable.
+    container.hidden = false;
+    container.setAttribute('aria-hidden', 'false');
+
+    // Trigger a layout after making it visible and before setting
+    // the class to 'selected', so that it animates in.
+    container.offsetTop;
     container.classList.add('selected');
 
     setContentChanging(true);
@@ -333,6 +350,29 @@
     uber.invokeMethodOnWindow(iframe.contentWindow, 'mouseWheel', params);
   }
 
+  /**
+   * Make sure that iframe containers that are not selected are
+   * hidden, so that elements in those frames aren't part of the
+   * focus order. Containers that are unselected later get hidden
+   * when the transition ends. We also set the aria-hidden attribute
+   * because that hides the container from assistive technology
+   * immediately, rather than only after the transition ends.
+   */
+  function ensureNonSelectedFrameContainersAreHidden() {
+    var containers = document.querySelectorAll('.iframe-container');
+    for (var i = 0; i < containers.length; i++) {
+      var container = containers[i];
+      if (!container.classList.contains('selected')) {
+        container.hidden = true;
+        container.setAttribute('aria-hidden', 'true');
+      }
+      container.addEventListener('webkitTransitionEnd', function(event) {
+        if (!event.target.classList.contains('selected'))
+          event.target.hidden = true;
+      });
+    }
+  }
+
   return {
     onLoad: onLoad,
     onPopHistoryState: onPopHistoryState
diff --git a/chrome/browser/resources/user_chooser/desktop_user_pod_template.html b/chrome/browser/resources/user_chooser/desktop_user_pod_template.html
deleted file mode 100644
index 73a13cd..0000000
--- a/chrome/browser/resources/user_chooser/desktop_user_pod_template.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<div id="user-pod-template" class="pod need-password" hidden>
-  <div class="main-pane">
-    <div class="signed-in-indicator" i18n-content="signedIn" hidden></div>
-    <img class="user-image" alt="">
-    <div class="name"></div>
-    <input type="password" class="password"
-           i18n-values="placeholder:passwordHint">
-    <button class="signin-button" i18n-content="signinButton"></button>
-  </div>
-  <div class="action-box-area">
-    <div class="custom-appearance action-box-button"></div>
-  </div>
-  <div class="user-type-icon-area" hidden>
-    <div class="custom-appearance user-type-icon-image"></div>
-  </div>
-  <div class="action-box-menu">
-    <div class="action-box-menu-title">
-      <span class="action-box-menu-title-name"></span>
-      <span class="action-box-menu-title-email"></span>
-    </div>
-    <div class="action-box-menu-remove">
-      <span class="action-box-menu-remove-command"/>
-    </div>
-  </div>
-</div>
diff --git a/chrome/browser/resources/user_chooser/user_chooser.html b/chrome/browser/resources/user_chooser/user_chooser.html
index ccbea9d..024693a 100644
--- a/chrome/browser/resources/user_chooser/user_chooser.html
+++ b/chrome/browser/resources/user_chooser/user_chooser.html
@@ -2,14 +2,14 @@
 <html i18n-values="dir:textdirection;screen:screenType;build:buildType">
 <head>
 <title i18n-content="title"></title>
+<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
+<link rel="stylesheet" href="../chromeos/login/bubble.css">
+<link rel="stylesheet" href="../chromeos/login/screen_account_picker.css">
+<link rel="stylesheet" href="../chromeos/login/screen_container.css">
 <link rel="stylesheet" href="../chromeos/login/oobe.css">
 <link rel="stylesheet" href="../chromeos/login/user_pod_row.css">
 <!-- desktop user chooser specific overrides -->
 <link rel="stylesheet" href="control_bar.css">
-<link rel="stylesheet" href="../chromeos/login/bubble.css">
-<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
-<link rel="stylesheet" href="../chromeos/login/screen_account_picker.css">
-<link rel="stylesheet" href="../chromeos/login/screen_container.css">
 <!-- framework imports -->
 <!-- as per chrome/browser/resources/chromeos/login/login_resources.html -->
 <script src="chrome://resources/js/cr.js"></script>
@@ -44,7 +44,7 @@
   </div>
   <div id="bubble" class="bubble faded" hidden></div>
   <include src="control_bar.html">
-  <include src="desktop_user_pod_template.html">
+  <include src="../chromeos/login/user_pod_template.html">
   <script src="chrome://resources/js/i18n_template2.js"></script>
 </body>
 </html>
diff --git a/chrome/browser/rlz/rlz_extension_apitest.cc b/chrome/browser/rlz/rlz_extension_apitest.cc
index bb06299..3cf21c2 100644
--- a/chrome/browser/rlz/rlz_extension_apitest.cc
+++ b/chrome/browser/rlz/rlz_extension_apitest.cc
@@ -9,8 +9,8 @@
 #include "chrome/browser/extensions/extension_function.h"
 #include "chrome/browser/extensions/extension_function_dispatcher.h"
 #include "chrome/browser/rlz/rlz_extension_api.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
+#include "extensions/common/switches.h"
 #include "net/dns/mock_host_resolver.h"
 #include "rlz/lib/rlz_lib.h"
 
@@ -63,7 +63,7 @@
   net::ScopedDefaultHostResolverProc scoper(resolver);
 
   CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
+      extensions::switches::kEnableExperimentalExtensionApis);
 
   // Before running the tests, clear the state of the RLZ products used.
   rlz_lib::AccessPoint access_points[] = {
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor.cc b/chrome/browser/safe_browsing/browser_feature_extractor.cc
index ed8408d..29e7033 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor.cc
@@ -35,6 +35,8 @@
 
 namespace safe_browsing {
 
+const int BrowserFeatureExtractor::kMaxMalwareIPPerRequest = 5;
+
 BrowseInfo::BrowseInfo() : http_status_code(0) {}
 
 BrowseInfo::~BrowseInfo() {}
@@ -234,13 +236,20 @@
   DCHECK(request);
   DCHECK(info);
   DCHECK_EQ(0U, request->url().find("http:"));
-  // get the IPs and hosts that match the malware blacklisted IP list.
+  // get the IPs and urls that match the malware blacklisted IP list.
   if (service_) {
-    for (IPHostMap::const_iterator it = info->ips.begin();
+    int matched_bad_ips = 0;
+    for (IPUrlMap::const_iterator it = info->ips.begin();
          it != info->ips.end(); ++it) {
       if (service_->IsBadIpAddress(it->first)) {
         AddMalwareFeature(features::kBadIpFetch + it->first,
                           it->second, 1.0, request);
+        ++matched_bad_ips;
+        // Limit the number of matched bad IPs in one request to control
+        // the request's size
+        if (matched_bad_ips >= kMaxMalwareIPPerRequest) {
+          return;
+        }
       }
     }
   }
@@ -250,7 +259,7 @@
     const BrowseInfo& info,
     ClientPhishingRequest* request) {
   if (service_) {
-    for (IPHostMap::const_iterator it = info.ips.begin();
+    for (IPUrlMap::const_iterator it = info.ips.begin();
          it != info.ips.end(); ++it) {
       if (service_->IsBadIpAddress(it->first)) {
         AddFeature(features::kBadIpFetch + it->first, 1.0, request);
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor.h b/chrome/browser/safe_browsing/browser_feature_extractor.h
index 086e9bb..9a6fbf5 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor.h
+++ b/chrome/browser/safe_browsing/browser_feature_extractor.h
@@ -39,12 +39,12 @@
 class ClientPhishingRequest;
 class ClientSideDetectionService;
 
-typedef std::map<std::string, std::set<std::string> > IPHostMap;
+typedef std::map<std::string, std::set<std::string> > IPUrlMap;
 
 struct BrowseInfo {
   // List of IPv4 and IPv6 addresses from which content was requested
   // together with the hosts on it, while browsing to the |url|.
-  IPHostMap ips;
+  IPUrlMap ips;
 
   // If a SafeBrowsing interstitial was shown for the current URL
   // this will contain the UnsafeResource struct for that URL.
@@ -172,6 +172,9 @@
   // the history callback hasn't been invoked yet).
   PendingQueriesMap pending_queries_;
 
+  // Max number of malware IPs can be sent in one malware request
+  static const int kMaxMalwareIPPerRequest;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserFeatureExtractor);
 };
 
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
index c54c71c..163268d 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
@@ -480,10 +480,10 @@
                     GURL("https://bankofamerica.com"),
                     content::PAGE_TRANSITION_GENERATED);
 
-  std::set<std::string> hosts;
-  hosts.insert("test.com");
-  browse_info_->ips.insert(std::make_pair("193.5.163.8", hosts));
-  browse_info_->ips.insert(std::make_pair("23.94.78.1", hosts));
+  std::set<std::string> urls;
+  urls.insert("http://test.com");
+  browse_info_->ips.insert(std::make_pair("193.5.163.8", urls));
+  browse_info_->ips.insert(std::make_pair("23.94.78.1", urls));
   EXPECT_CALL(*service_, IsBadIpAddress("193.5.163.8")).WillOnce(Return(true));
   EXPECT_CALL(*service_, IsBadIpAddress("23.94.78.1")).WillOnce(Return(false));
 
@@ -544,14 +544,14 @@
   ClientMalwareRequest request;
   request.set_url("http://www.foo.com/");
 
-  std::set<std::string> bad_hosts;
-  bad_hosts.insert("bad.com");
-  bad_hosts.insert("evil.com");
-  browse_info_->ips.insert(std::make_pair("193.5.163.8", bad_hosts));
-  browse_info_->ips.insert(std::make_pair("92.92.92.92", bad_hosts));
-  std::set<std::string> good_hosts;
-  good_hosts.insert("ok.com");
-  browse_info_->ips.insert(std::make_pair("23.94.78.1", good_hosts));
+  std::set<std::string> bad_urls;
+  bad_urls.insert("http://bad.com");
+  bad_urls.insert("http://evil.com");
+  browse_info_->ips.insert(std::make_pair("193.5.163.8", bad_urls));
+  browse_info_->ips.insert(std::make_pair("92.92.92.92", bad_urls));
+  std::set<std::string> good_urls;
+  good_urls.insert("http://ok.com");
+  browse_info_->ips.insert(std::make_pair("23.94.78.1", good_urls));
   EXPECT_CALL(*service_, IsBadIpAddress("193.5.163.8")).WillOnce(Return(true));
   EXPECT_CALL(*service_, IsBadIpAddress("92.92.92.92")).WillOnce(Return(true));
   EXPECT_CALL(*service_, IsBadIpAddress("23.94.78.1")).WillOnce(Return(false));
@@ -564,16 +564,45 @@
   std::string feature_name = base::StringPrintf("%s%s", features::kBadIpFetch,
                                                 "193.5.163.8");
   EXPECT_TRUE(features.count(feature_name));
-  std::set<std::string> hosts = features[feature_name];
-  EXPECT_EQ(2U, hosts.size());
-  EXPECT_TRUE(hosts.find("bad.com") != hosts.end());
-  EXPECT_TRUE(hosts.find("evil.com") != hosts.end());
+  std::set<std::string> urls = features[feature_name];
+  EXPECT_EQ(2U, urls.size());
+  EXPECT_TRUE(urls.find("http://bad.com") != urls.end());
+  EXPECT_TRUE(urls.find("http://evil.com") != urls.end());
   feature_name = base::StringPrintf("%s%s", features::kBadIpFetch,
                                     "92.92.92.92");
   EXPECT_TRUE(features.count(feature_name));
-  hosts = features[feature_name];
-  EXPECT_EQ(2U, hosts.size());
-  EXPECT_TRUE(hosts.find("bad.com") != hosts.end());
-  EXPECT_TRUE(hosts.find("evil.com") != hosts.end());
+  urls = features[feature_name];
+  EXPECT_EQ(2U, urls.size());
+  EXPECT_TRUE(urls.find("http://bad.com") != urls.end());
+  EXPECT_TRUE(urls.find("http://evil.com") != urls.end());
 }
+
+TEST_F(BrowserFeatureExtractorTest, MalwareFeatures_ExceedLimit) {
+  ClientMalwareRequest request;
+  request.set_url("http://www.foo.com/");
+
+  std::set<std::string> bad_urls;
+  bad_urls.insert("http://bad.com");
+  std::vector<std::string> ips;
+  for (int i = 0; i < 7; ++i) {  // Add 7 ips
+    std::string ip = base::StringPrintf("%d.%d.%d.%d", i, i, i, i);
+    ips.push_back(ip);
+    browse_info_->ips.insert(std::make_pair(ip, bad_urls));
+  }
+
+  // First ip is good, then check the next 5 bad ips.
+  // Not check the 7th as reached limit.
+  EXPECT_CALL(*service_, IsBadIpAddress(ips[0])).WillOnce(Return(false));
+  for (int i = 1; i < 6; ++i) {
+    EXPECT_CALL(*service_, IsBadIpAddress(ips[i])).WillOnce(Return(true));
+  }
+
+  ExtractMalwareFeatures(&request);
+  std::map<std::string, std::set<std::string> > features;
+  GetMalwareFeatureMap(request, &features);
+
+  // Only keep 5 ips.
+  EXPECT_EQ(5U, features.size());
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/chrome/browser/safe_browsing/client_side_detection_host.cc
index 1f67d5c..b87a87d 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host.cc
@@ -44,7 +44,7 @@
 
 namespace safe_browsing {
 
-const int ClientSideDetectionHost::kMaxHostsPerIP = 20;
+const int ClientSideDetectionHost::kMaxUrlsPerIP = 20;
 const int ClientSideDetectionHost::kMaxIPsPerBrowse = 200;
 
 namespace {
@@ -493,20 +493,20 @@
   }
 }
 
-void ClientSideDetectionHost::UpdateIPHostMap(const std::string& ip,
-                                              const std::string& host) {
-  if (ip.empty() || host.empty())
+void ClientSideDetectionHost::UpdateIPUrlMap(const std::string& ip,
+                                             const std::string& url) {
+  if (ip.empty() || url.empty())
     return;
 
-  IPHostMap::iterator it = browse_info_->ips.find(ip);
+  IPUrlMap::iterator it = browse_info_->ips.find(ip);
   if (it == browse_info_->ips.end()) {
     if (int(browse_info_->ips.size()) < kMaxIPsPerBrowse) {
-      std::set<std::string> hosts;
-      hosts.insert(host);
-      browse_info_->ips.insert(make_pair(ip, hosts));
+      std::set<std::string> urls;
+      urls.insert(url);
+      browse_info_->ips.insert(make_pair(ip, urls));
     }
-  } else if (int(it->second.size()) < kMaxHostsPerIP) {
-    it->second.insert(host);
+  } else if (int(it->second.size()) < kMaxUrlsPerIP) {
+    it->second.insert(url);
   }
 }
 
@@ -520,8 +520,10 @@
       details).ptr();
   if (req && browse_info_.get() && malware_report_enabled_ &&
       !MalwareKillSwitchIsOn()) {
-    UpdateIPHostMap(req->socket_address.host() /* ip */,
-                    req->url.host()  /* url host */);
+    if (req->url.is_valid()) {
+      UpdateIPUrlMap(req->socket_address.host() /* ip */,
+                     req->url.spec()  /* url */);
+    }
   }
 }
 
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.h b/chrome/browser/safe_browsing/client_side_detection_host.h
index 7f3e93e..9543652 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.h
+++ b/chrome/browser/safe_browsing/client_side_detection_host.h
@@ -79,7 +79,7 @@
   void MalwareFeatureExtractionDone(scoped_ptr<ClientMalwareRequest> request);
 
   // Update the entries in browse_info_->ips map.
-  void UpdateIPHostMap(const std::string& ip, const std::string& host);
+  void UpdateIPUrlMap(const std::string& ip, const std::string& url);
 
   // From NotificationObserver.  Called when a notification comes in.  This
   // method is called in the UI thread.
@@ -131,8 +131,8 @@
 
   // Max number of ips we save for each browse
   static const int kMaxIPsPerBrowse;
-  // Max number of hosts we report for each malware IP.
-  static const int kMaxHostsPerIP;
+  // Max number of urls we report for each malware IP.
+  static const int kMaxUrlsPerIP;
 
   base::WeakPtrFactory<ClientSideDetectionHost> weak_factory_;
 
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index 432aea5..8fa8da6 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -227,8 +227,8 @@
     csd_host_->OnPhishingDetectionDone(verdict_str);
   }
 
-  void UpdateIPHostMap(const std::string& ip, const std::string& host) {
-    csd_host_->UpdateIPHostMap(ip, host);
+  void UpdateIPUrlMap(const std::string& ip, const std::string& host) {
+    csd_host_->UpdateIPUrlMap(ip, host);
   }
 
   BrowseInfo* GetBrowseInfo() {
@@ -582,57 +582,57 @@
   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
 }
 
-TEST_F(ClientSideDetectionHostTest, UpdateIPHostMap) {
+TEST_F(ClientSideDetectionHostTest, UpdateIPUrlMap) {
   BrowseInfo* browse_info = GetBrowseInfo();
 
   // Empty IP or host are skipped
-  UpdateIPHostMap("250.10.10.10", std::string());
+  UpdateIPUrlMap("250.10.10.10", std::string());
   ASSERT_EQ(0U, browse_info->ips.size());
-  UpdateIPHostMap(std::string(), "google.com/");
+  UpdateIPUrlMap(std::string(), "http://google.com/a");
   ASSERT_EQ(0U, browse_info->ips.size());
-  UpdateIPHostMap(std::string(), std::string());
+  UpdateIPUrlMap(std::string(), std::string());
   ASSERT_EQ(0U, browse_info->ips.size());
 
-  std::set<std::string> expected_hosts;
+  std::set<std::string> expected_urls;
   for (int i = 0; i < 20; i++) {
-    std::string host = base::StringPrintf("%d.com/", i);
-    expected_hosts.insert(host);
-    UpdateIPHostMap("250.10.10.10", host);
+    std::string url = base::StringPrintf("http://%d.com/", i);
+    expected_urls.insert(url);
+    UpdateIPUrlMap("250.10.10.10", url);
   }
   ASSERT_EQ(1U, browse_info->ips.size());
   ASSERT_EQ(20U, browse_info->ips["250.10.10.10"].size());
-  EXPECT_EQ(expected_hosts, browse_info->ips["250.10.10.10"]);
+  EXPECT_EQ(expected_urls, browse_info->ips["250.10.10.10"]);
 
-  // Add more hosts for this ip, it exceeds max limit and won't be added
-  UpdateIPHostMap("250.10.10.10", "21.com/");
+  // Add more urls for this ip, it exceeds max limit and won't be added
+  UpdateIPUrlMap("250.10.10.10", "http://21.com/");
   ASSERT_EQ(1U, browse_info->ips.size());
   ASSERT_EQ(20U, browse_info->ips["250.10.10.10"].size());
-  EXPECT_EQ(expected_hosts, browse_info->ips["250.10.10.10"]);
+  EXPECT_EQ(expected_urls, browse_info->ips["250.10.10.10"]);
 
   // Add 199 more IPs
   for (int i = 0; i < 199; i++) {
     std::string ip = base::StringPrintf("%d.%d.%d.256", i, i, i);
-    expected_hosts.clear();
-    expected_hosts.insert("test.com/");
-    UpdateIPHostMap(ip, "test.com/");
+    expected_urls.clear();
+    expected_urls.insert("test.com/");
+    UpdateIPUrlMap(ip, "test.com/");
     ASSERT_EQ(1U, browse_info->ips[ip].size());
-    EXPECT_EQ(expected_hosts, browse_info->ips[ip]);
+    EXPECT_EQ(expected_urls, browse_info->ips[ip]);
   }
   ASSERT_EQ(200U, browse_info->ips.size());
 
   // Exceeding max ip limit 200, these won't be added
-  UpdateIPHostMap("250.250.250.250", "goo.com/");
-  UpdateIPHostMap("250.250.250.250", "bar.com/");
-  UpdateIPHostMap("250.250.0.250", "foo.com/");
+  UpdateIPUrlMap("250.250.250.250", "goo.com/");
+  UpdateIPUrlMap("250.250.250.250", "bar.com/");
+  UpdateIPUrlMap("250.250.0.250", "foo.com/");
   ASSERT_EQ(200U, browse_info->ips.size());
 
-  // Add host to existing IPs succeed
-  UpdateIPHostMap("100.100.100.256", "more.com/");
+  // Add url to existing IPs succeed
+  UpdateIPUrlMap("100.100.100.256", "more.com/");
   ASSERT_EQ(2U, browse_info->ips["100.100.100.256"].size());
-  expected_hosts.clear();
-  expected_hosts.insert("test.com/");
-  expected_hosts.insert("more.com/");
-  EXPECT_EQ(expected_hosts, browse_info->ips["100.100.100.256"]);
+  expected_urls.clear();
+  expected_urls.insert("test.com/");
+  expected_urls.insert("more.com/");
+  EXPECT_EQ(expected_urls, browse_info->ips["100.100.100.256"]);
 }
 
 TEST_F(ClientSideDetectionHostTest,
diff --git a/chrome/browser/safe_browsing/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection_service.cc
index 6756bbf..473dc53 100644
--- a/chrome/browser/safe_browsing/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection_service.cc
@@ -668,6 +668,8 @@
     fetcher_->SetRequestContext(service_->request_context_getter_.get());
     fetcher_->SetUploadData("application/octet-stream",
                             client_download_request_data_);
+    UMA_HISTOGRAM_COUNTS("SBClientDownload.DownloadRequestPayloadSize",
+                         client_download_request_data_.size());
     fetcher_->Start();
   }
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
index 485046f..efb1153 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -8,6 +8,7 @@
 // they work.
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -25,6 +26,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/interstitial_page.h"
 #include "content/public/browser/navigation_controller.h"
@@ -35,10 +37,6 @@
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_utils.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::BrowserThread;
 using content::InterstitialPage;
 using content::NavigationController;
@@ -622,7 +620,7 @@
 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, MalwareDontProceed) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -673,7 +671,7 @@
                        MalwareIframeDontProceed) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -735,7 +733,7 @@
 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, ProceedDisabled) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -768,7 +766,7 @@
 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, ReportingDisabled) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -801,7 +799,7 @@
 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, PhishingDontProceed) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index d3763b0..196c8a3 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -98,6 +98,8 @@
       profile, FaviconSource::FAVICON));
   content::URLDataSource::Add(profile, new LocalNtpSource(profile));
   content::URLDataSource::Add(profile, new MostVisitedIframeSource());
+  registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
+                 content::Source<Profile>(profile_));
 }
 
 InstantService::~InstantService() {
@@ -242,6 +244,16 @@
       break;
     }
 #endif  // defined(ENABLE_THEMES)
+    case chrome::NOTIFICATION_PROFILE_DESTROYED: {
+      // Last chance to delete InstantNTP contents. We generally delete
+      // preloaded InstantNTP when the last BrowserInstantController object is
+      // destroyed. When the browser shutdown happens without closing browsers,
+      // there is a race condition between BrowserInstantController destruction
+      // and Profile destruction.
+      if (GetNTPContents())
+        ntp_prerenderer_.DeleteNTPContents();
+      break;
+    }
     default:
       NOTREACHED() << "Unexpected notification type in InstantService.";
   }
diff --git a/chrome/browser/search/instant_service_factory.cc b/chrome/browser/search/instant_service_factory.cc
index 7e82b61..d586aa4 100644
--- a/chrome/browser/search/instant_service_factory.cc
+++ b/chrome/browser/search/instant_service_factory.cc
@@ -7,12 +7,16 @@
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/instant_service.h"
+#include "chrome/browser/search/search.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
 
 // static
 InstantService* InstantServiceFactory::GetForProfile(Profile* profile) {
+  if (!chrome::IsInstantExtendedAPIEnabled())
+    return NULL;
+
   return static_cast<InstantService*>(
       GetInstance()->GetServiceForBrowserContext(profile, true));
 }
@@ -42,5 +46,6 @@
 
 BrowserContextKeyedService* InstantServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* profile) const {
-  return new InstantService(static_cast<Profile*>(profile));
+  return chrome::IsInstantExtendedAPIEnabled() ?
+      new InstantService(static_cast<Profile*>(profile)) : NULL;
 }
diff --git a/chrome/browser/search/search.cc b/chrome/browser/search/search.cc
index 291fb08..7880a6f 100644
--- a/chrome/browser/search/search.cc
+++ b/chrome/browser/search/search.cc
@@ -312,6 +312,18 @@
        url.host() == chrome::kChromeSearchOnlineNtpHost);
 }
 
+bool IsNTPURL(const GURL& url, Profile* profile) {
+  if (!url.is_valid())
+    return false;
+
+  if (!IsInstantExtendedAPIEnabled())
+    return url == GURL(chrome::kChromeUINewTabURL);
+
+  return profile &&
+      (IsInstantURL(url, profile) ||
+       url == GURL(chrome::kChromeSearchLocalNtpUrl));
+}
+
 bool IsInstantNTP(const content::WebContents* contents) {
   if (!contents)
     return false;
@@ -426,14 +438,14 @@
   return MatchesOrigin(my_url, other_url) && my_url.path() == other_url.path();
 }
 
-GURL GetPrivilegedURLForInstant(const GURL& url, Profile* profile) {
+GURL GetEffectiveURLForInstant(const GURL& url, Profile* profile) {
   CHECK(ShouldAssignURLToInstantRenderer(url, profile))
       << "Error granting Instant access.";
 
-  if (IsPrivilegedURLForInstant(url))
+  if (url.SchemeIs(chrome::kChromeSearchScheme))
     return url;
 
-  GURL privileged_url(url);
+  GURL effective_url(url);
 
   // Replace the scheme with "chrome-search:".
   url_canon::Replacements<char> replacements;
@@ -454,13 +466,8 @@
     }
   }
 
-  privileged_url = privileged_url.ReplaceComponents(replacements);
-  return privileged_url;
-}
-
-bool IsPrivilegedURLForInstant(const GURL& url) {
-  return IsInstantExtendedAPIEnabled() &&
-         url.SchemeIs(chrome::kChromeSearchScheme);
+  effective_url = effective_url.ReplaceComponents(replacements);
+  return effective_url;
 }
 
 int GetInstantLoaderStalenessTimeoutSec() {
@@ -485,6 +492,9 @@
 }
 
 bool IsPreloadedInstantExtendedNTP(const content::WebContents* contents) {
+  if (!IsInstantExtendedAPIEnabled())
+    return false;
+
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   if (!profile_manager)
     return false;  // The profile manager can be NULL while testing.
diff --git a/chrome/browser/search/search.h b/chrome/browser/search/search.h
index b7ec61f..0efe355 100644
--- a/chrome/browser/search/search.h
+++ b/chrome/browser/search/search.h
@@ -82,6 +82,10 @@
 // Returns true if the Instant |url| should use process per site.
 bool ShouldUseProcessPerSiteForInstantURL(const GURL& url, Profile* profile);
 
+// Returns true if |url| corresponds to a New Tab page (it can be either an
+// Instant Extended NTP or a non-extended NTP).
+bool IsNTPURL(const GURL& url, Profile* profile);
+
 // Returns true if the visible entry of |contents| is a New Tab Page rendered
 // by Instant. A page that matches the search or Instant URL of the default
 // search provider but does not have any search terms is considered an Instant
@@ -122,7 +126,7 @@
 // Returns true if |my_url| matches |other_url|.
 bool MatchesOriginAndPath(const GURL& my_url, const GURL& other_url);
 
-// Transforms the input |url| into its "privileged URL". The returned URL
+// Transforms the input |url| into its "effective URL". The returned URL
 // facilitates grouping process-per-site. The |url| is transformed, for
 // example, from
 //
@@ -139,10 +143,7 @@
 // If |url| is that of the online NTP, its host is replaced with "online-ntp".
 // This forces the NTP and search results pages to have different SiteIntances,
 // and hence different processes.
-GURL GetPrivilegedURLForInstant(const GURL& url, Profile* profile);
-
-// Returns true if the input |url| is a privileged Instant URL.
-bool IsPrivilegedURLForInstant(const GURL& url);
+GURL GetEffectiveURLForInstant(const GURL& url, Profile* profile);
 
 // Returns the staleness timeout (in seconds) that should be used to refresh the
 // InstantLoader.
diff --git a/chrome/browser/search/search_unittest.cc b/chrome/browser/search/search_unittest.cc
index 925a56d..9c69611 100644
--- a/chrome/browser/search/search_unittest.cc
+++ b/chrome/browser/search/search_unittest.cc
@@ -8,6 +8,8 @@
 #include "base/metrics/histogram_samples.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/prefs/pref_service.h"
+#include "chrome/browser/search/instant_service.h"
+#include "chrome/browser/search/instant_service_factory.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/search_engines/search_terms_data.h"
 #include "chrome/browser/search_engines/template_url_service.h"
@@ -23,6 +25,7 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/renderer_preferences.h"
 
 namespace chrome {
 
@@ -224,6 +227,13 @@
     template_url_service->SetDefaultSearchProvider(template_url);
   }
 
+  bool InInstantProcess(const content::WebContents* contents) {
+    InstantService* instant_service =
+        InstantServiceFactory::GetForProfile(profile());
+    return instant_service->IsInstantProcess(
+        contents->GetRenderProcessHost()->GetID());
+  }
+
   scoped_ptr<base::FieldTrialList> field_trial_list_;
 };
 
@@ -319,109 +329,123 @@
   }
 }
 
-struct PrivilegedURLTestCase {
-  bool add_as_alternate_url;
-  const char* input_url;
-  const char* privileged_url;
-  bool different_site_instance;
-  const char* comment;
+// Each test case represents a navigation to |start_url| followed by a
+// navigation to |end_url|. We will check whether each navigation lands in an
+// Instant process, and also whether the navigation from start to end re-uses
+// the same SiteInstance (and hence the same RenderViewHost, etc.).
+const struct ProcessIsolationTestCase {
+  const char* description;
+  const char* start_url;
+  bool start_in_instant_process;
+  const char* end_url;
+  bool end_in_instant_process;
+  bool same_site_instance;
+} kProcessIsolationTestCases[] = {
+  {"Local NTP -> SRP",
+   "chrome-search://local-ntp",       true,
+   "https://foo.com/url?strk",        true,   false },
+  {"Local NTP -> Regular",
+   "chrome-search://local-ntp",       true,
+   "https://foo.com/other",           false,  false },
+  {"Remote NTP -> SRP",
+   "https://foo.com/instant?strk",    true,
+   "https://foo.com/url?strk",        true,   false },
+  {"Remote NTP -> Regular",
+   "https://foo.com/instant?strk",    true,
+   "https://foo.com/other",           false,  false },
+  {"SRP -> SRP",
+   "https://foo.com/url?strk",        true,
+   "https://foo.com/url?strk",        true,   true  },
+  {"SRP -> Regular",
+   "https://foo.com/url?strk",        true,
+   "https://foo.com/other",           false,  false },
+  {"Regular -> SRP",
+   "https://foo.com/other",           false,
+   "https://foo.com/url?strk",        true,   false },
 };
 
-TEST_F(SearchTest, GetPrivilegedURLForInstant) {
+TEST_F(SearchTest, ProcessIsolation) {
   EnableInstantExtendedAPIForTesting();
 
-  const PrivilegedURLTestCase kTestCases[] = {
-    { false,  // don't append input_url as alternate URL.
-      chrome::kChromeSearchLocalNtpUrl,  // input URL.
-      chrome::kChromeSearchLocalNtpUrl,  // expected privileged URL.
-      true,  // expected different SiteInstance.
-      "local NTP" },
-    { false,  // don't append input_url as alternate URL.
-      "https://foo.com/instant?strk",
-      "chrome-search://online-ntp/instant?strk",
-      true,  // expected different SiteInstance.
-      "Valid Instant NTP" },
-    { false,  // don't append input_url as alternate URL.
-      "https://foo.com/instant?strk&more=junk",
-      "chrome-search://online-ntp/instant?strk&more=junk",
-      true,  // expected different SiteInstance.
-      "Valid Instant NTP with extra query" },
-    { false,  // don't append input_url as alternate URL.
-      "https://foo.com/url?strk",
-      "chrome-search://foo.com/url?strk",
-      false,  // expected same SiteInstance.
-      "Search URL" },
-    { true,  // append input_url as alternate URL.
-      "https://notfoo.com/instant?strk",
-      "chrome-search://notfoo.com/instant?strk",
-      true,  // expected different SiteInstance.
-      "Invalid host in URL" },
-    { true,  // append input_url as alternate URL.
-      "https://foo.com/webhp?strk",
-      "chrome-search://foo.com/webhp?strk",
-      false,  // expected same SiteInstance.
-      "Invalid path in URL" },
-    { true,  // append input_url as alternate URL.
-      "https://foo.com/search?strk",
-      "chrome-search://foo.com/search?strk",
-      false,  // expected same SiteInstance.
-      "Invalid path in URL" },
-  };
+  for (size_t i = 0; i < arraysize(kProcessIsolationTestCases); ++i) {
+    const ProcessIsolationTestCase& test = kProcessIsolationTestCases[i];
+    AddTab(browser(), GURL("chrome://blank"));
+    const content::WebContents* contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
 
-  // GetPrivilegedURLForInstant expects ShouldAssignURLToInstantRenderer to
-  // be true, and the latter expects chrome-search: scheme or IsInstantURL to be
-  // true.  To force IsInstantURL to return true, add the input_url of each
-  // PrivilegedURLTestCase as the alternate URL for the default template URL.
-  const char* kSearchURL = "https://foo.com/url?strk";
-  TemplateURLService* template_url_service =
-      TemplateURLServiceFactory::GetForProfile(profile());
-  TemplateURLData data;
-  data.SetURL(kSearchURL);
-  data.instant_url = "http://foo.com/instant?strk";
-  data.search_terms_replacement_key = "strk";
-  for (size_t i = 0; i < arraysize(kTestCases); ++i) {
-    const PrivilegedURLTestCase& test = kTestCases[i];
-    if (test.add_as_alternate_url)
-      data.alternate_urls.push_back(test.input_url);
-  }
-  TemplateURL* template_url = new TemplateURL(profile(), data);
-  // Takes ownership of |template_url|.
-  template_url_service->Add(template_url);
-  template_url_service->SetDefaultSearchProvider(template_url);
+    // Navigate to start URL.
+    NavigateAndCommitActiveTab(GURL(test.start_url));
+    EXPECT_EQ(test.start_in_instant_process, InInstantProcess(contents))
+        << test.description;
 
-  AddTab(browser(), GURL("chrome://blank"));
-  for (size_t i = 0; i < arraysize(kTestCases); ++i) {
-    const PrivilegedURLTestCase& test = kTestCases[i];
-
-    // Verify GetPrivilegedURLForInstant.
-    EXPECT_EQ(GURL(test.privileged_url),
-              GetPrivilegedURLForInstant(GURL(test.input_url), profile()))
-        << test.input_url << " " << test.comment;
-
-    // Verify that navigating from input_url to search URL results in same or
-    // different SiteInstance.
-    // First, navigate to input_url.
-    NavigateAndCommitActiveTab(GURL(test.input_url));
-    content::WebContents* contents =
-        browser()->tab_strip_model()->GetWebContentsAt(0);
-    // Cache the SiteInstance, RenderViewHost and RenderProcessHost for
-    // input_url.
-    const scoped_refptr<content::SiteInstance> prev_site_instance =
+    // Save state.
+    const scoped_refptr<content::SiteInstance> start_site_instance =
         contents->GetSiteInstance();
-    const content::RenderViewHost* prev_rvh = contents->GetRenderViewHost();
-    const content::RenderProcessHost* prev_rph =
+    const content::RenderProcessHost* start_rph =
         contents->GetRenderProcessHost();
-    // Then, navigate to search URL.
-    NavigateAndCommitActiveTab(GURL(kSearchURL));
-    EXPECT_EQ(test.different_site_instance,
-              contents->GetSiteInstance() != prev_site_instance)
-        << test.input_url << " " << test.comment;
-    EXPECT_EQ(test.different_site_instance,
-              contents->GetRenderViewHost() != prev_rvh)
-        << test.input_url << " " << test.comment;
-    EXPECT_EQ(test.different_site_instance,
-              contents->GetRenderProcessHost() != prev_rph)
-        << test.input_url << " " << test.comment;
+    const content::RenderViewHost* start_rvh =
+        contents->GetRenderViewHost();
+
+    // Navigate to end URL.
+    NavigateAndCommitActiveTab(GURL(test.end_url));
+    EXPECT_EQ(test.end_in_instant_process, InInstantProcess(contents))
+        << test.description;
+
+    EXPECT_EQ(test.same_site_instance,
+              start_site_instance == contents->GetSiteInstance())
+        << test.description;
+    EXPECT_EQ(test.same_site_instance,
+              start_rvh == contents->GetRenderViewHost())
+        << test.description;
+    EXPECT_EQ(test.same_site_instance,
+              start_rph == contents->GetRenderProcessHost())
+        << test.description;
+  }
+}
+
+TEST_F(SearchTest, ProcessIsolation_RendererInitiated) {
+  EnableInstantExtendedAPIForTesting();
+
+  for (size_t i = 0; i < arraysize(kProcessIsolationTestCases); ++i) {
+    const ProcessIsolationTestCase& test = kProcessIsolationTestCases[i];
+    AddTab(browser(), GURL("chrome://blank"));
+    content::WebContents* contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+
+    // Navigate to start URL.
+    NavigateAndCommitActiveTab(GURL(test.start_url));
+    EXPECT_EQ(test.start_in_instant_process, InInstantProcess(contents))
+        << test.description;
+
+    // Save state.
+    const scoped_refptr<content::SiteInstance> start_site_instance =
+        contents->GetSiteInstance();
+    const content::RenderProcessHost* start_rph =
+        contents->GetRenderProcessHost();
+    const content::RenderViewHost* start_rvh =
+        contents->GetRenderViewHost();
+
+    // Navigate to end URL via a renderer-initiated navigation.
+    content::NavigationController* controller = &contents->GetController();
+    content::NavigationController::LoadURLParams load_params(
+        GURL(test.end_url));
+    load_params.is_renderer_initiated = true;
+    load_params.transition_type = content::PAGE_TRANSITION_LINK;
+
+    controller->LoadURLWithParams(load_params);
+    CommitPendingLoad(controller);
+    EXPECT_EQ(test.end_in_instant_process, InInstantProcess(contents))
+        << test.description;
+
+    EXPECT_EQ(test.same_site_instance,
+              start_site_instance == contents->GetSiteInstance())
+        << test.description;
+    EXPECT_EQ(test.same_site_instance,
+              start_rvh == contents->GetRenderViewHost())
+        << test.description;
+    EXPECT_EQ(test.same_site_instance,
+              start_rph == contents->GetRenderProcessHost())
+        << test.description;
   }
 }
 
@@ -602,4 +626,29 @@
   EXPECT_FALSE(ShouldShowInstantNTP());
 }
 
+TEST_F(SearchTest, IsNTPURL) {
+  GURL invalid_url;
+  GURL ntp_url(chrome::kChromeUINewTabURL);
+  GURL local_ntp_url(GetLocalInstantURL(profile()));
+
+  EXPECT_FALSE(chrome::IsNTPURL(invalid_url, profile()));
+  EXPECT_FALSE(chrome::IsNTPURL(local_ntp_url, profile()));
+
+  EXPECT_TRUE(chrome::IsNTPURL(ntp_url, NULL));
+  EXPECT_FALSE(chrome::IsNTPURL(local_ntp_url, NULL));
+
+  // Enable Instant. No margin.
+  EnableInstantExtendedAPIForTesting();
+  profile()->GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true);
+  GURL remote_ntp_url(GetInstantURL(profile(), kDisableStartMargin));
+
+  EXPECT_FALSE(chrome::IsNTPURL(ntp_url, profile()));
+  EXPECT_TRUE(chrome::IsNTPURL(local_ntp_url, profile()));
+  EXPECT_TRUE(chrome::IsNTPURL(remote_ntp_url, profile()));
+
+  EXPECT_FALSE(chrome::IsNTPURL(ntp_url, NULL));
+  EXPECT_FALSE(chrome::IsNTPURL(local_ntp_url, NULL));
+  EXPECT_FALSE(chrome::IsNTPURL(remote_ntp_url, NULL));
+}
+
 }  // namespace chrome
diff --git a/chrome/browser/search_engines/OWNERS b/chrome/browser/search_engines/OWNERS
index 00102bf..dd79a14 100644
--- a/chrome/browser/search_engines/OWNERS
+++ b/chrome/browser/search_engines/OWNERS
@@ -1,5 +1,5 @@
+estade@chromium.org
 pkasting@chromium.org
-sky@chromium.org
 stevet@chromium.org
 
 per-file *_android.*=yfriedman@chromium.org
\ No newline at end of file
diff --git a/chrome/browser/search_engines/prepopulated_engines.json b/chrome/browser/search_engines/prepopulated_engines.json
index 8e30f34..a4e7c94 100644
--- a/chrome/browser/search_engines/prepopulated_engines.json
+++ b/chrome/browser/search_engines/prepopulated_engines.json
@@ -26,7 +26,7 @@
 
     // Increment this if you change the data in ways that mean users with
     // existing data should get a new version.
-    "kCurrentDataVersion": 57
+    "kCurrentDataVersion": 58
   },
 
   // The following engines are included in country lists and are added to the
@@ -851,7 +851,7 @@
       "suggest_url": "{google:baseSuggestURL}search?{google:searchFieldtrialParameter}client={google:suggestClient}&q={searchTerms}&{google:cursorPosition}{google:zeroPrefixUrl}sugkey={google:suggestAPIKeyParameter}",
       "instant_url": "{google:baseURL}webhp?sourceid=chrome-instant&{google:RLZ}{google:instantEnabledParameter}{google:instantExtendedEnabledParameter}{google:ntpIsThemedParameter}{google:omniboxStartMarginParameter}ie={inputEncoding}",
       "image_url": "{google:baseURL}searchbyimage/upload",
-      "image_url_post_params": "image_content={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource}",
+      "image_url_post_params": "encoded_image={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource}",
       "alternate_urls": [
         "{google:baseURL}#q={searchTerms}",
         "{google:baseURL}search#q={searchTerms}",
diff --git a/chrome/browser/search_engines/template_url.cc b/chrome/browser/search_engines/template_url.cc
index ecd139b..dc574ed 100644
--- a/chrome/browser/search_engines/template_url.cc
+++ b/chrome/browser/search_engines/template_url.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/search_engines/template_url.h"
 
+#include "base/basictypes.h"
 #include "base/command_line.h"
 #include "base/format_macros.h"
 #include "base/guid.h"
@@ -12,6 +13,7 @@
 #include "base/i18n/rtl.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial.h"
+#include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -26,6 +28,7 @@
 #include "extensions/common/constants.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/escape.h"
+#include "net/base/mime_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace {
@@ -169,6 +172,7 @@
     std::string modifier(version_info.GetVersionStringModifier());
     if (!modifier.empty())
       version += " " + modifier;
+    return version;
   }
   return "unknown";
 }
@@ -257,15 +261,35 @@
   return !post_params_.empty();
 }
 
-bool TemplateURLRef::EncodeFormData(const std::vector<PostParam>& post_params,
-                                    std::string* post_data) const {
+bool TemplateURLRef::EncodeFormData(const PostParams& post_params,
+                                    PostContent* post_content) const {
   // TODO(jnd): implement this function once we have form encoding utility in
   // Chrome side.
-  if (!post_params.empty()) {
-    if (!post_data)
-      return false;
-    *post_data = "not implemented yet!";
+  if (post_params.empty())
+    return true;
+  if (!post_content)
+    return false;
+
+  const char kUploadDataMIMEType[] = "multipart/form-data; boundary=";
+  const char kMultipartBoundary[] = "----+*+----%016" PRIx64 "----+*+----";
+  // Each name/value pair is stored in a body part which is preceded by a
+  // boundary delimiter line. Uses random number generator here to create
+  // a unique boundary delimiter for form data encoding.
+  std::string boundary = base::StringPrintf(kMultipartBoundary,
+                                            base::RandUint64());
+  // Sets the content MIME type.
+  post_content->first = kUploadDataMIMEType;
+  post_content->first += boundary;
+  // Encodes the post parameters.
+  std::string* post_data = &post_content->second;
+  post_data->clear();
+  for (PostParams::const_iterator param = post_params.begin();
+       param != post_params.end(); ++param) {
+    DCHECK(!param->first.empty());
+    net::AddMultipartValueForUpload(param->first, param->second, boundary,
+                                    std::string(), post_data);
   }
+  net::AddMultipartFinalDelimiterForUpload(boundary, post_data);
   return true;
 }
 
@@ -282,22 +306,22 @@
 
 std::string TemplateURLRef::ReplaceSearchTerms(
     const SearchTermsArgs& search_terms_args,
-    std::string* post_data) const {
+    PostContent* post_content) const {
   UIThreadSearchTermsData search_terms_data(owner_->profile());
   return ReplaceSearchTermsUsingTermsData(search_terms_args, search_terms_data,
-                                          post_data);
+                                          post_content);
 }
 
 std::string TemplateURLRef::ReplaceSearchTermsUsingTermsData(
     const SearchTermsArgs& search_terms_args,
     const SearchTermsData& search_terms_data,
-    std::string* post_data) const {
+    PostContent* post_content) const {
   ParseIfNecessaryUsingTermsData(search_terms_data);
   if (!valid_)
     return std::string();
 
   std::string url(HandleReplacements(search_terms_args, search_terms_data,
-                                     post_data));
+                                     post_content));
 
   // If the user specified additional query params on the command line, add
   // them.
@@ -727,10 +751,10 @@
 std::string TemplateURLRef::HandleReplacements(
     const SearchTermsArgs& search_terms_args,
     const SearchTermsData& search_terms_data,
-    std::string* post_data) const {
+    PostContent* post_content) const {
   if (replacements_.empty()) {
     if (!post_params_.empty())
-      EncodeFormData(post_params_, post_data);
+      EncodeFormData(post_params_, post_content);
     return parsed_url_;
   }
 
@@ -925,7 +949,7 @@
   }
 
   if (!post_params_.empty())
-    EncodeFormData(post_params_, post_data);
+    EncodeFormData(post_params_, post_content);
 
   return url;
 }
diff --git a/chrome/browser/search_engines/template_url.h b/chrome/browser/search_engines/template_url.h
index 4b5b2d2..c192e04 100644
--- a/chrome/browser/search_engines/template_url.h
+++ b/chrome/browser/search_engines/template_url.h
@@ -52,6 +52,13 @@
     INDEXED
   };
 
+  // Type to store <content_type, post_data> pair for POST URLs.
+  // The |content_type|(first part of the pair) is the content-type of
+  // the |post_data|(second part of the pair) which is encoded in
+  // "multipart/form-data" format, it also contains the MIME boundary used in
+  // the |post_data|. See http://tools.ietf.org/html/rfc2046 for the details.
+  typedef std::pair<std::string, std::string> PostContent;
+
   // This struct encapsulates arguments passed to
   // TemplateURLRef::ReplaceSearchTerms methods.  By default, only search_terms
   // is required and is passed in the constructor.
@@ -129,15 +136,15 @@
   //
   // If this TemplateURLRef does not support replacement (SupportsReplacement
   // returns false), an empty string is returned.
-  // If this TemplateURLRef uses POST, and |post_data| is not NULL, the
+  // If this TemplateURLRef uses POST, and |post_content| is not NULL, the
   // |post_params_| will be replaced, encoded in "multipart/form-data" format
-  // and stored into |post_data|.
+  // and stored into |post_content|.
   std::string ReplaceSearchTerms(
       const SearchTermsArgs& search_terms_args,
-      std::string* post_data) const;
+      PostContent* post_content) const;
   // TODO(jnd): remove the following ReplaceSearchTerms definition which does
-  // not have |post_data| parameter once all reference callers pass |post_data|
-  // parameter.
+  // not have |post_content| parameter once all reference callers pass
+  // |post_content| parameter.
   std::string ReplaceSearchTerms(
       const SearchTermsArgs& search_terms_args) const {
     return ReplaceSearchTerms(search_terms_args, NULL);
@@ -149,7 +156,7 @@
   std::string ReplaceSearchTermsUsingTermsData(
       const SearchTermsArgs& search_terms_args,
       const SearchTermsData& search_terms_data,
-      std::string* post_data) const;
+      PostContent* post_content) const;
 
   // Returns true if the TemplateURLRef is valid. An invalid TemplateURLRef is
   // one that contains unknown terms, or invalid characters.
@@ -304,10 +311,10 @@
       const SearchTermsData& search_terms_data) const;
 
   // Encode post parameters in "multipart/form-data" format and store it
-  // inside |post_data|. Returns false if errors are encountered during
+  // inside |post_content|. Returns false if errors are encountered during
   // encoding. This method is called each time ReplaceSearchTerms gets called.
   bool EncodeFormData(const PostParams& post_params,
-                      std::string* post_data) const;
+                      PostContent* post_content) const;
 
   // Handles a replacement by using real term data. If the replacement
   // belongs to a PostParam, the PostParam will be replaced by the term data.
@@ -324,7 +331,7 @@
   std::string HandleReplacements(
       const SearchTermsArgs& search_terms_args,
       const SearchTermsData& search_terms_data,
-      std::string* post_data) const;
+      PostContent* post_content) const;
 
   // The TemplateURL that contains us.  This should outlive us.
   TemplateURL* const owner_;
diff --git a/chrome/browser/search_engines/template_url_service.cc b/chrome/browser/search_engines/template_url_service.cc
index cb9a697..8ae629a 100644
--- a/chrome/browser/search_engines/template_url_service.cc
+++ b/chrome/browser/search_engines/template_url_service.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/search_engines/template_url_service.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "base/auto_reset.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
@@ -87,6 +90,13 @@
       (url1->url() == url2->url()) &&
       (url1->suggestions_url() == url2->suggestions_url()) &&
       (url1->instant_url() == url2->instant_url()) &&
+      (url1->image_url() == url2->image_url()) &&
+      (url1->search_url_post_params() == url2->search_url_post_params()) &&
+      (url1->suggestions_url_post_params() ==
+          url2->suggestions_url_post_params()) &&
+      (url1->instant_url_post_params() == url2->instant_url_post_params()) &&
+      (url1->image_url_post_params() == url2->image_url_post_params()) &&
+      (url1->image_url() == url2->image_url()) &&
       (url1->favicon_url() == url2->favicon_url()) &&
       (url1->safe_for_autoreplace() == url2->safe_for_autoreplace()) &&
       (url1->show_in_default_list() == url2->show_in_default_list()) &&
@@ -1256,6 +1266,18 @@
   se_specifics->set_suggestions_url(turl.suggestions_url());
   se_specifics->set_prepopulate_id(turl.prepopulate_id());
   se_specifics->set_instant_url(turl.instant_url());
+  if (!turl.image_url().empty())
+    se_specifics->set_image_url(turl.image_url());
+  if (!turl.search_url_post_params().empty())
+    se_specifics->set_search_url_post_params(turl.search_url_post_params());
+  if (!turl.suggestions_url_post_params().empty()) {
+    se_specifics->set_suggestions_url_post_params(
+        turl.suggestions_url_post_params());
+  }
+  if (!turl.instant_url_post_params().empty())
+    se_specifics->set_instant_url_post_params(turl.instant_url_post_params());
+  if (!turl.image_url_post_params().empty())
+    se_specifics->set_image_url_post_params(turl.image_url_post_params());
   se_specifics->set_last_modified(turl.last_modified().ToInternalValue());
   se_specifics->set_sync_guid(turl.sync_guid());
   for (size_t i = 0; i < turl.alternate_urls().size(); ++i)
@@ -1309,6 +1331,11 @@
   data.SetURL(specifics.url());
   data.suggestions_url = specifics.suggestions_url();
   data.instant_url = specifics.instant_url();
+  data.image_url = specifics.image_url();
+  data.search_url_post_params = specifics.search_url_post_params();
+  data.suggestions_url_post_params = specifics.suggestions_url_post_params();
+  data.instant_url_post_params = specifics.instant_url_post_params();
+  data.image_url_post_params = specifics.image_url_post_params();
   data.favicon_url = GURL(specifics.favicon_url());
   data.show_in_default_list = specifics.show_in_default_list();
   data.safe_for_autoreplace = specifics.safe_for_autoreplace();
@@ -1574,6 +1601,11 @@
   std::string search_url;
   std::string suggest_url;
   std::string instant_url;
+  std::string image_url;
+  std::string search_url_post_params;
+  std::string suggest_url_post_params;
+  std::string instant_url_post_params;
+  std::string image_url_post_params;
   std::string icon_url;
   std::string encodings;
   std::string short_name;
@@ -1588,6 +1620,11 @@
     search_url = t_url->url();
     suggest_url = t_url->suggestions_url();
     instant_url = t_url->instant_url();
+    image_url = t_url->image_url();
+    search_url_post_params = t_url->search_url_post_params();
+    suggest_url_post_params = t_url->suggestions_url_post_params();
+    instant_url_post_params = t_url->instant_url_post_params();
+    image_url_post_params = t_url->image_url_post_params();
     GURL icon_gurl = t_url->favicon_url();
     if (!icon_gurl.is_empty())
       icon_url = icon_gurl.spec();
@@ -1604,6 +1641,15 @@
   prefs->SetString(prefs::kDefaultSearchProviderSearchURL, search_url);
   prefs->SetString(prefs::kDefaultSearchProviderSuggestURL, suggest_url);
   prefs->SetString(prefs::kDefaultSearchProviderInstantURL, instant_url);
+  prefs->SetString(prefs::kDefaultSearchProviderImageURL, image_url);
+  prefs->SetString(prefs::kDefaultSearchProviderSearchURLPostParams,
+                   search_url_post_params);
+  prefs->SetString(prefs::kDefaultSearchProviderSuggestURLPostParams,
+                   suggest_url_post_params);
+  prefs->SetString(prefs::kDefaultSearchProviderInstantURLPostParams,
+                   instant_url_post_params);
+  prefs->SetString(prefs::kDefaultSearchProviderImageURLPostParams,
+                   image_url_post_params);
   prefs->SetString(prefs::kDefaultSearchProviderIconURL, icon_url);
   prefs->SetString(prefs::kDefaultSearchProviderEncodings, encodings);
   prefs->SetString(prefs::kDefaultSearchProviderName, short_name);
@@ -1654,6 +1700,16 @@
       prefs->GetString(prefs::kDefaultSearchProviderSuggestURL);
   std::string instant_url =
       prefs->GetString(prefs::kDefaultSearchProviderInstantURL);
+  std::string image_url =
+      prefs->GetString(prefs::kDefaultSearchProviderImageURL);
+  std::string search_url_post_params =
+      prefs->GetString(prefs::kDefaultSearchProviderSearchURLPostParams);
+  std::string suggest_url_post_params =
+      prefs->GetString(prefs::kDefaultSearchProviderSuggestURLPostParams);
+  std::string instant_url_post_params =
+      prefs->GetString(prefs::kDefaultSearchProviderInstantURLPostParams);
+  std::string image_url_post_params =
+      prefs->GetString(prefs::kDefaultSearchProviderImageURLPostParams);
   std::string icon_url =
       prefs->GetString(prefs::kDefaultSearchProviderIconURL);
   std::string encodings =
@@ -1672,6 +1728,11 @@
   data.SetURL(search_url);
   data.suggestions_url = suggest_url;
   data.instant_url = instant_url;
+  data.image_url = image_url;
+  data.search_url_post_params = search_url_post_params;
+  data.suggestions_url_post_params = suggest_url_post_params;
+  data.instant_url_post_params = instant_url_post_params;
+  data.image_url_post_params = image_url_post_params;
   data.favicon_url = GURL(icon_url);
   data.show_in_default_list = true;
   data.alternate_urls.clear();
@@ -1858,7 +1919,6 @@
     string16 search_terms;
     if ((*i)->ExtractSearchTermsFromURL(row.url(), &search_terms) &&
         !search_terms.empty()) {
-
       if (content::PageTransitionStripQualifier(details.transition) ==
           content::PAGE_TRANSITION_KEYWORD) {
         // The visit is the result of the user entering a keyword, generate a
@@ -2030,9 +2090,10 @@
     // Otherwise, it should be FindNewDefaultSearchProvider.
     TemplateURL* synced_default = GetPendingSyncedDefaultSearchProvider();
     if (synced_default) {
+      pending_synced_default_search_ = false;
+
       base::AutoReset<DefaultSearchChangeOrigin> change_origin(
           &dsp_change_origin_, DSP_CHANGE_SYNC_NOT_MANAGED);
-      pending_synced_default_search_ = false;
       SetDefaultSearchProviderNoNotify(synced_default);
     } else {
       SetDefaultSearchProviderNoNotify(FindNewDefaultSearchProvider());
diff --git a/chrome/browser/search_engines/template_url_service_android.cc b/chrome/browser/search_engines/template_url_service_android.cc
index 262dc34..9ffe4b8 100644
--- a/chrome/browser/search_engines/template_url_service_android.cc
+++ b/chrome/browser/search_engines/template_url_service_android.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/search_engines/search_terms_data.h"
 #include "chrome/browser/search_engines/template_url.h"
 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
 #include "chrome/browser/search_engines/template_url_service.h"
@@ -100,6 +101,15 @@
   return template_url_service_->is_default_search_managed();
 }
 
+jboolean TemplateUrlServiceAndroid::IsSearchByImageAvailable(JNIEnv* env,
+                                                             jobject obj) {
+  const TemplateURL* default_search_provider =
+      template_url_service_->GetDefaultSearchProvider();
+  return default_search_provider &&
+      !default_search_provider->image_url().empty() &&
+      default_search_provider->image_url_ref().IsValid();
+}
+
 jboolean TemplateUrlServiceAndroid::IsDefaultSearchEngineGoogle(JNIEnv* env,
                                                                 jobject obj) {
   TemplateURL* default_search_provider =
diff --git a/chrome/browser/search_engines/template_url_service_android.h b/chrome/browser/search_engines/template_url_service_android.h
index 9c6fad5..331ac88 100644
--- a/chrome/browser/search_engines/template_url_service_android.h
+++ b/chrome/browser/search_engines/template_url_service_android.h
@@ -29,6 +29,7 @@
   base::android::ScopedJavaLocalRef<jobject>
       GetPrepopulatedTemplateUrlAt(JNIEnv* env, jobject obj, jint index);
   jboolean IsSearchProviderManaged(JNIEnv* env, jobject obj);
+  jboolean IsSearchByImageAvailable(JNIEnv* env, jobject obj);
   jboolean IsDefaultSearchEngineGoogle(JNIEnv* env, jobject obj);
 
   // NotificationObserver:
diff --git a/chrome/browser/search_engines/template_url_service_factory.cc b/chrome/browser/search_engines/template_url_service_factory.cc
index 915e275..1beedcd 100644
--- a/chrome/browser/search_engines/template_url_service_factory.cc
+++ b/chrome/browser/search_engines/template_url_service_factory.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 
+#include <string>
+
 #include "base/prefs/pref_service.h"
 #include "chrome/browser/google/google_url_tracker_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
@@ -81,6 +83,26 @@
       std::string(),
       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
   registry->RegisterStringPref(
+      prefs::kDefaultSearchProviderImageURL,
+      std::string(),
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+  registry->RegisterStringPref(
+      prefs::kDefaultSearchProviderSearchURLPostParams,
+      std::string(),
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+  registry->RegisterStringPref(
+      prefs::kDefaultSearchProviderSuggestURLPostParams,
+      std::string(),
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+  registry->RegisterStringPref(
+      prefs::kDefaultSearchProviderInstantURLPostParams,
+      std::string(),
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+  registry->RegisterStringPref(
+      prefs::kDefaultSearchProviderImageURLPostParams,
+      std::string(),
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+  registry->RegisterStringPref(
       prefs::kDefaultSearchProviderKeyword,
       std::string(),
       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
diff --git a/chrome/browser/search_engines/template_url_unittest.cc b/chrome/browser/search_engines/template_url_unittest.cc
index 23601f2..a4d38a5 100644
--- a/chrome/browser/search_engines/template_url_unittest.cc
+++ b/chrome/browser/search_engines/template_url_unittest.cc
@@ -208,13 +208,14 @@
   GURL result(url.image_url_ref().ReplaceSearchTerms(search_args));
   ASSERT_TRUE(result.is_valid());
   EXPECT_EQ(KImageSearchURL, result.spec());
-  std::string post_data;
+  TemplateURLRef::PostContent post_content;
   TestSearchTermsData search_terms_data("http://X");
   result = GURL(url.image_url_ref().ReplaceSearchTermsUsingTermsData(
-      search_args, search_terms_data, &post_data));
+      search_args, search_terms_data, &post_content));
   ASSERT_TRUE(result.is_valid());
   EXPECT_EQ(KImageSearchURL, result.spec());
-  ASSERT_FALSE(post_data.empty());
+  ASSERT_FALSE(post_content.first.empty());
+  ASSERT_FALSE(post_content.second.empty());
 
   // Check parsed result of post parameters.
   const TemplateURLRef::Replacements& replacements =
@@ -252,7 +253,7 @@
     if (i->first == "empty_param") {
       EXPECT_TRUE(i->second.empty());
     } else if (i->first == "sbisrc") {
-      EXPECT_EQ("unknown", i->second);
+      EXPECT_FALSE(i->second.empty());
     } else {
       EXPECT_EQ("constant_param", i->first);
       EXPECT_EQ("constant", i->second);
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index ba9fe9d..6bcbeca 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -30,6 +30,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/sessions/serialized_navigation_entry_test_helper.h"
 #include "content/public/browser/navigation_controller.h"
@@ -51,10 +52,6 @@
 #include "base/mac/scoped_nsautorelease_pool.h"
 #endif
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 class SessionRestoreTest : public InProcessBrowserTest {
  public:
   SessionRestoreTest() : active_browser_list_(NULL) {}
@@ -90,12 +87,6 @@
   }
 
   virtual bool SetUpUserDataDirectory() OVERRIDE {
-    // Make sure the first run sentinel file exists before running these tests,
-    // since some of them customize the session startup pref whose value can
-    // be different than the default during the first run.
-    // TODO(bauerb): set the first run flag instead of creating a sentinel file.
-    first_run::CreateSentinel();
-
     url1_ = ui_test_utils::GetTestUrl(
         base::FilePath().AppendASCII("session_history"),
         base::FilePath().AppendASCII("bot1.html"));
@@ -858,7 +849,7 @@
                        RestoreAfterClosingTabbedBrowserWithAppAndLaunching) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/signin/DEPS b/chrome/browser/signin/DEPS
index 2eaadea..2b4bc38 100644
--- a/chrome/browser/signin/DEPS
+++ b/chrome/browser/signin/DEPS
@@ -53,8 +53,9 @@
 
   # These files are staying in //chrome so no need to limit.
   r"(chrome_signin_manager_delegate|"
-  r"signin_names_io_thread.*|"
-  r"signin_manager_factory)"
+  r"signin_names_io_thread|"
+  r"signin_manager_factory|"
+  r"signin_promo)"
   r"\.(h|cc)": [
     "+chrome/browser",
     "+chrome/common",
diff --git a/chrome/browser/signin/oauth2_token_service.cc b/chrome/browser/signin/oauth2_token_service.cc
index 1cc4e18..0b6bd5d 100644
--- a/chrome/browser/signin/oauth2_token_service.cc
+++ b/chrome/browser/signin/oauth2_token_service.cc
@@ -77,6 +77,8 @@
   // The given |oauth2_token_service| will be informed when fetching is done.
   static Fetcher* CreateAndStart(OAuth2TokenService* oauth2_token_service,
                                  net::URLRequestContextGetter* getter,
+                                 const std::string& chrome_client_id,
+                                 const std::string& chrome_client_secret,
                                  const std::string& refresh_token,
                                  const OAuth2TokenService::ScopeSet& scopes,
                                  base::WeakPtr<RequestImpl> waiting_request);
@@ -85,6 +87,8 @@
   // Add a request that is waiting for the result of this Fetcher.
   void AddWaitingRequest(base::WeakPtr<RequestImpl> waiting_request);
 
+  void Cancel();
+
   const OAuth2TokenService::ScopeSet& GetScopeSet() const;
   const std::string& GetRefreshToken() const;
 
@@ -100,6 +104,8 @@
  private:
   Fetcher(OAuth2TokenService* oauth2_token_service,
           net::URLRequestContextGetter* getter,
+          const std::string& chrome_client_id,
+          const std::string& chrome_client_secret,
           const std::string& refresh_token,
           const OAuth2TokenService::ScopeSet& scopes,
           base::WeakPtr<RequestImpl> waiting_request);
@@ -129,6 +135,9 @@
   GoogleServiceAuthError error_;
   std::string access_token_;
   base::Time expiration_date_;
+  // OAuth2 client id and secret.
+  std::string chrome_client_id_;
+  std::string chrome_client_secret_;
 
   DISALLOW_COPY_AND_ASSIGN(Fetcher);
 };
@@ -137,11 +146,19 @@
 OAuth2TokenService::Fetcher* OAuth2TokenService::Fetcher::CreateAndStart(
     OAuth2TokenService* oauth2_token_service,
     net::URLRequestContextGetter* getter,
+    const std::string& chrome_client_id,
+    const std::string& chrome_client_secret,
     const std::string& refresh_token,
     const OAuth2TokenService::ScopeSet& scopes,
     base::WeakPtr<RequestImpl> waiting_request) {
   OAuth2TokenService::Fetcher* fetcher = new Fetcher(
-      oauth2_token_service, getter, refresh_token, scopes, waiting_request);
+      oauth2_token_service,
+      getter,
+      chrome_client_id,
+      chrome_client_secret,
+      refresh_token,
+      scopes,
+      waiting_request);
   fetcher->Start();
   return fetcher;
 }
@@ -149,6 +166,8 @@
 OAuth2TokenService::Fetcher::Fetcher(
     OAuth2TokenService* oauth2_token_service,
     net::URLRequestContextGetter* getter,
+    const std::string& chrome_client_id,
+    const std::string& chrome_client_secret,
     const std::string& refresh_token,
     const OAuth2TokenService::ScopeSet& scopes,
     base::WeakPtr<RequestImpl> waiting_request)
@@ -157,7 +176,9 @@
       refresh_token_(refresh_token),
       scopes_(scopes),
       retry_number_(0),
-      error_(GoogleServiceAuthError::SERVICE_UNAVAILABLE) {
+      error_(GoogleServiceAuthError::SERVICE_UNAVAILABLE),
+      chrome_client_id_(chrome_client_id),
+      chrome_client_secret_(chrome_client_secret) {
   DCHECK(oauth2_token_service_);
   DCHECK(getter_.get());
   DCHECK(refresh_token_.length());
@@ -172,8 +193,8 @@
 
 void OAuth2TokenService::Fetcher::Start() {
   fetcher_.reset(new OAuth2AccessTokenFetcher(this, getter_.get()));
-  fetcher_->Start(GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
-                  GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
+  fetcher_->Start(chrome_client_id_,
+                  chrome_client_secret_,
                   refresh_token_,
                   std::vector<std::string>(scopes_.begin(), scopes_.end()));
   retry_timer_.Stop();
@@ -262,6 +283,13 @@
   waiting_requests_.push_back(waiting_request);
 }
 
+void OAuth2TokenService::Fetcher::Cancel() {
+  fetcher_.reset();
+  retry_timer_.Stop();
+  error_ = GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
+  InformWaitingRequestsAndDelete();
+}
+
 const OAuth2TokenService::ScopeSet& OAuth2TokenService::Fetcher::GetScopeSet()
     const {
   return scopes_;
@@ -309,7 +337,26 @@
 scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
     const OAuth2TokenService::ScopeSet& scopes,
     OAuth2TokenService::Consumer* consumer) {
-  return StartRequestWithContext(GetRequestContext(), scopes, consumer);
+  return StartRequestForClientWithContext(
+      GetRequestContext(),
+      GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
+      GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
+      scopes,
+      consumer);
+}
+
+scoped_ptr<OAuth2TokenService::Request>
+OAuth2TokenService::StartRequestForClient(
+    const std::string& client_id,
+    const std::string& client_secret,
+    const OAuth2TokenService::ScopeSet& scopes,
+    OAuth2TokenService::Consumer* consumer) {
+  return StartRequestForClientWithContext(
+      GetRequestContext(),
+      client_id,
+      client_secret,
+      scopes,
+      consumer);
 }
 
 scoped_ptr<OAuth2TokenService::Request>
@@ -317,6 +364,21 @@
     net::URLRequestContextGetter* getter,
     const ScopeSet& scopes,
     Consumer* consumer) {
+  return StartRequestForClientWithContext(
+      getter,
+      GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
+      GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
+      scopes,
+      consumer);
+}
+
+scoped_ptr<OAuth2TokenService::Request>
+OAuth2TokenService::StartRequestForClientWithContext(
+    net::URLRequestContextGetter* getter,
+    const std::string& client_id,
+    const std::string& client_secret,
+    const ScopeSet& scopes,
+    Consumer* consumer) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
   scoped_ptr<RequestImpl> request(new RequestImpl(consumer));
@@ -348,7 +410,12 @@
   }
 
   pending_fetchers_[fetch_parameters] =
-      Fetcher::CreateAndStart(this, getter, refresh_token, scopes,
+      Fetcher::CreateAndStart(this,
+                              getter,
+                              client_id,
+                              client_secret,
+                              refresh_token,
+                              scopes,
                               request->AsWeakPtr());
   return request.PassAs<Request>();
 }
@@ -469,6 +536,40 @@
   token_cache_.clear();
 }
 
+void OAuth2TokenService::CancelAllRequests() {
+  std::vector<Fetcher*> fetchers_to_cancel;
+  for (std::map<FetchParameters, Fetcher*>::iterator iter =
+           pending_fetchers_.begin();
+       iter != pending_fetchers_.end();
+       ++iter) {
+    fetchers_to_cancel.push_back(iter->second);
+  }
+  CancelFetchers(fetchers_to_cancel);
+}
+
+void OAuth2TokenService::CancelRequestsForToken(
+    const std::string& refresh_token) {
+  std::vector<Fetcher*> fetchers_to_cancel;
+  for (std::map<FetchParameters, Fetcher*>::iterator iter =
+           pending_fetchers_.begin();
+       iter != pending_fetchers_.end();
+       ++iter) {
+    if (iter->first.first == refresh_token)
+      fetchers_to_cancel.push_back(iter->second);
+  }
+  CancelFetchers(fetchers_to_cancel);
+}
+
+void OAuth2TokenService::CancelFetchers(
+    std::vector<Fetcher*> fetchers_to_cancel) {
+  for (std::vector<OAuth2TokenService::Fetcher*>::iterator iter =
+           fetchers_to_cancel.begin();
+       iter != fetchers_to_cancel.end();
+       ++iter) {
+    (*iter)->Cancel();
+  }
+}
+
 void OAuth2TokenService::FireRefreshTokenAvailable(
     const std::string& account_id) {
   FOR_EACH_OBSERVER(Observer, observer_list_,
diff --git a/chrome/browser/signin/oauth2_token_service.h b/chrome/browser/signin/oauth2_token_service.h
index d979fcf..d63fbdf 100644
--- a/chrome/browser/signin/oauth2_token_service.h
+++ b/chrome/browser/signin/oauth2_token_service.h
@@ -117,6 +117,15 @@
   virtual scoped_ptr<Request> StartRequest(const ScopeSet& scopes,
                                            Consumer* consumer);
 
+  // This method does the same as |StartRequest| except it uses |client_id| and
+  // |client_secret| to identify OAuth client app instead of using
+  // Chrome's default values.
+  virtual scoped_ptr<Request> StartRequestForClient(
+      const std::string& client_id,
+      const std::string& client_secret,
+      const ScopeSet& scopes,
+      Consumer* consumer);
+
   // This method does the same as |StartRequest| except it uses the request
   // context given by |getter| instead of using the one returned by
   // |GetRequestContext| implemented by derived classes.
@@ -188,6 +197,12 @@
   // Clears the internal token cache.
   void ClearCache();
 
+  // Cancels all requests that are currently in progress.
+  void CancelAllRequests();
+
+  // Cancels all requests related to a given refresh token.
+  void CancelRequestsForToken(const std::string& refresh_token);
+
   // Called by subclasses to notify observers.
   void FireRefreshTokenAvailable(const std::string& account_id);
   void FireRefreshTokenRevoked(const std::string& account_id,
@@ -211,6 +226,16 @@
     base::Time expiration_date;
   };
 
+  // This method does the same as |StartRequestWithContext| except it
+  // uses |client_id| and |client_secret| to identify OAuth
+  // client app instead of using Chrome's default values.
+  scoped_ptr<Request> StartRequestForClientWithContext(
+      net::URLRequestContextGetter* getter,
+      const std::string& client_id,
+      const std::string& client_secret,
+      const ScopeSet& scopes,
+      Consumer* consumer);
+
   // Returns a currently valid OAuth2 access token for the given set of scopes,
   // or NULL if none have been cached. Note the user of this method should
   // ensure no entry with the same |scopes| is added before the usage of the
@@ -227,6 +252,9 @@
   // Called when |fetcher| finishes fetching.
   void OnFetchComplete(Fetcher* fetcher);
 
+  // Called when a number of fetchers need to be canceled.
+  void CancelFetchers(std::vector<Fetcher*> fetchers_to_cancel);
+
   // The cache of currently valid tokens.
   typedef std::map<ScopeSet, CacheEntry> TokenCache;
   TokenCache token_cache_;
diff --git a/chrome/browser/signin/oauth2_token_service_unittest.cc b/chrome/browser/signin/oauth2_token_service_unittest.cc
index ba68353..730c303 100644
--- a/chrome/browser/signin/oauth2_token_service_unittest.cc
+++ b/chrome/browser/signin/oauth2_token_service_unittest.cc
@@ -50,6 +50,12 @@
       : request_context_getter_(getter) {
   }
 
+  void CancelAllRequestsForTest() { CancelAllRequests(); }
+
+  void CancelRequestsForTokenForTest(const std::string& refresh_token) {
+    CancelRequestsForToken(refresh_token);
+  }
+
   // For testing: set the refresh token to be used.
   void set_refresh_token(const std::string& refresh_token) {
     refresh_token_ = refresh_token;
@@ -466,3 +472,54 @@
   EXPECT_EQ(0, consumer_.number_of_errors_);
   EXPECT_EQ("token2", consumer_.last_token_);
 }
+
+TEST_F(OAuth2TokenServiceTest, CancelAllRequests) {
+  oauth2_service_->set_refresh_token("refreshToken");
+  scoped_ptr<OAuth2TokenService::Request> request(
+      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+
+  oauth2_service_->set_refresh_token("refreshToken2");
+  scoped_ptr<OAuth2TokenService::Request> request2(
+      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+
+  oauth2_service_->CancelAllRequestsForTest();
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(2, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, CancelRequestsForToken) {
+  std::set<std::string> scope_set_1;
+  scope_set_1.insert("scope1");
+  scope_set_1.insert("scope2");
+  std::set<std::string> scope_set_2(scope_set_1.begin(), scope_set_1.end());
+  scope_set_2.insert("scope3");
+
+  oauth2_service_->set_refresh_token("refreshToken");
+  scoped_ptr<OAuth2TokenService::Request> request1(
+      oauth2_service_->StartRequest(scope_set_1, &consumer_));
+  scoped_ptr<OAuth2TokenService::Request> request2(
+      oauth2_service_->StartRequest(scope_set_2, &consumer_));
+
+  oauth2_service_->set_refresh_token("refreshToken2");
+  scoped_ptr<OAuth2TokenService::Request> request3(
+      oauth2_service_->StartRequest(scope_set_1, &consumer_));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+
+  oauth2_service_->CancelRequestsForTokenForTest("refreshToken");
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(2, consumer_.number_of_errors_);
+
+  oauth2_service_->CancelRequestsForTokenForTest("refreshToken2");
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(3, consumer_.number_of_errors_);
+}
diff --git a/chrome/browser/signin/profile_oauth2_token_service.cc b/chrome/browser/signin/profile_oauth2_token_service.cc
index 26b337f..439e5ec 100644
--- a/chrome/browser/signin/profile_oauth2_token_service.cc
+++ b/chrome/browser/signin/profile_oauth2_token_service.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/signin/token_service_factory.h"
 #include "chrome/browser/ui/global_error/global_error_service.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
+#include "chrome/browser/webdata/token_web_data.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_source.h"
@@ -26,6 +27,25 @@
 
 namespace {
 
+const char kAccountIdPrefix[] = "AccountId-";
+const size_t kAccountIdPrefixLength = 10;
+
+bool IsLegacyServiceId(const std::string& account_id) {
+  return account_id.compare(0u, kAccountIdPrefixLength, kAccountIdPrefix) != 0;
+}
+
+bool IsLegacyRefreshTokenId(const std::string& service_id) {
+  return service_id == GaiaConstants::kGaiaOAuth2LoginRefreshToken;
+}
+
+std::string ApplyAccountIdPrefix(const std::string& account_id) {
+  return kAccountIdPrefix + account_id;
+}
+
+std::string RemoveAccountIdPrefix(const std::string& prefixed_account_id) {
+  return prefixed_account_id.substr(kAccountIdPrefixLength);
+}
+
 std::string GetAccountId(Profile* profile) {
   SigninManagerBase* signin_manager =
       SigninManagerFactory::GetForProfileIfExists(profile);
@@ -37,6 +57,7 @@
 
 ProfileOAuth2TokenService::ProfileOAuth2TokenService()
     : profile_(NULL),
+      web_data_service_request_(0),
       last_auth_error_(GoogleServiceAuthError::NONE) {
 }
 
@@ -75,6 +96,7 @@
 }
 
 void ProfileOAuth2TokenService::Shutdown() {
+  CancelAllRequests();
   signin_global_error_->RemoveProvider(this);
   GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError(
       signin_global_error_.get());
@@ -113,6 +135,12 @@
           content::Details<TokenService::TokenAvailableDetails>(details).ptr();
       if (tok_details->service() ==
           GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
+        // TODO(fgorski): Canceling all requests will not be correct in a
+        // multi-login environment. We should cancel only the requests related
+        // to the token being replaced (old token for the same account_id).
+        // Previous refresh token is not available at this point, but since
+        // there are no other refresh tokens, we cancel all active requests.
+        CancelAllRequests();
         ClearCache();
         UpdateAuthError(GoogleServiceAuthError::AuthErrorNone());
         FireRefreshTokenAvailable(GetAccountId(profile_));
@@ -126,6 +154,12 @@
       if (tok_details->service() == GaiaConstants::kLSOService ||
           tok_details->service() ==
               GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
+        // TODO(fgorski): Canceling all requests will not be correct in a
+        // multi-login environment. We should cacnel only the requests related
+        // to the failed refresh token.
+        // Failed refresh token is not available at this point, but since
+        // there are no other refresh tokens, we cancel all active requests.
+        CancelAllRequests();
         ClearCache();
         UpdateAuthError(tok_details->error());
         FireRefreshTokenRevoked(GetAccountId(profile_), tok_details->error());
@@ -133,6 +167,7 @@
       break;
     }
     case chrome::NOTIFICATION_TOKENS_CLEARED: {
+      CancelAllRequests();
       ClearCache();
       UpdateAuthError(GoogleServiceAuthError::AuthErrorNone());
       FireRefreshTokensCleared();
@@ -181,3 +216,133 @@
   }
   return true;
 }
+
+void ProfileOAuth2TokenService::UpdateCredentials(
+    const std::string& account_id,
+    const std::string& refresh_token) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK(!refresh_token.empty());
+
+  bool refresh_token_present = refresh_tokens_.count(account_id) > 0;
+  if (!refresh_token_present ||
+      refresh_tokens_[account_id] != refresh_token) {
+    // If token present, and different from the new one, cancel its requests.
+    if (refresh_token_present)
+      CancelRequestsForToken(refresh_tokens_[account_id]);
+
+    // Save the token in memory and in persistent store.
+    refresh_tokens_[account_id] = refresh_token;
+    scoped_refptr<TokenWebData> token_web_data =
+        TokenWebData::FromBrowserContext(profile_);
+    if (token_web_data.get())
+      token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id),
+                                         refresh_token);
+
+    FireRefreshTokenAvailable(account_id);
+    // TODO(fgorski): Notify diagnostic observers.
+  }
+}
+
+void ProfileOAuth2TokenService::RevokeCredentials(
+    const std::string& account_id) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  if (refresh_tokens_.count(account_id) > 0) {
+    CancelRequestsForToken(refresh_tokens_[account_id]);
+    refresh_tokens_.erase(account_id);
+    scoped_refptr<TokenWebData> token_web_data =
+        TokenWebData::FromBrowserContext(profile_);
+    if (token_web_data.get())
+      token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id));
+    FireRefreshTokenRevoked(account_id,
+        GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
+
+    // TODO(fgorski): Notify diagnostic observers.
+  }
+}
+
+void ProfileOAuth2TokenService::RevokeAllCredentials() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  CancelAllRequests();
+  GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
+  for (std::map<std::string, std::string>::const_iterator iter =
+           refresh_tokens_.begin();
+       iter != refresh_tokens_.end();
+       ++iter) {
+    FireRefreshTokenRevoked(iter->first, error);
+  }
+  refresh_tokens_.clear();
+
+  scoped_refptr<TokenWebData> token_web_data =
+      TokenWebData::FromBrowserContext(profile_);
+  if (token_web_data.get())
+    token_web_data->RemoveAllTokens();
+  FireRefreshTokensCleared();
+
+  // TODO(fgorski): Notify diagnostic observers.
+}
+
+void ProfileOAuth2TokenService::LoadCredentials() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_EQ(0, web_data_service_request_);
+
+  CancelAllRequests();
+  refresh_tokens_.clear();
+  scoped_refptr<TokenWebData> token_web_data =
+      TokenWebData::FromBrowserContext(profile_);
+  if (token_web_data.get())
+    web_data_service_request_ = token_web_data->GetAllTokens(this);
+}
+
+void ProfileOAuth2TokenService::OnWebDataServiceRequestDone(
+    WebDataServiceBase::Handle handle,
+    const WDTypedResult* result) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  DCHECK_EQ(web_data_service_request_, handle);
+  web_data_service_request_ = 0;
+
+  if (result) {
+    DCHECK(result->GetType() == TOKEN_RESULT);
+    const WDResult<std::map<std::string, std::string> > * token_result =
+        static_cast<const WDResult<std::map<std::string, std::string> > * > (
+            result);
+    LoadAllCredentialsIntoMemory(token_result->GetValue());
+  }
+}
+
+void ProfileOAuth2TokenService::LoadAllCredentialsIntoMemory(
+    const std::map<std::string, std::string>& db_tokens) {
+  std::string old_login_token;
+
+  for (std::map<std::string, std::string>::const_iterator iter =
+           db_tokens.begin();
+       iter != db_tokens.end();
+       ++iter) {
+    std::string prefixed_account_id = iter->first;
+    std::string refresh_token = iter->second;
+
+    if (IsLegacyRefreshTokenId(prefixed_account_id) && !refresh_token.empty())
+      old_login_token = refresh_token;
+
+    if (IsLegacyServiceId(prefixed_account_id)) {
+      scoped_refptr<TokenWebData> token_web_data =
+          TokenWebData::FromBrowserContext(profile_);
+      if (token_web_data.get())
+        token_web_data->RemoveTokenForService(prefixed_account_id);
+    } else {
+      DCHECK(!refresh_token.empty());
+      std::string account_id = RemoveAccountIdPrefix(prefixed_account_id);
+      refresh_tokens_[account_id] = refresh_token;
+      FireRefreshTokenAvailable(account_id);
+      // TODO(fgorski): Notify diagnostic observers.
+    }
+  }
+
+  if (!old_login_token.empty() &&
+      refresh_tokens_.count(GetAccountId(profile_)) == 0) {
+    UpdateCredentials(GetAccountId(profile_), old_login_token);
+  }
+
+  FireRefreshTokensLoaded();
+}
diff --git a/chrome/browser/signin/profile_oauth2_token_service.h b/chrome/browser/signin/profile_oauth2_token_service.h
index 28ee5d5..013cc06 100644
--- a/chrome/browser/signin/profile_oauth2_token_service.h
+++ b/chrome/browser/signin/profile_oauth2_token_service.h
@@ -11,6 +11,7 @@
 #include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/signin_global_error.h"
 #include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+#include "components/webdata/common/web_data_service_consumer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 
@@ -40,7 +41,8 @@
 class ProfileOAuth2TokenService : public OAuth2TokenService,
                                   public content::NotificationObserver,
                                   public SigninGlobalError::AuthStatusProvider,
-                                  public BrowserContextKeyedService {
+                                  public BrowserContextKeyedService,
+                                  public WebDataServiceConsumer {
  public:
   // content::NotificationObserver listening for TokenService updates.
   virtual void Observe(int type,
@@ -63,6 +65,17 @@
   bool ShouldCacheForRefreshToken(TokenService *token_service,
                                   const std::string& refresh_token);
 
+  // Updates a |refresh_token| for an |account_id|. Credentials are persisted,
+  // and avialable through |LoadCredentials| after service is restarted.
+  void UpdateCredentials(const std::string& account_id,
+                         const std::string& refresh_token);
+
+  // Revokes credentials related to |account_id|.
+  void RevokeCredentials(const std::string& account_id);
+
+  // Revokes all credentials handled by the object.
+  void RevokeAllCredentials();
+
   SigninGlobalError* signin_global_error() {
     return signin_global_error_.get();
   }
@@ -100,10 +113,37 @@
                            StaleRefreshTokensNotCached);
   FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceTest,
                            TokenServiceUpdateClearsCache);
+  FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceTest,
+                           PersistenceDBUpgrade);
+  FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceTest,
+                           PersistenceLoadCredentials);
+
+  // WebDataServiceConsumer implementation:
+  virtual void OnWebDataServiceRequestDone(
+      WebDataServiceBase::Handle handle,
+      const WDTypedResult* result) OVERRIDE;
+
+  // Loads credentials from a backing persistent store to make them available
+  // after service is used between profile restarts.
+  void LoadCredentials();
+
+  // Loads credentials into in memory stucture.
+  void LoadAllCredentialsIntoMemory(
+      const std::map<std::string, std::string>& db_tokens);
+
+  // Loads a single pair of |account_id|, |refresh_token| into memory.
+  void LoadCredentialsIntoMemory(const std::string& account_id,
+                                 const std::string& refresh_token);
 
   // The profile with which this instance was initialized, or NULL.
   Profile* profile_;
 
+  // Handle to the request reading tokens from database.
+  WebDataServiceBase::Handle web_data_service_request_;
+
+  // In memory refresh token store mapping account_id to refresh_token.
+  std::map<std::string, std::string> refresh_tokens_;
+
   // Used to show auth errors in the wrench menu. The SigninGlobalError is
   // different than most GlobalErrors in that its lifetime is controlled by
   // ProfileOAuth2TokenService (so we can expose a reference for use in the
diff --git a/chrome/browser/signin/profile_oauth2_token_service_unittest.cc b/chrome/browser/signin/profile_oauth2_token_service_unittest.cc
index 0cbb571..78dece9 100644
--- a/chrome/browser/signin/profile_oauth2_token_service_unittest.cc
+++ b/chrome/browser/signin/profile_oauth2_token_service_unittest.cc
@@ -124,6 +124,157 @@
   ExpectOneTokensClearedNotification();
 }
 
+TEST_F(ProfileOAuth2TokenServiceTest, PersistenceDBUpgrade) {
+  // No username for profile in unit tests, defaulting to empty string.
+  std::string main_account_id;
+  std::string main_refresh_token("old_refresh_token");
+
+  // Populate DB with legacy tokens.
+  service()->OnIssueAuthTokenSuccess(GaiaConstants::kSyncService,
+                                     "syncServiceToken");
+  service()->OnIssueAuthTokenSuccess(GaiaConstants::kLSOService,
+                                     "lsoToken");
+  service()->OnIssueAuthTokenSuccess(
+      GaiaConstants::kGaiaOAuth2LoginRefreshToken,
+      main_refresh_token);
+  // Add a token using the new API.
+  ResetObserverCounts();
+
+  // Force LoadCredentials.
+  oauth2_service_->LoadCredentials();
+  base::RunLoop().RunUntilIdle();
+
+  // Legacy tokens get discarded, but the old refresh token is kept.
+  EXPECT_EQ(1, tokens_loaded_count_);
+  EXPECT_EQ(1, token_available_count_);
+  EXPECT_TRUE(oauth2_service_->RefreshTokenIsAvailable());
+  EXPECT_EQ(1U, oauth2_service_->refresh_tokens_.size());
+  EXPECT_EQ(main_refresh_token,
+            oauth2_service_->refresh_tokens_[main_account_id]);
+
+  // Add an old legacy token to the DB, to ensure it will not overwrite existing
+  // credentials for main account.
+  service()->OnIssueAuthTokenSuccess(
+      GaiaConstants::kGaiaOAuth2LoginRefreshToken,
+      "secondOldRefreshToken");
+  // Add some other legacy token. (Expected to get discarded).
+  service()->OnIssueAuthTokenSuccess(GaiaConstants::kLSOService,
+                                     "lsoToken");
+  // Also add a token using PO2TS.UpdateCredentials and make sure upgrade does
+  // not wipe it.
+  std::string other_account_id("other_account_id");
+  std::string other_refresh_token("other_refresh_token");
+  oauth2_service_->UpdateCredentials(other_account_id, other_refresh_token);
+  ResetObserverCounts();
+
+  // Force LoadCredentials.
+  oauth2_service_->LoadCredentials();
+  base::RunLoop().RunUntilIdle();
+
+  // Again legacy tokens get discarded, but since the main porfile account
+  // token is present it is not overwritten.
+  EXPECT_EQ(2, token_available_count_);
+  EXPECT_EQ(1, tokens_loaded_count_);
+  EXPECT_TRUE(oauth2_service_->RefreshTokenIsAvailable());
+  EXPECT_EQ(2U, oauth2_service_->refresh_tokens_.size());
+  EXPECT_EQ(main_refresh_token,
+            oauth2_service_->refresh_tokens_[main_account_id]);
+  EXPECT_EQ(other_refresh_token,
+            oauth2_service_->refresh_tokens_[other_account_id]);
+
+  oauth2_service_->RevokeAllCredentials();
+}
+
+TEST_F(ProfileOAuth2TokenServiceTest, PersistenceRevokeCredentials) {
+  std::string account_id_1 = "account_id_1";
+  std::string refresh_token_1 = "refresh_token_1";
+  std::string account_id_2 = "account_id_2";
+  std::string refresh_token_2 = "refresh_token_2";
+
+  // TODO(fgorski): Enable below when implemented:
+  // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1));
+  // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_2));
+
+  oauth2_service_->UpdateCredentials(account_id_1, refresh_token_1);
+  oauth2_service_->UpdateCredentials(account_id_2, refresh_token_2);
+
+  // TODO(fgorski): Enable below when implemented:
+  // EXPECT_TRUE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1));
+  // EXPECT_TRUE(oauth2_service_->RefreshTokenIsAvailable(account_id_2));
+
+  ResetObserverCounts();
+  oauth2_service_->RevokeCredentials(account_id_1);
+  ExpectOneTokenRevokedNotification();
+
+  // TODO(fgorski): Enable below when implemented:
+  // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1));
+  // EXPECT_TRUE(oauth2_service_->RefreshTokenIsAvailable(account_id_2));
+
+  oauth2_service_->RevokeAllCredentials();
+  EXPECT_EQ(0, token_available_count_);
+  EXPECT_EQ(1, token_revoked_count_);
+  EXPECT_EQ(0, tokens_loaded_count_);
+  EXPECT_EQ(1, tokens_cleared_count_);
+  ResetObserverCounts();
+}
+
+TEST_F(ProfileOAuth2TokenServiceTest, PersistenceLoadCredentials) {
+  // Ensure DB is clean.
+  oauth2_service_->RevokeAllCredentials();
+  ExpectOneTokensClearedNotification();
+  // Perform a load from an empty DB.
+  oauth2_service_->LoadCredentials();
+  base::RunLoop().RunUntilIdle();
+  ExpectOneTokensLoadedNotification();
+  EXPECT_EQ(0U, oauth2_service_->refresh_tokens_.size());
+  // Setup a DB with tokens that don't require upgrade and clear memory.
+  oauth2_service_->UpdateCredentials("account_id", "refresh_token");
+  oauth2_service_->UpdateCredentials("account_id2", "refresh_token2");
+  oauth2_service_->refresh_tokens_.clear();
+  ResetObserverCounts();
+
+  oauth2_service_->LoadCredentials();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, token_available_count_);
+  EXPECT_EQ(0, token_revoked_count_);
+  EXPECT_EQ(1, tokens_loaded_count_);
+  EXPECT_EQ(0, tokens_cleared_count_);
+  ResetObserverCounts();
+
+  // TODO(fgorski): Enable below when implemented:
+  // EXPECT_TRUE(oauth2_servive_->RefreshTokenIsAvailable("account_id"));
+  // EXPECT_TRUE(oauth2_service_->RefreshTokenIsAvailable("account_id2"));
+
+  oauth2_service_->RevokeAllCredentials();
+  EXPECT_EQ(0, token_available_count_);
+  EXPECT_EQ(2, token_revoked_count_);
+  EXPECT_EQ(0, tokens_loaded_count_);
+  EXPECT_EQ(1, tokens_cleared_count_);
+  ResetObserverCounts();
+}
+
+TEST_F(ProfileOAuth2TokenServiceTest, PersistanceNotifications) {
+  EXPECT_EQ(0, oauth2_service_->cache_size_for_testing());
+  oauth2_service_->UpdateCredentials("account_id", "refresh_token");
+  ExpectOneTokenAvailableNotification();
+
+  oauth2_service_->UpdateCredentials("account_id", "refresh_token");
+  ExpectNoNotifications();
+
+  oauth2_service_->UpdateCredentials("account_id", "refresh_token2");
+  ExpectOneTokenAvailableNotification();
+
+  oauth2_service_->RevokeCredentials("account_id");
+  ExpectOneTokenRevokedNotification();
+
+  oauth2_service_->UpdateCredentials("account_id", "refresh_token2");
+  ExpectOneTokenAvailableNotification();
+
+  oauth2_service_->RevokeAllCredentials();
+  EXPECT_EQ(1, tokens_cleared_count_);
+  ResetObserverCounts();
+}
+
 // Until the TokenService class is removed, problems fetching the LSO token
 // should translate to problems fetching the oauth2 refresh token.
 TEST_F(ProfileOAuth2TokenServiceTest, LsoNotification) {
diff --git a/chrome/browser/signin/signin_browsertest.cc b/chrome/browser/signin/signin_browsertest.cc
index 4cfde8d..aef931c 100644
--- a/chrome/browser/signin/signin_browsertest.cc
+++ b/chrome/browser/signin/signin_browsertest.cc
@@ -8,9 +8,9 @@
 #include "base/command_line.h"
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/singleton_tabs.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
@@ -104,16 +104,16 @@
       browser()->profile());
   EXPECT_FALSE(signin->HasSigninProcess());
 
-  ui_test_utils::NavigateToURL(browser(), SyncPromoUI::GetSyncPromoURL(
-      SyncPromoUI::SOURCE_NTP_LINK, true));
+  ui_test_utils::NavigateToURL(browser(), signin::GetPromoURL(
+      signin::SOURCE_NTP_LINK, true));
   EXPECT_EQ(kOneClickSigninEnabled, signin->HasSigninProcess());
 
   // Navigating away should change the process.
   ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
   EXPECT_FALSE(signin->HasSigninProcess());
 
-  ui_test_utils::NavigateToURL(browser(), SyncPromoUI::GetSyncPromoURL(
-      SyncPromoUI::SOURCE_NTP_LINK, true));
+  ui_test_utils::NavigateToURL(browser(), signin::GetPromoURL(
+      signin::SOURCE_NTP_LINK, true));
   EXPECT_EQ(kOneClickSigninEnabled, signin->HasSigninProcess());
 
   content::WebContents* active_tab =
@@ -128,8 +128,7 @@
   // shouldn't change anything.
   chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams(
       browser(),
-      GURL(SyncPromoUI::GetSyncPromoURL(SyncPromoUI::SOURCE_NTP_LINK,
-                                        false))));
+      GURL(signin::GetPromoURL(signin::SOURCE_NTP_LINK, false))));
   params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
   ShowSingletonTabOverwritingNTP(browser(), params);
   EXPECT_EQ(active_tab, browser()->tab_strip_model()->GetActiveWebContents());
@@ -147,7 +146,7 @@
       browser()->profile());
   EXPECT_FALSE(signin->HasSigninProcess());
 
-  GURL url = SyncPromoUI::GetSyncPromoURL(SyncPromoUI::SOURCE_NTP_LINK, true);
+  GURL url = signin::GetPromoURL(signin::SOURCE_NTP_LINK, true);
   ui_test_utils::NavigateToURL(browser(), url);
   EXPECT_EQ(kOneClickSigninEnabled, signin->HasSigninProcess());
 
@@ -195,9 +194,8 @@
 // DidStopLoading of the NTP.
 IN_PROC_BROWSER_TEST_F(SigninBrowserTest, SigninSkipForNowAndGoBack) {
   GURL ntp_url(chrome::kChromeUINewTabURL);
-  GURL start_url =
-      SyncPromoUI::GetSyncPromoURL(SyncPromoUI::SOURCE_START_PAGE, true);
-  GURL skip_url(SyncPromoUI::GetSyncLandingURL("ntp", 1));
+  GURL start_url = signin::GetPromoURL(signin::SOURCE_START_PAGE, true);
+  GURL skip_url = signin::GetLandingURL("ntp", 1);
 
   SigninManager* signin = SigninManagerFactory::GetForProfile(
       browser()->profile());
diff --git a/chrome/browser/signin/signin_promo.cc b/chrome/browser/signin/signin_promo.cc
new file mode 100644
index 0000000..311e860
--- /dev/null
+++ b/chrome/browser/signin/signin_promo.cc
@@ -0,0 +1,286 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/signin/signin_promo.h"
+
+#include "base/command_line.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/first_run/first_run.h"
+#include "chrome/browser/google/google_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_info_cache.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/signin/signin_manager.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/webui/options/core_options_handler.h"
+#include "chrome/browser/ui/webui/sync_promo/sync_promo_trial.h"
+#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/net/url_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "components/user_prefs/pref_registry_syncable.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "grit/browser_resources.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "net/base/escape.h"
+#include "net/base/network_change_notifier.h"
+#include "net/base/url_util.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using content::WebContents;
+
+namespace {
+
+const char kStringsJsFile[] = "strings.js";
+const char kSignInPromoJsFile[] = "sync_promo.js";
+
+const char kSignInPromoQueryKeyAutoClose[] = "auto_close";
+const char kSignInPromoQueryKeyContinue[] = "continue";
+const char kSignInPromoQueryKeySource[] = "source";
+
+// Gaia cannot support about:blank as a continue URL, so using a hosted blank
+// page instead.
+const char kSignInLandingUrlPrefix[] =
+    "https://www.google.com/intl/%s/chrome/blank.html";
+
+// The maximum number of times we want to show the sign in promo at startup.
+const int kSignInPromoShowAtStartupMaximum = 10;
+
+// Forces the web based signin flow when set.
+bool g_force_web_based_signin_flow = false;
+
+// Checks we want to show the sign in promo for the given brand.
+bool AllowPromoAtStartupForCurrentBrand() {
+  std::string brand;
+  google_util::GetBrand(&brand);
+
+  if (brand.empty())
+    return true;
+
+  if (google_util::IsInternetCafeBrandCode(brand))
+    return false;
+
+  // Enable for both organic and distribution.
+  return true;
+}
+
+// Returns true if a user has seen the sign in promo at startup previously.
+bool HasShownPromoAtStartup(Profile* profile) {
+  return profile->GetPrefs()->HasPrefPath(prefs::kSyncPromoStartupCount);
+}
+
+// Returns true if the user has previously skipped the sign in promo.
+bool HasUserSkippedPromo(Profile* profile) {
+  return profile->GetPrefs()->GetBoolean(prefs::kSyncPromoUserSkipped);
+}
+
+}  // namespace
+
+namespace signin {
+
+bool ShouldShowPromo(Profile* profile) {
+#if defined(OS_CHROMEOS)
+  // There's no need to show the sign in promo on cros since cros users are
+  // already logged in.
+  return false;
+#else
+
+  // Don't bother if we don't have any kind of network connection.
+  if (net::NetworkChangeNotifier::IsOffline())
+    return false;
+
+  // Don't show for managed profiles.
+  if (profile->GetPrefs()->GetBoolean(prefs::kProfileIsManaged))
+    return false;
+
+  // Display the signin promo if the user is not signed in.
+  SigninManager* signin = SigninManagerFactory::GetForProfile(
+      profile->GetOriginalProfile());
+  return !signin->AuthInProgress() && signin->IsSigninAllowed() &&
+      signin->GetAuthenticatedUsername().empty();
+#endif
+}
+
+bool ShouldShowPromoAtStartup(Profile* profile, bool is_new_profile) {
+  DCHECK(profile);
+
+  // Don't show if the profile is an incognito.
+  if (profile->IsOffTheRecord())
+    return false;
+
+  if (!ShouldShowPromo(profile))
+    return false;
+
+  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+  if (command_line.HasSwitch(switches::kNoFirstRun))
+    is_new_profile = false;
+
+  if (!is_new_profile) {
+    if (!HasShownPromoAtStartup(profile))
+      return false;
+  }
+
+  if (HasUserSkippedPromo(profile))
+    return false;
+
+  // For Chinese users skip the sign in promo.
+  if (g_browser_process->GetApplicationLocale() == "zh-CN")
+    return false;
+
+  PrefService* prefs = profile->GetPrefs();
+  int show_count = prefs->GetInteger(prefs::kSyncPromoStartupCount);
+  if (show_count >= kSignInPromoShowAtStartupMaximum)
+    return false;
+
+  // This pref can be set in the master preferences file to allow or disallow
+  // showing the sign in promo at startup.
+  if (prefs->HasPrefPath(prefs::kSyncPromoShowOnFirstRunAllowed))
+    return prefs->GetBoolean(prefs::kSyncPromoShowOnFirstRunAllowed);
+
+  // For now don't show the promo for some brands.
+  if (!AllowPromoAtStartupForCurrentBrand())
+    return false;
+
+  // Default to show the promo for Google Chrome builds.
+#if defined(GOOGLE_CHROME_BUILD)
+  return true;
+#else
+  return false;
+#endif
+}
+
+void DidShowPromoAtStartup(Profile* profile) {
+  int show_count = profile->GetPrefs()->GetInteger(
+      prefs::kSyncPromoStartupCount);
+  show_count++;
+  profile->GetPrefs()->SetInteger(prefs::kSyncPromoStartupCount, show_count);
+}
+
+void SetUserSkippedPromo(Profile* profile) {
+  profile->GetPrefs()->SetBoolean(prefs::kSyncPromoUserSkipped, true);
+}
+
+GURL GetLandingURL(const char* option, int value) {
+  const std::string& locale = g_browser_process->GetApplicationLocale();
+  std::string url = base::StringPrintf(kSignInLandingUrlPrefix, locale.c_str());
+  base::StringAppendF(&url, "?%s=%d", option, value);
+  return GURL(url);
+}
+
+GURL GetPromoURL(Source source, bool auto_close) {
+  DCHECK_NE(SOURCE_UNKNOWN, source);
+
+  std::string url_string;
+
+  // Build a Gaia-based URL that can be used to sign the user into chrome.
+  // There are required request parameters:
+  //
+  //  - tell Gaia which service the user is signing into.  In this case,
+  //    a chrome sign in uses the service "chromiumsync"
+  //  - provide a continue URL.  This is the URL that Gaia will redirect to
+  //    once the sign is complete.
+  //
+  // The continue URL includes a source parameter that can be extracted using
+  // the function GetSourceForSignInPromoURL() below.  This is used to know
+  // which of the chrome sign in access points was used to sign the user in.
+  // It is also parsed for the |auto_close| flag, which indicates that the tab
+  // must be closed after sync setup is successful.
+  // See OneClickSigninHelper for details.
+  url_string = GaiaUrls::GetInstance()->service_login_url();
+  url_string.append("?service=chromiumsync&sarp=1");
+
+  std::string continue_url = GetLandingURL(kSignInPromoQueryKeySource,
+                                           static_cast<int>(source)).spec();
+  if (auto_close)
+    base::StringAppendF(&continue_url, "&%s=1", kSignInPromoQueryKeyAutoClose);
+
+  base::StringAppendF(&url_string, "&%s=%s", kSignInPromoQueryKeyContinue,
+                      net::EscapeQueryParamValue(
+                          continue_url, false).c_str());
+
+  return GURL(url_string);
+}
+
+GURL GetNextPageURLForPromoURL(const GURL& url) {
+  std::string value;
+  if (net::GetValueForKeyInQuery(url, kSignInPromoQueryKeyContinue, &value))
+    return GURL(value);
+
+  return GURL();
+}
+
+Source GetSourceForPromoURL(const GURL& url) {
+  std::string value;
+  if (net::GetValueForKeyInQuery(url, kSignInPromoQueryKeySource, &value)) {
+    int source = 0;
+    if (base::StringToInt(value, &source) && source >= SOURCE_START_PAGE &&
+        source < SOURCE_UNKNOWN) {
+      return static_cast<Source>(source);
+    }
+  }
+  return SOURCE_UNKNOWN;
+}
+
+bool IsAutoCloseEnabledInURL(const GURL& url) {
+  std::string value;
+  if (net::GetValueForKeyInQuery(url, kSignInPromoQueryKeyAutoClose, &value)) {
+    int enabled = 0;
+    if (base::StringToInt(value, &enabled) && enabled == 1)
+      return true;
+  }
+  return false;
+}
+
+bool IsContinueUrlForWebBasedSigninFlow(const GURL& url) {
+  GURL::Replacements replacements;
+  replacements.ClearQuery();
+  const std::string& locale = g_browser_process->GetApplicationLocale();
+  return url.ReplaceComponents(replacements) ==
+      GURL(base::StringPrintf(kSignInLandingUrlPrefix, locale.c_str()));
+}
+
+void ForceWebBasedSigninFlowForTesting(bool force) {
+  g_force_web_based_signin_flow = force;
+}
+
+void RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  // TODO(fdoray): Rename these preferences to start with kSignInPromo.
+  // (crbug.com/264283)
+  registry->RegisterIntegerPref(
+      prefs::kSyncPromoStartupCount,
+      0,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+  registry->RegisterBooleanPref(
+      prefs::kSyncPromoUserSkipped,
+      false,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+  registry->RegisterBooleanPref(
+      prefs::kSyncPromoShowOnFirstRunAllowed,
+      true,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+  registry->RegisterBooleanPref(
+      prefs::kSyncPromoShowNTPBubble,
+      false,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+  registry->RegisterStringPref(
+      prefs::kSyncPromoErrorMessage,
+      std::string(),
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+}
+
+}  // namespace signin
diff --git a/chrome/browser/signin/signin_promo.h b/chrome/browser/signin/signin_promo.h
new file mode 100644
index 0000000..9005f42
--- /dev/null
+++ b/chrome/browser/signin/signin_promo.h
@@ -0,0 +1,82 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_SIGNIN_SIGNIN_PROMO_H_
+#define CHROME_BROWSER_SIGNIN_SIGNIN_PROMO_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+class GURL;
+class Profile;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+// Utility functions for sign in promos.
+namespace signin {
+
+// Please keep this in sync with enums in sync_promo_trial.cc.
+enum Source {
+  SOURCE_START_PAGE = 0, // This must be first.
+  SOURCE_NTP_LINK,
+  SOURCE_MENU,
+  SOURCE_SETTINGS,
+  SOURCE_EXTENSION_INSTALL_BUBBLE,
+  SOURCE_WEBSTORE_INSTALL,
+  SOURCE_APP_LAUNCHER,
+  SOURCE_APPS_PAGE_LINK,
+  SOURCE_BOOKMARK_BUBBLE,
+  SOURCE_UNKNOWN, // This must be last.
+};
+
+// Returns true if the sign in promo should be visible.
+// |profile| is the profile of the tab the promo would be shown on.
+bool ShouldShowPromo(Profile* profile);
+
+// Returns true if we should show the sign in promo at startup.
+bool ShouldShowPromoAtStartup(Profile* profile, bool is_new_profile);
+
+// Called when the sign in promo has been shown so that we can keep track
+// of the number of times we've displayed it.
+void DidShowPromoAtStartup(Profile* profile);
+
+// Registers the fact that the user has skipped the sign in promo.
+void SetUserSkippedPromo(Profile* profile);
+
+// Gets the sign in landing page URL.
+GURL GetLandingURL(const char* option, int value);
+
+// Returns the sign in promo URL wth the given arguments in the query.
+// |source| identifies from where the sign in promo is being called, and is
+// used to record sync promo UMA stats in the context of the source.
+// |auto_close| whether to close the sign in promo automatically when done.
+GURL GetPromoURL(Source source, bool auto_close);
+
+// Gets the next page URL from the query portion of the sign in promo URL.
+GURL GetNextPageURLForPromoURL(const GURL& url);
+
+// Gets the source from the query portion of the sign in promo URL.
+// The source identifies from where the sign in promo was opened.
+Source GetSourceForPromoURL(const GURL& url);
+
+// Returns true if the auto_close parameter in the given URL is set to true.
+bool IsAutoCloseEnabledInURL(const GURL& url);
+
+// Returns true if the given URL is the standard continue URL used with the
+// sync promo when the web-based flow is enabled.  The query parameters
+// of the URL are ignored for this comparison.
+bool IsContinueUrlForWebBasedSigninFlow(const GURL& url);
+
+// Forces UseWebBasedSigninFlow() to return true when set; used in tests only.
+void ForceWebBasedSigninFlowForTesting(bool force);
+
+// Registers the preferences the Sign In Promo needs.
+void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+}  // namespace signin
+
+#endif  // CHROME_BROWSER_SIGNIN_SIGNIN_PROMO_H_
diff --git a/chrome/browser/signin/signin_ui_util.cc b/chrome/browser/signin/signin_ui_util.cc
index 1c75cc6..e3bb8ba 100644
--- a/chrome/browser/signin/signin_ui_util.cc
+++ b/chrome/browser/signin/signin_ui_util.cc
@@ -126,14 +126,16 @@
         link_label->clear();
       break;
     case GoogleServiceAuthError::CONNECTION_FAILED:
-      // Note that there is little the user can do if the server is not
-      // reachable. Since attempting to re-connect is done automatically by
-      // the Syncer, we do not show the (re)login link.
       if (status_label) {
         status_label->assign(
             l10n_util::GetStringFUTF16(IDS_SYNC_SERVER_IS_UNREACHABLE,
                                        product_name));
       }
+      // Note that there is little the user can do if the server is not
+      // reachable. Since attempting to re-connect is done automatically by
+      // the Syncer, we do not show the (re)login link.
+      if (link_label)
+        link_label->clear();
       break;
     default:
       if (status_label) {
diff --git a/chrome/browser/signin/ubertoken_fetcher_unittest.cc b/chrome/browser/signin/ubertoken_fetcher_unittest.cc
index 90b1d78..569025a 100644
--- a/chrome/browser/signin/ubertoken_fetcher_unittest.cc
+++ b/chrome/browser/signin/ubertoken_fetcher_unittest.cc
@@ -64,7 +64,6 @@
  public:
   virtual void SetUp() OVERRIDE {
     TokenServiceTestHarness::SetUp();
-    profile()->CreateRequestContext(NULL);
 
     ProfileOAuth2TokenServiceFactory::GetInstance()->
         SetTestingFactoryAndUse(profile(), Build);
diff --git a/chrome/browser/ssl/OWNERS b/chrome/browser/ssl/OWNERS
index cf00f71..dde6828 100644
--- a/chrome/browser/ssl/OWNERS
+++ b/chrome/browser/ssl/OWNERS
@@ -1 +1,4 @@
-abarth@chromium.org
+agl@chromium.org
+palmer@chromium.org
+rsleevi@chromium.org
+wtc@chromium.org
diff --git a/chrome/browser/ssl/ssl_blocking_page.cc b/chrome/browser/ssl/ssl_blocking_page.cc
index 5b45b35..33ef570 100644
--- a/chrome/browser/ssl/ssl_blocking_page.cc
+++ b/chrome/browser/ssl/ssl_blocking_page.cc
@@ -29,6 +29,7 @@
 #include "grit/browser_resources.h"
 #include "grit/generated_resources.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/webui/jstemplate_builder.h"
@@ -37,26 +38,11 @@
 #include "base/win/windows_version.h"
 #endif
 
-using base::TimeDelta;
 using base::TimeTicks;
 using content::InterstitialPage;
 using content::NavigationController;
 using content::NavigationEntry;
 
-#define HISTOGRAM_INTERSTITIAL_SMALL_TIME(name, sample) \
-    UMA_HISTOGRAM_CUSTOM_TIMES( \
-        name, \
-        sample, \
-        base::TimeDelta::FromMilliseconds(400), \
-        base::TimeDelta::FromMinutes(15), 75);
-
-#define HISTOGRAM_INTERSTITIAL_LARGE_TIME(name, sample) \
-    UMA_HISTOGRAM_CUSTOM_TIMES( \
-        name, \
-        sample, \
-        base::TimeDelta::FromMilliseconds(400), \
-        base::TimeDelta::FromMinutes(20), 50);
-
 namespace {
 
 // These represent the commands sent by ssl_roadblock.html.
@@ -82,6 +68,8 @@
   DONT_PROCEED_AUTHORITY,
   MORE,
   SHOW_UNDERSTAND,
+  SHOW_INTERNAL_HOSTNAME,
+  PROCEED_INTERNAL_HOSTNAME,
   UNUSED_BLOCKING_PAGE_EVENT,
 };
 
@@ -91,35 +79,30 @@
                             UNUSED_BLOCKING_PAGE_EVENT);
 }
 
-void RecordSSLBlockingPageTimeStats(
+void RecordSSLBlockingPageDetailedStats(
     bool proceed,
     int cert_error,
     bool overridable,
-    const base::TimeTicks& start_time,
-    const base::TimeTicks& end_time) {
+    bool internal,
+    const base::TimeTicks& start_time) {
   UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_type",
      SSLErrorInfo::NetErrorToErrorType(cert_error), SSLErrorInfo::END_OF_ENUM);
   if (start_time.is_null() || !overridable) {
-    // A null start time will occur if the page never came into focus and the
-    // user quit without seeing it. If so, we don't record the time.
-    // The user might not have an option except to turn back; that happens
-    // if overridable is true.  If so, the time/outcome isn't meaningful.
+    // A null start time will occur if the page never came into focus.
+    // Overridable is false if the user didn't have any option except to turn
+    // back. In either case, we don't want to record some of our metrics.
     return;
   }
-  base::TimeDelta delta = end_time - start_time;
   if (proceed) {
     RecordSSLBlockingPageEventStats(PROCEED_OVERRIDABLE);
-    HISTOGRAM_INTERSTITIAL_LARGE_TIME("interstitial.ssl_accept_time", delta);
+    if (internal)
+      RecordSSLBlockingPageEventStats(PROCEED_INTERNAL_HOSTNAME);
   } else if (!proceed) {
     RecordSSLBlockingPageEventStats(DONT_PROCEED_OVERRIDABLE);
-    HISTOGRAM_INTERSTITIAL_LARGE_TIME("interstitial.ssl_reject_time", delta);
   }
   SSLErrorInfo::ErrorType type = SSLErrorInfo::NetErrorToErrorType(cert_error);
   switch (type) {
     case SSLErrorInfo::CERT_COMMON_NAME_INVALID: {
-      HISTOGRAM_INTERSTITIAL_SMALL_TIME(
-          "interstitial.common_name_invalid_time",
-          delta);
       if (proceed)
         RecordSSLBlockingPageEventStats(PROCEED_NAME);
       else
@@ -127,9 +110,6 @@
       break;
     }
     case SSLErrorInfo::CERT_DATE_INVALID: {
-      HISTOGRAM_INTERSTITIAL_SMALL_TIME(
-          "interstitial.date_invalid_time",
-          delta);
       if (proceed)
         RecordSSLBlockingPageEventStats(PROCEED_DATE);
       else
@@ -137,9 +117,6 @@
       break;
     }
     case SSLErrorInfo::CERT_AUTHORITY_INVALID: {
-      HISTOGRAM_INTERSTITIAL_SMALL_TIME(
-          "interstitial.authority_invalid_time",
-          delta);
       if (proceed)
         RecordSSLBlockingPageEventStats(PROCEED_AUTHORITY);
       else
@@ -180,12 +157,19 @@
       ssl_info_(ssl_info),
       request_url_(request_url),
       overridable_(overridable),
-      strict_enforcement_(strict_enforcement) {
+      strict_enforcement_(strict_enforcement),
+      internal_(false) {
   trialCondition_ = base::FieldTrialList::FindFullName(kStudyName);
 
+  if (net::IsHostnameNonUnique(request_url_.HostNoBrackets()))
+    internal_ = true;
+
   RecordSSLBlockingPageEventStats(SHOW_ALL);
-  if (overridable_ && !strict_enforcement_)
+  if (overridable_ && !strict_enforcement_) {
     RecordSSLBlockingPageEventStats(SHOW_OVERRIDABLE);
+    if (internal_)
+      RecordSSLBlockingPageEventStats(SHOW_INTERNAL_HOSTNAME);
+  }
 
   interstitial_page_ = InterstitialPage::Create(
       web_contents_, true, request_url, this);
@@ -195,10 +179,11 @@
 
 SSLBlockingPage::~SSLBlockingPage() {
   if (!callback_.is_null()) {
-    RecordSSLBlockingPageTimeStats(
-      false, cert_error_,
-      overridable_ && !strict_enforcement_, display_start_time_,
-      base::TimeTicks::Now());
+    RecordSSLBlockingPageDetailedStats(false,
+                                       cert_error_,
+                                       overridable_ && !strict_enforcement_,
+                                       internal_,
+                                       display_start_time_);
     // The page is closed without the user having chosen what to do, default to
     // deny.
     NotifyDenyCertificate();
@@ -321,19 +306,21 @@
 }
 
 void SSLBlockingPage::OnProceed() {
-  RecordSSLBlockingPageTimeStats(true, cert_error_,
-      overridable_ && !strict_enforcement_, display_start_time_,
-      base::TimeTicks::Now());
-
+  RecordSSLBlockingPageDetailedStats(true,
+                                     cert_error_,
+                                     overridable_ && !strict_enforcement_,
+                                     internal_,
+                                     display_start_time_);
   // Accepting the certificate resumes the loading of the page.
   NotifyAllowCertificate();
 }
 
 void SSLBlockingPage::OnDontProceed() {
-  RecordSSLBlockingPageTimeStats(false, cert_error_,
-    overridable_ && !strict_enforcement_, display_start_time_,
-    base::TimeTicks::Now());
-
+  RecordSSLBlockingPageDetailedStats(false,
+                                     cert_error_,
+                                     overridable_ && !strict_enforcement_,
+                                     internal_,
+                                     display_start_time_);
   NotifyDenyCertificate();
 }
 
diff --git a/chrome/browser/ssl/ssl_blocking_page.h b/chrome/browser/ssl/ssl_blocking_page.h
index e0e6d61..af24412 100644
--- a/chrome/browser/ssl/ssl_blocking_page.h
+++ b/chrome/browser/ssl/ssl_blocking_page.h
@@ -72,6 +72,8 @@
   // Has the site requested strict enforcement of certificate errors?
   bool strict_enforcement_;
   content::InterstitialPage* interstitial_page_;  // Owns us.
+  // Is the hostname for an internal network?
+  bool internal_;
 
   // For the FieldTrial: this contains the name of the condition.
   std::string trialCondition_;
diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc
index f9868fc..334f0c1 100644
--- a/chrome/browser/ssl/ssl_browser_tests.cc
+++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -398,6 +398,15 @@
                                  false);  // No interstitial showing
 }
 
+#if defined(OS_WIN)
+// Flaky on Windows (http://crbug.com/267653).
+#define MAYBE_TestHTTPSExpiredCertAndDontProceed \
+        DISABLED_TestHTTPSExpiredCertAndDontProceed
+#else
+#define MAYBE_TestHTTPSExpiredCertAndDontProceed \
+        TestHTTPSExpiredCertAndDontProceed
+#endif
+
 // Visits a page with https error and don't proceed (and ensure we can still
 // navigate at that point):
 IN_PROC_BROWSER_TEST_F(SSLUITest, TestHTTPSExpiredCertAndDontProceed) {
@@ -1513,6 +1522,15 @@
   CheckAuthenticatedState(tab, false);
 }
 
+#if defined(OS_WIN)
+// Flaky on Windows (http://crbug.com/267653).
+#define MAYBE_TestUnsafeContentsInWorker \
+        DISABLED_TestUnsafeContentsInWorker
+#else
+#define MAYBE_TestUnsafeContentsInWorker \
+        TestUnsafeContentsInWorker
+#endif
+
 IN_PROC_BROWSER_TEST_F(SSLUITest, TestUnsafeContentsInWorker) {
   ASSERT_TRUE(https_server_.Start());
   ASSERT_TRUE(https_server_expired_.Start());
@@ -1560,6 +1578,15 @@
       browser()->tab_strip_model()->GetActiveWebContents(), false);
 }
 
+#if defined(OS_WIN)
+// Flaky on Windows (http://crbug.com/267653).
+#define MAYBE_TestBlockDisplayingInsecureIframe \
+        DISABLED_TestBlockDisplayingInsecureIframe
+#else
+#define MAYBE_TestBlockDisplayingInsecureIframe \
+        TestBlockDisplayingInsecureIframe
+#endif
+
 // Test that when the browser blocks displaying insecure content (iframes), the
 // indicator shows a secure page, because the blocking made the otherwise
 // unsafe page safe (the notification of this state is handled by other means)
diff --git a/chrome/browser/status_icons/status_icon.cc b/chrome/browser/status_icons/status_icon.cc
index 79efafc..00270d2 100644
--- a/chrome/browser/status_icons/status_icon.cc
+++ b/chrome/browser/status_icons/status_icon.cc
@@ -29,9 +29,6 @@
   FOR_EACH_OBSERVER(StatusIconObserver, observers_, OnStatusIconClicked());
 }
 
-void StatusIcon::SetClickActionLabel(const string16& label) {
-}
-
 #if defined(OS_WIN)
 void StatusIcon::DispatchBalloonClickEvent() {
   FOR_EACH_OBSERVER(StatusIconObserver, observers_, OnBalloonClicked());
diff --git a/chrome/browser/status_icons/status_icon.h b/chrome/browser/status_icons/status_icon.h
index 6110c64..ccc8c2e 100644
--- a/chrome/browser/status_icons/status_icon.h
+++ b/chrome/browser/status_icons/status_icon.h
@@ -31,15 +31,12 @@
   // Sets the image associated with this status icon when pressed.
   virtual void SetPressedImage(const gfx::ImageSkia& image) = 0;
 
-  // Sets the hover text for this status icon.
+  // Sets the hover text for this status icon. This is also used as the label
+  // for the menu item which is created as a replacement for the status icon
+  // click action on platforms that do not support custom click actions for the
+  // status icon (e.g. Ubuntu Unity).
   virtual void SetToolTip(const string16& tool_tip) = 0;
 
-  // Sets the label for the menu item which is created as a replacement for the
-  // status icon click action on platforms that do not support custom click
-  // actions for the status icon (e.g. Ubuntu Unity). Since only a few platforms
-  // would need this, the default action is to ignore the call.
-  virtual void SetClickActionLabel(const string16& label);
-
   // Displays a notification balloon with the specified contents.
   // Depending on the platform it might not appear by the icon tray.
   virtual void DisplayBalloon(const gfx::ImageSkia& icon,
diff --git a/chrome/browser/status_icons/status_tray.cc b/chrome/browser/status_icons/status_tray.cc
index aaa7a57..bdd11da 100644
--- a/chrome/browser/status_icons/status_tray.cc
+++ b/chrome/browser/status_icons/status_tray.cc
@@ -11,8 +11,10 @@
 StatusTray::~StatusTray() {
 }
 
-StatusIcon* StatusTray::CreateStatusIcon(StatusIconType type) {
-  StatusIcon* icon = CreatePlatformStatusIcon(type);
+StatusIcon* StatusTray::CreateStatusIcon(StatusIconType type,
+                                         const gfx::ImageSkia& image,
+                                         const string16& tool_tip) {
+  StatusIcon* icon = CreatePlatformStatusIcon(type, image, tool_tip);
   if (icon)
     status_icons_.push_back(icon);
   return icon;
diff --git a/chrome/browser/status_icons/status_tray.h b/chrome/browser/status_icons/status_tray.h
index e4f240d..1c4be06 100644
--- a/chrome/browser/status_icons/status_tray.h
+++ b/chrome/browser/status_icons/status_tray.h
@@ -8,6 +8,11 @@
 #include "base/basictypes.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/scoped_vector.h"
+#include "base/strings/string16.h"
+
+namespace gfx {
+class ImageSkia;
+}
 
 class StatusIcon;
 
@@ -32,7 +37,9 @@
 
   // Creates a new StatusIcon. The StatusTray retains ownership of the
   // StatusIcon. Returns NULL if the StatusIcon could not be created.
-  StatusIcon* CreateStatusIcon(StatusIconType type);
+  StatusIcon* CreateStatusIcon(StatusIconType type,
+                               const gfx::ImageSkia& image,
+                               const string16& tool_tip);
 
   // Removes |icon| from this status tray.
   void RemoveStatusIcon(StatusIcon* icon);
@@ -43,7 +50,9 @@
   StatusTray();
 
   // Factory method for creating a status icon for this platform.
-  virtual StatusIcon* CreatePlatformStatusIcon(StatusIconType type) = 0;
+  virtual StatusIcon* CreatePlatformStatusIcon(StatusIconType type,
+                                               const gfx::ImageSkia& image,
+                                               const string16& tool_tip) = 0;
 
   // Returns the list of active status icons so subclasses can operate on them.
   const StatusIcons& status_icons() const { return status_icons_; }
diff --git a/chrome/browser/status_icons/status_tray_unittest.cc b/chrome/browser/status_icons/status_tray_unittest.cc
index a71fa9a..238d8b7 100644
--- a/chrome/browser/status_icons/status_tray_unittest.cc
+++ b/chrome/browser/status_icons/status_tray_unittest.cc
@@ -7,10 +7,10 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/status_icons/status_icon.h"
 #include "chrome/browser/status_icons/status_tray.h"
-#include "testing/gmock/include/gmock/gmock.h"
+#include "grit/chrome_unscaled_resources.h"
 #include "testing/gtest/include/gtest/gtest.h"
-
-using testing::Return;
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia.h"
 
 class MockStatusIcon : public StatusIcon {
   virtual void SetImage(const gfx::ImageSkia& image) OVERRIDE {}
@@ -24,9 +24,12 @@
 
 class TestStatusTray : public StatusTray {
  public:
-  MOCK_METHOD1(CreatePlatformStatusIcon,
-               StatusIcon*(StatusTray::StatusIconType type));
-  MOCK_METHOD1(UpdatePlatformContextMenu, void(ui::MenuModel*));
+  virtual StatusIcon* CreatePlatformStatusIcon(
+      StatusIconType type,
+      const gfx::ImageSkia& image,
+      const string16& tool_tip) OVERRIDE {
+    return new MockStatusIcon();
+  }
 
   const StatusIcons& GetStatusIconsForTest() const { return status_icons(); }
 };
@@ -34,17 +37,20 @@
 TEST(StatusTrayTest, Create) {
   // Check for creation and leaks.
   TestStatusTray tray;
-  EXPECT_CALL(tray, CreatePlatformStatusIcon(StatusTray::OTHER_ICON)).WillOnce(
-      Return(new MockStatusIcon()));
-  tray.CreateStatusIcon(StatusTray::OTHER_ICON);
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
+  tray.CreateStatusIcon(
+      StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip"));
+  EXPECT_EQ(1U, tray.GetStatusIconsForTest().size());
 }
 
 // Make sure that removing an icon removes it from the list.
 TEST(StatusTrayTest, CreateRemove) {
   TestStatusTray tray;
-  EXPECT_CALL(tray, CreatePlatformStatusIcon(StatusTray::OTHER_ICON)).WillOnce(
-      Return(new MockStatusIcon()));
-  StatusIcon* icon = tray.CreateStatusIcon(StatusTray::OTHER_ICON);
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
+  StatusIcon* icon = tray.CreateStatusIcon(
+      StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip"));
   EXPECT_EQ(1U, tray.GetStatusIconsForTest().size());
   tray.RemoveStatusIcon(icon);
   EXPECT_EQ(0U, tray.GetStatusIconsForTest().size());
diff --git a/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.cc b/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.cc
index d078e47..f84ace5 100644
--- a/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.cc
+++ b/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.cc
@@ -19,18 +19,9 @@
 
 namespace {
 
-static MediaTransferProtocolDeviceObserverLinux* g_mtp_device_observer = NULL;
-
 // Device root path constant.
 const char kRootPath[] = "/";
 
-device::MediaTransferProtocolManager* GetMediaTransferProtocolManager() {
-  StorageMonitor* monitor = StorageMonitor::GetInstance();
-  if (!monitor)
-    return NULL;
-  return monitor->media_transfer_protocol_manager();
-}
-
 // Constructs and returns the location of the device using the |storage_name|.
 std::string GetDeviceLocationFromStorageName(const std::string& storage_name) {
   // Construct a dummy device path using the storage name. This is only used
@@ -104,12 +95,13 @@
 // Helper function to get the device storage details such as device id, label
 // and location. On success and fills in |id|, |label| and |location|.
 void GetStorageInfo(const std::string& storage_name,
+                    device::MediaTransferProtocolManager* mtp_manager,
                     std::string* id,
                     string16* label,
                     std::string* location) {
   DCHECK(!storage_name.empty());
   const MtpStorageInfo* storage_info =
-      GetMediaTransferProtocolManager()->GetStorageInfo(storage_name);
+      mtp_manager->GetStorageInfo(storage_name);
 
   if (!storage_info)
     return;
@@ -122,13 +114,13 @@
 }  // namespace
 
 MediaTransferProtocolDeviceObserverLinux::
-MediaTransferProtocolDeviceObserverLinux(StorageMonitor::Receiver* receiver)
-    : get_storage_info_func_(&GetStorageInfo),
+MediaTransferProtocolDeviceObserverLinux(
+    StorageMonitor::Receiver* receiver,
+    device::MediaTransferProtocolManager* mtp_manager)
+    : mtp_manager_(mtp_manager),
+      get_storage_info_func_(&GetStorageInfo),
       notifications_(receiver) {
-  DCHECK(!g_mtp_device_observer);
-  g_mtp_device_observer = this;
-
-  GetMediaTransferProtocolManager()->AddObserver(this);
+  mtp_manager_->AddObserver(this);
   EnumerateStorages();
 }
 
@@ -136,22 +128,16 @@
 MediaTransferProtocolDeviceObserverLinux::
 MediaTransferProtocolDeviceObserverLinux(
     StorageMonitor::Receiver* receiver,
+    device::MediaTransferProtocolManager* mtp_manager,
     GetStorageInfoFunc get_storage_info_func)
-    : get_storage_info_func_(get_storage_info_func),
+    : mtp_manager_(mtp_manager),
+      get_storage_info_func_(get_storage_info_func),
       notifications_(receiver) {
-  DCHECK(!g_mtp_device_observer);
-  g_mtp_device_observer = this;
 }
 
 MediaTransferProtocolDeviceObserverLinux::
 ~MediaTransferProtocolDeviceObserverLinux() {
-  DCHECK_EQ(this, g_mtp_device_observer);
-  g_mtp_device_observer = NULL;
-
-  device::MediaTransferProtocolManager* mtp_manager =
-      GetMediaTransferProtocolManager();
-  if (mtp_manager)
-    mtp_manager->RemoveObserver(this);
+  mtp_manager_->RemoveObserver(this);
 }
 
 bool MediaTransferProtocolDeviceObserverLinux::GetStorageInfoForPath(
@@ -190,7 +176,8 @@
     std::string device_id;
     string16 device_name;
     std::string location;
-    get_storage_info_func_(storage_name, &device_id, &device_name, &location);
+    get_storage_info_func_(storage_name, mtp_manager_,
+                           &device_id, &device_name, &location);
 
     // Keep track of device id and device name to see how often we receive
     // empty values.
@@ -217,7 +204,7 @@
 
 void MediaTransferProtocolDeviceObserverLinux::EnumerateStorages() {
   typedef std::vector<std::string> StorageList;
-  StorageList storages = GetMediaTransferProtocolManager()->GetStorages();
+  StorageList storages = mtp_manager_->GetStorages();
   for (StorageList::const_iterator storage_iter = storages.begin();
        storage_iter != storages.end(); ++storage_iter) {
     StorageChanged(true, *storage_iter);
diff --git a/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.h b/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.h
index 73dfd48..d95d1f6 100644
--- a/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.h
+++ b/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.h
@@ -20,18 +20,21 @@
 
 // Gets the mtp device information given a |storage_name|. On success,
 // fills in |id|, |name| and |location|.
-typedef void (*GetStorageInfoFunc)(const std::string& storage_name,
-                                   std::string* id,
-                                   string16* name,
-                                   std::string* location);
+typedef void (*GetStorageInfoFunc)(
+    const std::string& storage_name,
+    device::MediaTransferProtocolManager* mtp_manager,
+    std::string* id,
+    string16* name,
+    std::string* location);
 
 // Helper class to send MTP storage attachment and detachment events to
 // StorageMonitor.
 class MediaTransferProtocolDeviceObserverLinux
     : public device::MediaTransferProtocolManager::Observer {
  public:
-  explicit MediaTransferProtocolDeviceObserverLinux(
-      StorageMonitor::Receiver* receiver);
+  MediaTransferProtocolDeviceObserverLinux(
+      StorageMonitor::Receiver* receiver,
+      device::MediaTransferProtocolManager* mtp_manager);
   virtual ~MediaTransferProtocolDeviceObserverLinux();
 
   // Finds the storage that contains |path| and populates |storage_info|.
@@ -43,6 +46,7 @@
   // Only used in unit tests.
   MediaTransferProtocolDeviceObserverLinux(
       StorageMonitor::Receiver* receiver,
+      device::MediaTransferProtocolManager* mtp_manager,
       GetStorageInfoFunc get_storage_info_func);
 
   // device::MediaTransferProtocolManager::Observer implementation.
@@ -57,6 +61,10 @@
   // Enumerate existing mtp storage devices.
   void EnumerateStorages();
 
+  // Pointer to the MTP manager. Not owned. Client must ensure the MTP
+  // manager outlives this object.
+  device::MediaTransferProtocolManager* mtp_manager_;
+
   // Map of all attached mtp devices.
   StorageLocationToInfoMap storage_map_;
 
diff --git a/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux_unittest.cc b/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux_unittest.cc
index c895cee..e3ace3f 100644
--- a/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux_unittest.cc
+++ b/chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux_unittest.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/storage_monitor/test_storage_monitor.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "content/public/test/test_browser_thread.h"
+#include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chrome {
@@ -40,6 +41,7 @@
 // Helper function to get the device storage details such as device id, label
 // and location. On success, fills in |id|, |label| and |location|.
 void GetStorageInfo(const std::string& storage_name,
+                    device::MediaTransferProtocolManager* mtp_manager,
                     std::string* id,
                     string16* label,
                     std::string* location) {
@@ -57,8 +59,10 @@
     : public MediaTransferProtocolDeviceObserverLinux {
  public:
   TestMediaTransferProtocolDeviceObserverLinux(
-      StorageMonitor::Receiver* receiver)
-      : MediaTransferProtocolDeviceObserverLinux(receiver, &GetStorageInfo) {
+      StorageMonitor::Receiver* receiver,
+      device::MediaTransferProtocolManager* mtp_manager)
+      : MediaTransferProtocolDeviceObserverLinux(receiver, mtp_manager,
+                                                 &GetStorageInfo) {
   }
 
   // Notifies MediaTransferProtocolDeviceObserverLinux about the attachment of
@@ -97,7 +101,8 @@
     chrome::test::TestStorageMonitor* monitor =
         chrome::test::TestStorageMonitor::CreateAndInstall();
     mtp_device_observer_.reset(
-        new TestMediaTransferProtocolDeviceObserverLinux(monitor->receiver()));
+        new TestMediaTransferProtocolDeviceObserverLinux(
+            monitor->receiver(), monitor->media_transfer_protocol_manager()));
     monitor->AddObserver(mock_storage_observer_.get());
   }
 
diff --git a/chrome/browser/storage_monitor/storage_monitor.h b/chrome/browser/storage_monitor/storage_monitor.h
index f008dca..cf9767e 100644
--- a/chrome/browser/storage_monitor/storage_monitor.h
+++ b/chrome/browser/storage_monitor/storage_monitor.h
@@ -29,6 +29,10 @@
 class MediaTransferProtocolManager;
 }
 
+namespace extensions {
+class StorageInfoProviderTest;
+}
+
 namespace chrome {
 
 class MediaFileSystemRegistryTest;
@@ -141,6 +145,7 @@
   friend class MediaFileSystemRegistryTest;
   friend class ::SystemStorageApiTest;
   friend class ::SystemStorageEjectApiTest;
+  friend class extensions::StorageInfoProviderTest;
 
   StorageMonitor();
 
diff --git a/chrome/browser/storage_monitor/storage_monitor_chromeos.cc b/chrome/browser/storage_monitor/storage_monitor_chromeos.cc
index a871fa8..8b7f8de 100644
--- a/chrome/browser/storage_monitor/storage_monitor_chromeos.cc
+++ b/chrome/browser/storage_monitor/storage_monitor_chromeos.cc
@@ -6,7 +6,6 @@
 
 #include "chrome/browser/storage_monitor/storage_monitor_chromeos.h"
 
-#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/stl_util.h"
@@ -17,8 +16,6 @@
 #include "chrome/browser/storage_monitor/media_storage_util.h"
 #include "chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.h"
 #include "chrome/browser/storage_monitor/removable_device_constants.h"
-#include "chrome/browser/storage_monitor/test_media_transfer_protocol_manager_linux.h"
-#include "chrome/common/chrome_switches.h"
 #include "content/public/browser/browser_thread.h"
 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
 
@@ -97,12 +94,6 @@
 
 StorageMonitorCros::StorageMonitorCros()
     : weak_ptr_factory_(this) {
-  // TODO(thestig) Do not do this here. Do it in TestingBrowserProcess when
-  // BrowserProcess owns StorageMonitor.
-  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) {
-    SetMediaTransferProtocolManagerForTest(
-        new chrome::TestMediaTransferProtocolManagerLinux());
-  }
 }
 
 StorageMonitorCros::~StorageMonitorCros() {
@@ -124,7 +115,8 @@
   }
 
   media_transfer_protocol_device_observer_.reset(
-      new chrome::MediaTransferProtocolDeviceObserverLinux(receiver()));
+      new chrome::MediaTransferProtocolDeviceObserverLinux(
+          receiver(), media_transfer_protocol_manager_.get()));
 }
 
 void StorageMonitorCros::CheckExistingMountPoints() {
diff --git a/chrome/browser/storage_monitor/storage_monitor_linux.cc b/chrome/browser/storage_monitor/storage_monitor_linux.cc
index 29131c5..3be93b9 100644
--- a/chrome/browser/storage_monitor/storage_monitor_linux.cc
+++ b/chrome/browser/storage_monitor/storage_monitor_linux.cc
@@ -13,7 +13,6 @@
 
 #include "base/basictypes.h"
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/metrics/histogram.h"
 #include "base/process/kill.h"
 #include "base/process/launch.h"
@@ -25,9 +24,7 @@
 #include "chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.h"
 #include "chrome/browser/storage_monitor/removable_device_constants.h"
 #include "chrome/browser/storage_monitor/storage_info.h"
-#include "chrome/browser/storage_monitor/test_media_transfer_protocol_manager_linux.h"
 #include "chrome/browser/storage_monitor/udev_util_linux.h"
-#include "chrome/common/chrome_switches.h"
 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
 
 namespace chrome {
@@ -261,13 +258,6 @@
       get_device_info_callback_(base::Bind(&GetDeviceInfo)),
       weak_ptr_factory_(this) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  // TODO(thestig) Do not do this here. Do it in TestingBrowserProcess when
-  // BrowserProcess owns StorageMonitor.
-  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) {
-    SetMediaTransferProtocolManagerForTest(
-        new TestMediaTransferProtocolManagerLinux());
-  }
 }
 
 StorageMonitorLinux::~StorageMonitorLinux() {
@@ -293,7 +283,8 @@
   }
 
   media_transfer_protocol_device_observer_.reset(
-      new MediaTransferProtocolDeviceObserverLinux(receiver()));
+      new MediaTransferProtocolDeviceObserverLinux(
+          receiver(), media_transfer_protocol_manager_.get()));
 }
 
 bool StorageMonitorLinux::GetStorageInfoForPath(
diff --git a/chrome/browser/storage_monitor/test_storage_monitor.cc b/chrome/browser/storage_monitor/test_storage_monitor.cc
index 22c3f7d..b4a04a6 100644
--- a/chrome/browser/storage_monitor/test_storage_monitor.cc
+++ b/chrome/browser/storage_monitor/test_storage_monitor.cc
@@ -30,6 +30,7 @@
 
 TestStorageMonitor::~TestStorageMonitor() {}
 
+// static
 TestStorageMonitor* TestStorageMonitor::CreateAndInstall() {
   RemoveSingleton();
   TestStorageMonitor* monitor = new TestStorageMonitor();
@@ -44,6 +45,7 @@
   return NULL;
 }
 
+// static
 TestStorageMonitor* TestStorageMonitor::CreateForBrowserTests() {
   TestStorageMonitor* return_monitor = new TestStorageMonitor();
   return_monitor->Init();
@@ -57,6 +59,7 @@
   return return_monitor;
 }
 
+// static
 void TestStorageMonitor::RemoveSingleton() {
   TestingBrowserProcess* browser_process = TestingBrowserProcess::GetGlobal();
   if (browser_process)
diff --git a/chrome/browser/sync/about_sync_util.cc b/chrome/browser/sync/about_sync_util.cc
index 1b9d149..bdf1411 100644
--- a/chrome/browser/sync/about_sync_util.cc
+++ b/chrome/browser/sync/about_sync_util.cc
@@ -335,8 +335,11 @@
   }
 
   if (snapshot.is_initialized()) {
-    session_source.SetValue(
-        syncer::GetUpdatesSourceString(snapshot.source().updates_source));
+    if (snapshot.legacy_updates_source() !=
+        sync_pb::GetUpdatesCallerInfo::UNKNOWN) {
+      session_source.SetValue(
+          syncer::GetUpdatesSourceString(snapshot.legacy_updates_source()));
+    }
     get_key_result.SetValue(
         GetSyncerErrorString(
             snapshot.model_neutral_state().last_get_key_result));
diff --git a/chrome/browser/sync/glue/browser_thread_model_worker.cc b/chrome/browser/sync/glue/browser_thread_model_worker.cc
index 57e4b9d..7f4024a 100644
--- a/chrome/browser/sync/glue/browser_thread_model_worker.cc
+++ b/chrome/browser/sync/glue/browser_thread_model_worker.cc
@@ -51,6 +51,7 @@
 void BrowserThreadModelWorker::RegisterForLoopDestruction() {
   if (BrowserThread::CurrentlyOn(thread_)) {
     base::MessageLoop::current()->AddDestructionObserver(this);
+    SetWorkingLoopToCurrent();
   } else {
     BrowserThread::PostTask(
         thread_, FROM_HERE,
diff --git a/chrome/browser/sync/glue/chrome_extensions_activity_monitor.h b/chrome/browser/sync/glue/chrome_extensions_activity_monitor.h
deleted file mode 100644
index 969a3fe..0000000
--- a/chrome/browser/sync/glue/chrome_extensions_activity_monitor.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_SYNC_GLUE_CHROME_EXTENSIONS_ACTIVITY_MONITOR_H_
-#define CHROME_BROWSER_SYNC_GLUE_CHROME_EXTENSIONS_ACTIVITY_MONITOR_H_
-
-#include "base/compiler_specific.h"
-#include "base/synchronization/lock.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "sync/util/extensions_activity_monitor.h"
-
-namespace browser_sync {
-
-// Chrome-specific implementation of syncer::ExtensionsActivityMonitor.
-//
-// As per the requirements of syncer::ExtensionsActivityMonitor, all
-// overridden methods are thread-safe, although this class must be
-// created and destroyed on the UI thread.
-class ChromeExtensionsActivityMonitor
-    : public syncer::ExtensionsActivityMonitor,
-      public content::NotificationObserver {
- public:
-  ChromeExtensionsActivityMonitor();
-  virtual ~ChromeExtensionsActivityMonitor();
-
-  // syncer::ExtensionsActivityMonitor implementation.
-  virtual void GetAndClearRecords(Records* buffer) OVERRIDE;
-  virtual void PutRecords(const Records& records) OVERRIDE;
-
-  // content::NotificationObserver implementation.
-  virtual void Observe(int type,
-                       const content::NotificationSource& source,
-                       const content::NotificationDetails& details) OVERRIDE;
-
- private:
-  Records records_;
-  mutable base::Lock records_lock_;
-
-  // Used only on UI loop.
-  content::NotificationRegistrar registrar_;
-};
-
-}  // namespace browser_sync
-
-#endif  // CHROME_BROWSER_SYNC_GLUE_CHROME_EXTENSIONS_ACTIVITY_MONITOR_H_
diff --git a/chrome/browser/sync/glue/device_info.cc b/chrome/browser/sync/glue/device_info.cc
index c21a74b..486c8bb 100644
--- a/chrome/browser/sync/glue/device_info.cc
+++ b/chrome/browser/sync/glue/device_info.cc
@@ -95,6 +95,10 @@
   return sync_user_agent_;
 }
 
+const std::string& DeviceInfo::public_id() const {
+  return public_id_;
+}
+
 sync_pb::SyncEnums::DeviceType DeviceInfo::device_type() const {
   return device_type_;
 }
@@ -176,7 +180,7 @@
   return value;
 }
 
-void DeviceInfo::SetPublicId(std::string id) {
+void DeviceInfo::set_public_id(std::string id) {
   public_id_ = id;
 }
 
diff --git a/chrome/browser/sync/glue/device_info.h b/chrome/browser/sync/glue/device_info.h
index f8645a0..9c984a7 100644
--- a/chrome/browser/sync/glue/device_info.h
+++ b/chrome/browser/sync/glue/device_info.h
@@ -48,6 +48,9 @@
   // |DeviceInfo::MakeUserAgentForSyncApi|.
   const std::string& sync_user_agent() const;
 
+  // Third party visible id for the device. See |public_id_| for more details.
+  const std::string& public_id() const;
+
   // Device Type.
   sync_pb::SyncEnums::DeviceType device_type() const;
 
@@ -58,7 +61,7 @@
   // not unique enough so the user can be tracked. Exposing |guid|
   // would lead to a stable unique id for a device which can potentially
   // be used for tracking.
-  void SetPublicId(std::string id);
+  void set_public_id(std::string id);
 
   // Converts the |DeviceInfo| values to a JS friendly DictionaryValue,
   // which extension APIs can expose to third party apps.
@@ -102,6 +105,10 @@
 
   const sync_pb::SyncEnums::DeviceType device_type_;
 
+  // Exposing |guid| would lead to a stable unique id for a device which
+  // can potentially be used for tracking. Public ids are privacy safe
+  // ids in that the same device will have different id for different apps
+  // and they are also reset when app/extension is uninstalled.
   std::string public_id_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceInfo);
diff --git a/chrome/browser/sync/glue/chrome_extensions_activity_monitor.cc b/chrome/browser/sync/glue/extensions_activity_monitor.cc
similarity index 63%
rename from chrome/browser/sync/glue/chrome_extensions_activity_monitor.cc
rename to chrome/browser/sync/glue/extensions_activity_monitor.cc
index fbe3d2a..b8de3bb 100644
--- a/chrome/browser/sync/glue/chrome_extensions_activity_monitor.cc
+++ b/chrome/browser/sync/glue/extensions_activity_monitor.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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.
 
-#include "chrome/browser/sync/glue/chrome_extensions_activity_monitor.h"
+#include "chrome/browser/sync/glue/extensions_activity_monitor.h"
 
 #include "base/bind.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -10,12 +10,14 @@
 #include "chrome/common/extensions/extension.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
+#include "sync/util/extensions_activity.h"
 
 using content::BrowserThread;
 
 namespace browser_sync {
 
-ChromeExtensionsActivityMonitor::ChromeExtensionsActivityMonitor() {
+ExtensionsActivityMonitor::ExtensionsActivityMonitor()
+    : extensions_activity_(new syncer::ExtensionsActivity()) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   // It would be nice if we could specify a Source for each specific function
   // we wanted to observe, but the actual function objects are allocated on
@@ -27,29 +29,14 @@
                  content::NotificationService::AllSources());
 }
 
-ChromeExtensionsActivityMonitor::~ChromeExtensionsActivityMonitor() {
+ExtensionsActivityMonitor::~ExtensionsActivityMonitor() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
 
-void ChromeExtensionsActivityMonitor::GetAndClearRecords(Records* buffer) {
-  base::AutoLock lock(records_lock_);
-  buffer->clear();
-  buffer->swap(records_);
-}
-
-void ChromeExtensionsActivityMonitor::PutRecords(const Records& records) {
-  base::AutoLock lock(records_lock_);
-  for (Records::const_iterator i = records.begin(); i != records.end(); ++i) {
-    records_[i->first].extension_id = i->second.extension_id;
-    records_[i->first].bookmark_write_count += i->second.bookmark_write_count;
-  }
-}
-
-void ChromeExtensionsActivityMonitor::Observe(
+void ExtensionsActivityMonitor::Observe(
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  base::AutoLock lock(records_lock_);
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   const extensions::Extension* extension =
       content::Source<const extensions::Extension>(source).ptr();
@@ -60,10 +47,13 @@
       f->name() == "bookmarks.create" ||
       f->name() == "bookmarks.removeTree" ||
       f->name() == "bookmarks.remove") {
-    Record& record = records_[extension->id()];
-    record.extension_id = extension->id();
-    record.bookmark_write_count++;
+    extensions_activity_->UpdateRecord(extension->id());
   }
 }
 
+const scoped_refptr<syncer::ExtensionsActivity>&
+ExtensionsActivityMonitor::GetExtensionsActivity() {
+  return extensions_activity_;
+}
+
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/glue/extensions_activity_monitor.h b/chrome/browser/sync/glue/extensions_activity_monitor.h
new file mode 100644
index 0000000..4d5e741
--- /dev/null
+++ b/chrome/browser/sync/glue/extensions_activity_monitor.h
@@ -0,0 +1,41 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_SYNC_GLUE_EXTENSIONS_ACTIVITY_MONITOR_H_
+#define CHROME_BROWSER_SYNC_GLUE_EXTENSIONS_ACTIVITY_MONITOR_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+namespace syncer {
+class ExtensionsActivity;
+}
+
+namespace browser_sync {
+
+// Observe and record usage of extension bookmark API.
+class ExtensionsActivityMonitor : public content::NotificationObserver {
+ public:
+  ExtensionsActivityMonitor();
+  virtual ~ExtensionsActivityMonitor();
+
+  // content::NotificationObserver implementation.
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
+  const scoped_refptr<syncer::ExtensionsActivity>& GetExtensionsActivity();
+
+ private:
+  scoped_refptr<syncer::ExtensionsActivity> extensions_activity_;
+
+  // Used only on UI loop.
+  content::NotificationRegistrar registrar_;
+};
+
+}  // namespace browser_sync
+
+#endif  // CHROME_BROWSER_SYNC_GLUE_EXTENSIONS_ACTIVITY_MONITOR_H_
diff --git a/chrome/browser/sync/glue/chrome_extensions_activity_monitor_unittest.cc b/chrome/browser/sync/glue/extensions_activity_monitor_unittest.cc
similarity index 87%
rename from chrome/browser/sync/glue/chrome_extensions_activity_monitor_unittest.cc
rename to chrome/browser/sync/glue/extensions_activity_monitor_unittest.cc
index eab034c..bfeeb21 100644
--- a/chrome/browser/sync/glue/chrome_extensions_activity_monitor_unittest.cc
+++ b/chrome/browser/sync/glue/extensions_activity_monitor_unittest.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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.
 
-#include "chrome/browser/sync/glue/chrome_extensions_activity_monitor.h"
+#include "chrome/browser/sync/glue/extensions_activity_monitor.h"
 
 #include "base/files/file_path.h"
 #include "base/message_loop/message_loop.h"
@@ -15,6 +15,7 @@
 #include "chrome/common/extensions/extension_manifest_constants.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_browser_thread.h"
+#include "sync/util/extensions_activity.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using extensions::Extension;
@@ -74,7 +75,7 @@
   content::TestBrowserThread ui_thread_;
 
  protected:
-  ChromeExtensionsActivityMonitor monitor_;
+  ExtensionsActivityMonitor monitor_;
   scoped_refptr<Extension> extension1_;
   scoped_refptr<Extension> extension2_;
   // IDs of |extension{1,2}_|.
@@ -106,8 +107,8 @@
   FireBookmarksApiEvent<extensions::BookmarksGetTreeFunction>(extension2_, 33);
   const uint32 writes_by_extension2 = 8;
 
-  syncer::ExtensionsActivityMonitor::Records results;
-  monitor_.GetAndClearRecords(&results);
+  syncer::ExtensionsActivity::Records results;
+  monitor_.GetExtensionsActivity()->GetAndClearRecords(&results);
 
   EXPECT_EQ(2U, results.size());
   EXPECT_TRUE(results.find(id1_) != results.end());
@@ -124,8 +125,8 @@
   FireBookmarksApiEvent<extensions::BookmarksCreateFunction>(extension1_, 5);
   FireBookmarksApiEvent<extensions::BookmarksMoveFunction>(extension2_, 8);
 
-  syncer::ExtensionsActivityMonitor::Records results;
-  monitor_.GetAndClearRecords(&results);
+  syncer::ExtensionsActivity::Records results;
+  monitor_.GetExtensionsActivity()->GetAndClearRecords(&results);
 
   EXPECT_EQ(2U, results.size());
   EXPECT_EQ(5U, results[id1_].bookmark_write_count);
@@ -136,9 +137,9 @@
 
   // Simulate a commit failure, which augments the active record set with the
   // refugee records.
-  monitor_.PutRecords(results);
-  syncer::ExtensionsActivityMonitor::Records new_records;
-  monitor_.GetAndClearRecords(&new_records);
+  monitor_.GetExtensionsActivity()->PutRecords(results);
+  syncer::ExtensionsActivity::Records new_records;
+  monitor_.GetExtensionsActivity()->GetAndClearRecords(&new_records);
 
   EXPECT_EQ(2U, results.size());
   EXPECT_EQ(id1_, new_records[id1_].extension_id);
@@ -153,17 +154,17 @@
 TEST_F(SyncChromeExtensionsActivityMonitorTest, DISABLED_MultiGet) {
   FireBookmarksApiEvent<extensions::BookmarksCreateFunction>(extension1_, 5);
 
-  syncer::ExtensionsActivityMonitor::Records results;
-  monitor_.GetAndClearRecords(&results);
+  syncer::ExtensionsActivity::Records results;
+  monitor_.GetExtensionsActivity()->GetAndClearRecords(&results);
 
   EXPECT_EQ(1U, results.size());
   EXPECT_EQ(5U, results[id1_].bookmark_write_count);
 
-  monitor_.GetAndClearRecords(&results);
+  monitor_.GetExtensionsActivity()->GetAndClearRecords(&results);
   EXPECT_TRUE(results.empty());
 
   FireBookmarksApiEvent<extensions::BookmarksCreateFunction>(extension1_, 3);
-  monitor_.GetAndClearRecords(&results);
+  monitor_.GetExtensionsActivity()->GetAndClearRecords(&results);
 
   EXPECT_EQ(1U, results.size());
   EXPECT_EQ(3U, results[id1_].bookmark_write_count);
diff --git a/chrome/browser/sync/glue/history_model_worker.cc b/chrome/browser/sync/glue/history_model_worker.cc
index da5c370..dc63d5e 100644
--- a/chrome/browser/sync/glue/history_model_worker.cc
+++ b/chrome/browser/sync/glue/history_model_worker.cc
@@ -43,12 +43,12 @@
 
 class AddDBThreadObserverTask : public history::HistoryDBTask {
  public:
-  explicit AddDBThreadObserverTask(HistoryModelWorker* history_worker)
-     : history_worker_(history_worker) {}
+  explicit AddDBThreadObserverTask(base::Closure register_callback)
+     : register_callback_(register_callback) {}
 
   virtual bool RunOnDBThread(history::HistoryBackend* backend,
                              history::HistoryDatabase* db) OVERRIDE {
-    base::MessageLoop::current()->AddDestructionObserver(history_worker_.get());
+    register_callback_.Run();
     return true;
   }
 
@@ -57,7 +57,7 @@
  private:
   virtual ~AddDBThreadObserverTask() {}
 
-  scoped_refptr<HistoryModelWorker> history_worker_;
+  base::Closure register_callback_;
 };
 
 namespace {
@@ -91,8 +91,15 @@
 
 void HistoryModelWorker::RegisterForLoopDestruction() {
   CHECK(history_service_.get());
-  history_service_->ScheduleDBTask(new AddDBThreadObserverTask(this),
-                                   &cancelable_consumer_);
+  history_service_->ScheduleDBTask(
+      new AddDBThreadObserverTask(
+          base::Bind(&HistoryModelWorker::RegisterOnDBThread, this)),
+      &cancelable_consumer_);
+}
+
+void HistoryModelWorker::RegisterOnDBThread() {
+  base::MessageLoop::current()->AddDestructionObserver(this);
+  SetWorkingLoopToCurrent();
 }
 
 syncer::SyncerError HistoryModelWorker::DoWorkAndWaitUntilDoneImpl(
diff --git a/chrome/browser/sync/glue/history_model_worker.h b/chrome/browser/sync/glue/history_model_worker.h
index 93112ab..0f55569 100644
--- a/chrome/browser/sync/glue/history_model_worker.h
+++ b/chrome/browser/sync/glue/history_model_worker.h
@@ -32,6 +32,10 @@
   virtual void RegisterForLoopDestruction() OVERRIDE;
   virtual syncer::ModelSafeGroup GetModelSafeGroup() OVERRIDE;
 
+  // Called on history DB thread to register HistoryModelWorker to observe
+  // destruction of history backend loop.
+  void RegisterOnDBThread();
+
  protected:
   virtual syncer::SyncerError DoWorkAndWaitUntilDoneImpl(
       const syncer::WorkCallback& work) OVERRIDE;
diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller.cc b/chrome/browser/sync/glue/non_frontend_data_type_controller.cc
index 6d80bef..88fee95 100644
--- a/chrome/browser/sync/glue/non_frontend_data_type_controller.cc
+++ b/chrome/browser/sync/glue/non_frontend_data_type_controller.cc
@@ -7,7 +7,6 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/logging.h"
-#include "base/threading/thread_restrictions.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/glue/change_processor.h"
 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
@@ -17,12 +16,146 @@
 #include "content/public/browser/browser_thread.h"
 #include "sync/api/sync_error.h"
 #include "sync/internal_api/public/base/model_type.h"
+#include "sync/internal_api/public/util/weak_handle.h"
 #include "sync/util/data_type_histogram.h"
 
 using content::BrowserThread;
 
 namespace browser_sync {
 
+class NonFrontendDataTypeController::BackendComponentsContainer {
+ public:
+  explicit BackendComponentsContainer(
+      NonFrontendDataTypeController* controller);
+  ~BackendComponentsContainer();
+  void Run();
+  void Disconnect();
+
+ private:
+  bool CreateComponents();
+  void Associate();
+
+  // For creating components.
+  NonFrontendDataTypeController* controller_;
+  base::Lock controller_lock_;
+
+  syncer::ModelType type_;
+
+  // For returning association results to controller on UI.
+  syncer::WeakHandle<NonFrontendDataTypeController> controller_handle_;
+
+  scoped_ptr<AssociatorInterface> model_associator_;
+  scoped_ptr<ChangeProcessor> change_processor_;
+};
+
+NonFrontendDataTypeController::
+BackendComponentsContainer::BackendComponentsContainer(
+    NonFrontendDataTypeController* controller)
+     : controller_(controller),
+       type_(controller->type()) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  controller_handle_ =
+      syncer::MakeWeakHandle(controller_->weak_ptr_factory_.GetWeakPtr());
+}
+
+NonFrontendDataTypeController::
+BackendComponentsContainer::~BackendComponentsContainer() {
+  if (model_associator_)
+    model_associator_->DisassociateModels();
+}
+
+void NonFrontendDataTypeController::BackendComponentsContainer::Run() {
+  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (CreateComponents())
+    Associate();
+}
+
+bool
+NonFrontendDataTypeController::BackendComponentsContainer::CreateComponents() {
+  base::AutoLock al(controller_lock_);
+  if (!controller_) {
+    DVLOG(1) << "Controller was stopped before sync components are created.";
+    return false;
+  }
+
+  ProfileSyncComponentsFactory::SyncComponents sync_components =
+      controller_->CreateSyncComponents();
+  model_associator_.reset(sync_components.model_associator);
+  change_processor_.reset(sync_components.change_processor);
+  return true;
+}
+
+void NonFrontendDataTypeController::BackendComponentsContainer::Associate() {
+  CHECK(model_associator_);
+
+  bool succeeded = false;
+
+  browser_sync::NonFrontendDataTypeController::AssociationResult result(type_);
+  if (!model_associator_->CryptoReadyIfNecessary()) {
+    result.needs_crypto = true;
+  } else {
+    base::TimeTicks start_time = base::TimeTicks::Now();
+
+    if (!model_associator_->SyncModelHasUserCreatedNodes(
+        &result.sync_has_nodes)) {
+      result.unrecoverable_error = true;
+      result.error = syncer::SyncError(FROM_HERE,
+                                       syncer::SyncError::UNRECOVERABLE_ERROR,
+                                       "Failed to load sync nodes",
+                                       type_);
+    } else {
+      result.error = model_associator_->AssociateModels(
+          &result.local_merge_result, &result.syncer_merge_result);
+
+      // Return components to frontend when no error.
+      if (!result.error.IsSet()) {
+        succeeded = true;
+        result.change_processor = change_processor_.get();
+        result.model_associator = model_associator_.get();
+      }
+    }
+    result.association_time = base::TimeTicks::Now() - start_time;
+  }
+  result.local_merge_result.set_error(result.error);
+
+  // Destroy processor/associator on backend on failure.
+  if (!succeeded) {
+    base::AutoLock al(controller_lock_);
+    model_associator_->DisassociateModels();
+    change_processor_.reset();
+    model_associator_.reset();
+  }
+
+  controller_handle_.Call(
+      FROM_HERE,
+      &browser_sync::NonFrontendDataTypeController::AssociationCallback,
+      result);
+}
+
+void NonFrontendDataTypeController::BackendComponentsContainer::Disconnect() {
+  base::AutoLock al(controller_lock_);
+  CHECK(controller_);
+
+  if (change_processor_)
+    controller_->DisconnectProcessor(change_processor_.get());
+  if (model_associator_)
+    model_associator_->AbortAssociation();
+
+  controller_ = NULL;
+}
+
+NonFrontendDataTypeController::AssociationResult::AssociationResult(
+    syncer::ModelType type)
+    : needs_crypto(false),
+      unrecoverable_error(false),
+      sync_has_nodes(false),
+      local_merge_result(type),
+      syncer_merge_result(type),
+      change_processor(NULL),
+      model_associator(NULL) {}
+
+NonFrontendDataTypeController::AssociationResult::~AssociationResult() {}
+
 NonFrontendDataTypeController::NonFrontendDataTypeController(
     ProfileSyncComponentsFactory* profile_sync_factory,
     Profile* profile,
@@ -31,10 +164,9 @@
       profile_sync_factory_(profile_sync_factory),
       profile_(profile),
       profile_sync_service_(sync_service),
-      abort_association_(false),
-      abort_association_complete_(false, false),
-      start_association_called_(true, false),
-      start_models_failed_(false) {
+      model_associator_(NULL),
+      change_processor_(NULL),
+      weak_ptr_factory_(this) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(profile_sync_factory_);
   DCHECK(profile_);
@@ -45,8 +177,6 @@
     const ModelLoadCallback& model_load_callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!model_load_callback.is_null());
-  start_association_called_.Reset();
-  start_models_failed_ = false;
   if (state_ != NOT_RUNNING) {
     model_load_callback.Run(type(),
                             syncer::SyncError(FROM_HERE,
@@ -58,7 +188,6 @@
 
   state_ = MODEL_STARTING;
   if (!StartModels()) {
-    start_models_failed_ = true;
     // We failed to start the models. There is no point in waiting.
     // Note: This code is deprecated. The only 2 datatypes here,
     // passwords and typed urls, dont have any special loading. So if we
@@ -85,131 +214,69 @@
     const StartCallback& start_callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!start_callback.is_null());
+  DCHECK(!components_container_);
   DCHECK_EQ(state_, MODEL_LOADED);
 
   // Kick off association on the thread the datatype resides on.
   state_ = ASSOCIATING;
   start_callback_ = start_callback;
-  if (!StartAssociationAsync()) {
+
+  components_container_.reset(new BackendComponentsContainer(this));
+
+  if (!PostTaskOnBackendThread(
+      FROM_HERE,
+      base::Bind(&BackendComponentsContainer::Run,
+                 base::Unretained(components_container_.get())))) {
     syncer::SyncError error(
         FROM_HERE,
         syncer::SyncError::DATATYPE_ERROR,
         "Failed to post StartAssociation", type());
     syncer::SyncMergeResult local_merge_result(type());
     local_merge_result.set_error(error);
-    StartDoneImpl(ASSOCIATION_FAILED,
-                  DISABLED,
-                  local_merge_result,
-                  syncer::SyncMergeResult(type()));
+    StartDone(ASSOCIATION_FAILED,
+              local_merge_result,
+              syncer::SyncMergeResult(type()));
   }
 }
 
-void NonFrontendDataTypeController::StopWhileAssociating() {
-  state_ = STOPPING;
-  {
-    base::AutoLock lock(abort_association_lock_);
-    abort_association_ = true;
-    if (model_associator_)
-      model_associator_->AbortAssociation();
-    if (!start_association_called_.IsSignaled()) {
-      StartDoneImpl(ABORTED,
-                    NOT_RUNNING,
-                    syncer::SyncMergeResult(type()),
-                    syncer::SyncMergeResult(type()));
-      return; // There is nothing more for us to do.
-    }
-  }
-
-  // Wait for the model association to abort.
-  if (start_association_called_.IsSignaled()) {
-    LOG(INFO) << "Stopping after |StartAssocation| is called.";
-    if (start_models_failed_) {
-      LOG(INFO) << "Start models failed";
-      abort_association_complete_.Wait();
-    } else {
-      LOG(INFO) << "Start models succeeded";
-      abort_association_complete_.Wait();
-    }
-  } else {
-    LOG(INFO) << "Stopping before |StartAssocation| is called.";
-    if (start_models_failed_) {
-      LOG(INFO) << "Start models failed";
-      abort_association_complete_.Wait();
-    } else {
-      LOG(INFO) << "Start models succeeded";
-      abort_association_complete_.Wait();
-    }
-
-  }
-
-  StartDoneImpl(ABORTED,
-                STOPPING,
-                syncer::SyncMergeResult(type()),
-                syncer::SyncMergeResult(type()));
+void DestroyComponentsInBackend(
+    NonFrontendDataTypeController::BackendComponentsContainer *containter) {
+  delete containter;
 }
 
-namespace {
-// Helper function that signals the UI thread once the StopAssociation task
-// has finished completing (this task is queued after the StopAssociation task).
-void SignalCallback(base::WaitableEvent* wait_event) {
-  wait_event->Signal();
-}
-}  // namespace
-
 void NonFrontendDataTypeController::Stop() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK_NE(state_, NOT_RUNNING);
 
-  // TODO(sync): Blocking the UI thread at shutdown is bad. The new API avoids
-  // this. Once all non-frontend datatypes use the new API, we can get rid of
-  // this locking (see implementation in AutofillProfileDataTypeController).
-  // http://crbug.com/19757
-  base::ThreadRestrictions::ScopedAllowWait allow_wait;
+  // Deactivate the date type on the UI thread first to stop processing
+  // sync server changes. This needs to happen before posting task to destroy
+  // processor and associator on backend. Otherwise it could crash if syncer
+  // post work to backend after destruction task and that work is run before
+  // deactivation.
+  profile_sync_service()->DeactivateDataType(type());
 
-  // If Stop() is called while Start() is waiting for association to
-  // complete, we need to abort the association and wait for the DB
-  // thread to finish the StartImpl() task.
-  switch (state_) {
-    case ASSOCIATING:
-      StopWhileAssociating();
+  // Ignore association callback.
+  weak_ptr_factory_.InvalidateWeakPtrs();
 
-      // TODO(sync) : This should be cleaned up. Once we move to the new api
-      // this should not be a problem.
-      if (!start_association_called_.IsSignaled()) {
-        // If datatype's thread has not even picked up executing it is safe
-        // to bail out now. We have no more state cleanups to do.
-        // The risk of waiting is that the datatype thread might not respond.
-        return;
-      }
-      break;
-    case MODEL_STARTING:
-      // It is possible for a model to fail to start. For example, if the
-      // password store on a machine is unavailable, the password model will not
-      // start. In such cases, we should stop the model instead of crashing.
-      state_ = STOPPING;
-      StopModels();
-      return;
-    case DISABLED:
-      state_ = NOT_RUNNING;
-      StopModels();
-      return;
-    default:
-      DCHECK_EQ(state_, RUNNING);
-      state_ = STOPPING;
-      StopModels();
-      break;
+  // Disconnect on UI and post task to destroy on backend.
+  if (components_container_) {
+    components_container_->Disconnect();
+    PostTaskOnBackendThread(
+          FROM_HERE,
+          base::Bind(&DestroyComponentsInBackend,
+                     components_container_.release()));
+    model_associator_ = NULL;
+    change_processor_ = NULL;
   }
-  DCHECK(start_callback_.is_null());
 
-  // Deactivate the change processor on the UI thread. We dont want to listen
-  // for any more changes or process them from server.
-  profile_sync_service_->DeactivateDataType(type());
+  // Call start callback if waiting for association.
+  if (state_ == ASSOCIATING) {
+    StartDone(ABORTED,
+              syncer::SyncMergeResult(type()),
+              syncer::SyncMergeResult(type()));
 
-  if (!StopAssociationAsync()) {
-    // We do DFATAL here because this will eventually lead to a failed CHECK
-    // when the change processor gets destroyed on the wrong thread.
-    LOG(DFATAL) << "Failed to destroy datatype " << name();
   }
+
   state_ = NOT_RUNNING;
 }
 
@@ -239,13 +306,15 @@
       profile_sync_factory_(NULL),
       profile_(NULL),
       profile_sync_service_(NULL),
-      abort_association_(false),
-      abort_association_complete_(false, false),
-      start_association_called_(true, false) {
+      model_associator_(NULL),
+      change_processor_(NULL),
+      weak_ptr_factory_(this) {
 }
 
 NonFrontendDataTypeController::~NonFrontendDataTypeController() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!change_processor_);
+  DCHECK(!model_associator_);
 }
 
 bool NonFrontendDataTypeController::StartModels() {
@@ -260,7 +329,7 @@
     DataTypeController::StartResult start_result,
     const syncer::SyncMergeResult& local_merge_result,
     const syncer::SyncMergeResult& syncer_merge_result) {
-  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DataTypeController::State new_state;
 
   if (IsSuccessfulResult(start_result)) {
@@ -269,20 +338,10 @@
     new_state = (start_result == ASSOCIATION_FAILED ? DISABLED : NOT_RUNNING);
     if (IsUnrecoverableResult(start_result))
       RecordUnrecoverableError(FROM_HERE, "StartFailed");
-    StopAssociation();
   }
 
-  abort_association_complete_.Signal();
-  base::AutoLock lock(abort_association_lock_);
-  if (!abort_association_) {
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-        base::Bind(&NonFrontendDataTypeController::StartDoneImpl,
-                   this,
-                   start_result,
-                   new_state,
-                   local_merge_result,
-                   syncer_merge_result));
-  }
+  StartDoneImpl(start_result, new_state, local_merge_result,
+                syncer_merge_result);
 }
 
 void NonFrontendDataTypeController::StartDoneImpl(
@@ -291,21 +350,14 @@
     const syncer::SyncMergeResult& local_merge_result,
     const syncer::SyncMergeResult& syncer_merge_result) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  // It's possible to have StartDoneImpl called first from the UI thread
-  // (due to Stop being called) and then posted from the non-UI thread. In
-  // this case, we drop the second call because we've already been stopped.
-  if (state_ == NOT_RUNNING) {
-    DCHECK(start_callback_.is_null());
-    return;
-  }
 
   state_ = new_state;
   if (state_ != RUNNING) {
     // Start failed.
-    StopModels();
     RecordStartFailure(start_result);
   }
 
+  DCHECK(!start_callback_.is_null());
   // We have to release the callback before we call it, since it's possible
   // invoking the callback will trigger a call to STOP(), which will get
   // confused by the non-NULL start_callback_.
@@ -314,13 +366,6 @@
   callback.Run(start_result, local_merge_result, syncer_merge_result);
 }
 
-void NonFrontendDataTypeController::StopModels() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(state_ == STOPPING || state_ == NOT_RUNNING || state_ == DISABLED);
-  DVLOG(1) << "NonFrontendDataTypeController::StopModels(): State = " << state_;
-  // Do nothing by default.
-}
-
 void NonFrontendDataTypeController::DisableImpl(
     const tracked_objects::Location& from_here,
     const std::string& message) {
@@ -330,7 +375,7 @@
 
 void NonFrontendDataTypeController::RecordAssociationTime(
     base::TimeDelta time) {
-  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 #define PER_DATA_TYPE_MACRO(type_str) \
     UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time);
   SYNC_DATA_TYPE_HISTOGRAM(type());
@@ -349,14 +394,6 @@
 #undef PER_DATA_TYPE_MACRO
 }
 
-bool NonFrontendDataTypeController::StartAssociationAsync() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK_EQ(state(), ASSOCIATING);
-  return PostTaskOnBackendThread(
-      FROM_HERE,
-      base::Bind(&NonFrontendDataTypeController::StartAssociation, this));
-}
-
 ProfileSyncComponentsFactory*
     NonFrontendDataTypeController::profile_sync_factory() const {
   return profile_sync_factory_;
@@ -381,117 +418,49 @@
 }
 
 AssociatorInterface* NonFrontendDataTypeController::associator() const {
-  return model_associator_.get();
-}
-
-void NonFrontendDataTypeController::set_model_associator(
-    AssociatorInterface* associator) {
-  model_associator_.reset(associator);
+  return model_associator_;
 }
 
 ChangeProcessor* NonFrontendDataTypeController::change_processor() const {
-  return change_processor_.get();
+  return change_processor_;
 }
 
-void NonFrontendDataTypeController::set_change_processor(
-    ChangeProcessor* change_processor) {
-  change_processor_.reset(change_processor);
-}
+void NonFrontendDataTypeController::AssociationCallback(
+    AssociationResult result) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-void NonFrontendDataTypeController::StartAssociation() {
-  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
-  syncer::SyncMergeResult local_merge_result(type());
-  syncer::SyncMergeResult syncer_merge_result(type());
-
-  {
-    base::AutoLock lock(abort_association_lock_);
-    if (abort_association_) {
-      abort_association_complete_.Signal();
-      return;
-    }
-    start_association_called_.Signal();
-    CreateSyncComponents();
-  }
-
-  DCHECK_EQ(state_, ASSOCIATING);
-
-  if (!model_associator_->CryptoReadyIfNecessary()) {
+  if (result.needs_crypto) {
     StartDone(NEEDS_CRYPTO,
-              local_merge_result,
-              syncer_merge_result);
+              result.local_merge_result,
+              result.syncer_merge_result);
     return;
   }
 
-  bool sync_has_nodes = false;
-  if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
-    syncer::SyncError error(FROM_HERE,
-                            syncer::SyncError::UNRECOVERABLE_ERROR,
-                            "Failed to load sync nodes",
-                            type());
-    local_merge_result.set_error(error);
+  if (result.unrecoverable_error) {
     StartDone(UNRECOVERABLE_ERROR,
-              local_merge_result,
-              syncer_merge_result);
+              result.local_merge_result,
+              result.syncer_merge_result);
     return;
   }
 
-  // TODO(zea): Have AssociateModels fill the local and syncer merge results.
-  base::TimeTicks start_time = base::TimeTicks::Now();
-  syncer::SyncError error = model_associator_->AssociateModels(
-      &local_merge_result,
-      &syncer_merge_result);
-  // TODO(lipalani): crbug.com/122690 - handle abort.
-  RecordAssociationTime(base::TimeTicks::Now() - start_time);
-  if (error.IsSet()) {
-    local_merge_result.set_error(error);
+  RecordAssociationTime(result.association_time);
+  if (result.error.IsSet()) {
     StartDone(ASSOCIATION_FAILED,
-              local_merge_result,
-              syncer_merge_result);
+              result.local_merge_result,
+              result.syncer_merge_result);
     return;
   }
 
+  CHECK(result.change_processor);
+  CHECK(result.model_associator);
+  change_processor_ = result.change_processor;
+  model_associator_ = result.model_associator;
+
   profile_sync_service_->ActivateDataType(type(), model_safe_group(),
                                           change_processor());
-  StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK,
-            local_merge_result,
-            syncer_merge_result);
-}
-
-bool NonFrontendDataTypeController::StopAssociationAsync() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK_EQ(state(), STOPPING);
-  if (PostTaskOnBackendThread(
-          FROM_HERE,
-          base::Bind(
-              &NonFrontendDataTypeController::StopAssociation, this))) {
-    // The remote thread will hold on to a reference to this object until
-    // the StopAssociation task finishes running. We want to make sure that we
-    // do not return from this routine until there are no more references to
-    // this object on the remote thread, so we queue up the SignalCallback
-    // task below - this task does not maintain a reference to the DTC, so
-    // when it signals this thread, we know that the previous task has executed
-    // and there are no more lingering remote references to the DTC.
-    // This fixes the race described in http://crbug.com/127706.
-    base::WaitableEvent datatype_stopped(false, false);
-    if (PostTaskOnBackendThread(
-            FROM_HERE,
-            base::Bind(&SignalCallback, &datatype_stopped))) {
-      datatype_stopped.Wait();
-      return true;
-    }
-  }
-  return false;
-}
-
-void NonFrontendDataTypeController::StopAssociation() {
-  DCHECK(!HasOneRef());
-  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (model_associator_) {
-    syncer::SyncError error;  // Not used.
-    error = model_associator_->DisassociateModels();
-  }
-  model_associator_.reset();
-  change_processor_.reset();
+  StartDone(!result.sync_has_nodes ? OK_FIRST_RUN : OK,
+            result.local_merge_result,
+            result.syncer_merge_result);
 }
 
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller.h b/chrome/browser/sync/glue/non_frontend_data_type_controller.h
index 8a67b12..35cacb0 100644
--- a/chrome/browser/sync/glue/non_frontend_data_type_controller.h
+++ b/chrome/browser/sync/glue/non_frontend_data_type_controller.h
@@ -15,6 +15,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "chrome/browser/sync/glue/data_type_controller.h"
 #include "chrome/browser/sync/glue/data_type_error_handler.h"
+#include "chrome/browser/sync/profile_sync_components_factory.h"
 
 class Profile;
 class ProfileSyncService;
@@ -36,13 +37,16 @@
 // Implementation for datatypes that do not reside on the frontend thread
 // (UI thread). This is the same thread we perform initialization
 // on, so we don't have to worry about thread safety. The main start/stop
-// funtionality is implemented by default. Derived classes must implement:
+// functionality is implemented by default. Derived classes must implement:
 //    type()
 //    model_safe_group()
 //    PostTaskOnBackendThread()
 //    CreateSyncComponents()
 class NonFrontendDataTypeController : public DataTypeController {
  public:
+  // For creating non-frontend processor/associator and associating on backend.
+  class BackendComponentsContainer;
+
   NonFrontendDataTypeController(
       ProfileSyncComponentsFactory* profile_sync_factory,
       Profile* profile,
@@ -64,6 +68,22 @@
       const tracked_objects::Location& from_here,
       const std::string& message) OVERRIDE;
 
+  // Callback to receive background association results.
+  struct AssociationResult {
+    explicit AssociationResult(syncer::ModelType type);
+    ~AssociationResult();
+    bool needs_crypto;
+    bool unrecoverable_error;
+    bool sync_has_nodes;
+    syncer::SyncError error;
+    syncer::SyncMergeResult local_merge_result;
+    syncer::SyncMergeResult syncer_merge_result;
+    base::TimeDelta association_time;
+    ChangeProcessor* change_processor;
+    AssociatorInterface* model_associator;
+  };
+  void AssociationCallback(AssociationResult result);
+
  protected:
   // For testing only.
   NonFrontendDataTypeController();
@@ -94,7 +114,12 @@
 
   // Datatype specific creation of sync components.
   // Note: this is performed on the datatype's thread.
-  virtual void CreateSyncComponents() = 0;
+  virtual ProfileSyncComponentsFactory::SyncComponents
+      CreateSyncComponents() = 0;
+
+  // Called on UI thread during shutdown to effectively disable processing
+  // any changes.
+  virtual void DisconnectProcessor(ChangeProcessor* processor) = 0;
 
   // Start up complete, update the state and invoke the callback.
   // Note: this is performed on the datatype's thread.
@@ -110,11 +135,6 @@
       const syncer::SyncMergeResult& local_merge_result,
       const syncer::SyncMergeResult& syncer_merge_result);
 
-  // Perform any DataType controller specific state cleanup before stopping
-  // the datatype controller. The default implementation is a no-op.
-  // Note: this is performed on the frontend (UI) thread.
-  virtual void StopModels();
-
   // The actual implementation of Disabling the datatype. This happens
   // on the UI thread.
   virtual void DisableImpl(const tracked_objects::Location& from_here,
@@ -133,60 +153,26 @@
   void set_state(State state);
 
   virtual AssociatorInterface* associator() const;
-  virtual void set_model_associator(AssociatorInterface* associator);
   virtual ChangeProcessor* change_processor() const;
-  virtual void set_change_processor(ChangeProcessor* change_processor);
 
   State state_;
   StartCallback start_callback_;
   ModelLoadCallback model_load_callback_;
 
  private:
-  // Post the association task to the thread the datatype lives on.
-  // Note: this is performed on the frontend (UI) thread.
-  // Return value: True if task posted successfully, False otherwise.
-  //
-  // TODO(akalin): Callers handle false return values inconsistently;
-  // some set the state to NOT_RUNNING, and some set the state to
-  // DISABLED.  Move the error handling inside this function to be
-  // consistent.
-  virtual bool StartAssociationAsync();
-
-  // Build sync components and associate models.
-  // Note: this is performed on the datatype's thread.
-  void StartAssociation();
-
-  // Helper method to stop associating.
-  void StopWhileAssociating();
-
-  // Post the StopAssociation task to the thread the datatype lives on.
-  // Note: this is performed on the frontend (UI) thread.
-  // Return value: True if task posted successfully, False otherwise.
-  bool StopAssociationAsync();
-
-  // Disassociate the models and destroy the sync components.
-  // Note: this is performed on the datatype's thread.
-  void StopAssociation();
-
+  friend class BackendComponentsContainer;
   ProfileSyncComponentsFactory* const profile_sync_factory_;
   Profile* const profile_;
   ProfileSyncService* const profile_sync_service_;
 
-  scoped_ptr<AssociatorInterface> model_associator_;
-  scoped_ptr<ChangeProcessor> change_processor_;
+  // Created on UI thread and passed to backend to create processor/associator
+  // and associate model. Released on backend.
+  scoped_ptr<BackendComponentsContainer> components_container_;
 
-  // Locks/Barriers for aborting association early.
-  base::Lock abort_association_lock_;
-  bool abort_association_;
-  base::WaitableEvent abort_association_complete_;
+  AssociatorInterface* model_associator_;
+  ChangeProcessor* change_processor_;
 
-  // This is added for debugging purpose.
-  // TODO(lipalani): Remove this after debugging.
-  base::WaitableEvent start_association_called_;
-
-  // This is added for debugging purpose.
-  // TODO(lipalani): Remove after debugging.
-  bool start_models_failed_;
+  base::WeakPtrFactory<NonFrontendDataTypeController> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(NonFrontendDataTypeController);
 };
diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h b/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h
index a9cbfaf..6d3adc4 100644
--- a/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h
+++ b/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h
@@ -36,7 +36,8 @@
                bool(const tracked_objects::Location&,
                     const base::Closure&));
   MOCK_METHOD0(StartAssociation, void());
-  MOCK_METHOD0(CreateSyncComponents, void());
+  MOCK_METHOD0(CreateSyncComponents,
+               ProfileSyncComponentsFactory::SyncComponents());
   MOCK_METHOD3(StartDone,
                void(DataTypeController::StartResult result,
                     const syncer::SyncMergeResult& local_merge_result,
@@ -46,8 +47,7 @@
                     DataTypeController::State new_state,
                     const syncer::SyncMergeResult& local_merge_result,
                     const syncer::SyncMergeResult& syncer_merge_result));
-  MOCK_METHOD0(StopModels, void());
-  MOCK_METHOD0(StopAssociation, void());
+  MOCK_METHOD1(DisconnectProcessor, void(ChangeProcessor*));
   MOCK_METHOD2(OnUnrecoverableErrorImpl, void(const tracked_objects::Location&,
                                               const std::string&));
   MOCK_METHOD2(RecordUnrecoverableError, void(const tracked_objects::Location&,
diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller_unittest.cc b/chrome/browser/sync/glue/non_frontend_data_type_controller_unittest.cc
index 3a0f577..77f5e9e 100644
--- a/chrome/browser/sync/glue/non_frontend_data_type_controller_unittest.cc
+++ b/chrome/browser/sync/glue/non_frontend_data_type_controller_unittest.cc
@@ -69,12 +69,10 @@
  private:
   virtual ~NonFrontendDataTypeControllerFake() {}
 
-  virtual void CreateSyncComponents() OVERRIDE {
-    ProfileSyncComponentsFactory::SyncComponents sync_components =
-        profile_sync_factory()->
+  virtual ProfileSyncComponentsFactory::SyncComponents
+  CreateSyncComponents() OVERRIDE {
+    return profile_sync_factory()->
             CreateBookmarkSyncComponents(profile_sync_service(), this);
-    set_model_associator(sync_components.model_associator);
-    set_change_processor(sync_components.change_processor);
   }
 
   virtual bool PostTaskOnBackendThread(
@@ -88,9 +86,6 @@
   virtual bool StartModels() OVERRIDE {
     return mock_->StartModels();
   }
-  virtual void StopModels() OVERRIDE {
-    mock_->StopModels();
-  }
   virtual void RecordUnrecoverableError(
       const tracked_objects::Location& from_here,
       const std::string& message) OVERRIDE {
@@ -103,6 +98,11 @@
       DataTypeController::StartResult result) OVERRIDE {
     mock_->RecordStartFailure(result);
   }
+  virtual void DisconnectProcessor(
+      browser_sync::ChangeProcessor* processor) OVERRIDE{
+    mock_->DisconnectProcessor(processor);
+  }
+
  private:
   NonFrontendDataTypeControllerMock* mock_;
 };
@@ -130,6 +130,10 @@
   }
 
   virtual void TearDown() {
+    if (non_frontend_dtc_->state() !=
+        NonFrontendDataTypeController::NOT_RUNNING) {
+      non_frontend_dtc_->Stop();
+    }
     db_thread_.Stop();
   }
 
@@ -160,14 +164,13 @@
   }
 
   void SetStopExpectations() {
-    EXPECT_CALL(*dtc_mock_.get(), StopModels());
+    EXPECT_CALL(*dtc_mock_.get(), DisconnectProcessor(_));
     EXPECT_CALL(service_, DeactivateDataType(_));
     EXPECT_CALL(*model_associator_, DisassociateModels()).
                 WillOnce(Return(syncer::SyncError()));
   }
 
   void SetStartFailExpectations(DataTypeController::StartResult result) {
-    EXPECT_CALL(*dtc_mock_.get(), StopModels());
     if (DataTypeController::IsUnrecoverableResult(result))
       EXPECT_CALL(*dtc_mock_.get(), RecordUnrecoverableError(_, _));
     if (model_associator_) {
@@ -220,6 +223,7 @@
   SetStartExpectations();
   SetAssociateExpectations();
   SetActivateExpectations(DataTypeController::OK);
+  SetStopExpectations();
   EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
   Start();
   WaitForDTC();
@@ -236,6 +240,7 @@
       WillOnce(Return(syncer::SyncError()));
   EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
   SetActivateExpectations(DataTypeController::OK_FIRST_RUN);
+  SetStopExpectations();
   EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
   Start();
   WaitForDTC();
@@ -309,8 +314,6 @@
       SignalEvent(&pause_db_thread));
   EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
               WillOnce(Return(syncer::SyncError()));
-  EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
-  EXPECT_CALL(service_, ActivateDataType(_, _, _));
   EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED,_,_));
   EXPECT_CALL(*dtc_mock_.get(),
               RecordStartFailure(DataTypeController::ABORTED));
@@ -325,8 +328,8 @@
 
 // Same as above but abort during the Activate call.
 TEST_F(SyncNonFrontendDataTypeControllerTest, AbortDuringAssociationActivated) {
-  WaitableEvent wait_for_db_thread_pause(false, false);
-  WaitableEvent pause_db_thread(false, false);
+  WaitableEvent wait_for_association_starts(false, false);
+  WaitableEvent wait_for_dtc_stop(false, false);
 
   SetStartExpectations();
   EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
@@ -335,21 +338,21 @@
       WillOnce(DoAll(
           SetArgumentPointee<0>(true),
           Return(true)));
-  EXPECT_CALL(*model_associator_, AbortAssociation()).WillOnce(
-      SignalEvent(&pause_db_thread));
+  EXPECT_CALL(*model_associator_, AbortAssociation());
   EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
-      WillOnce(Return(syncer::SyncError()));
-  EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
-  EXPECT_CALL(service_, ActivateDataType(_, _, _)).WillOnce(DoAll(
-      SignalEvent(&wait_for_db_thread_pause), WaitOnEvent(&pause_db_thread)));
+      WillOnce(DoAll(
+          SignalEvent(&wait_for_association_starts),
+          WaitOnEvent(&wait_for_dtc_stop),
+          Return(syncer::SyncError())));
   EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED,_,_));
   EXPECT_CALL(*dtc_mock_.get(),
               RecordStartFailure(DataTypeController::ABORTED));
   SetStopExpectations();
   EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
   Start();
-  wait_for_db_thread_pause.Wait();
+  wait_for_association_starts.Wait();
   non_frontend_dtc_->Stop();
+  wait_for_dtc_stop.Signal();
   WaitForDTC();
   EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
 }
diff --git a/chrome/browser/sync/glue/password_change_processor.cc b/chrome/browser/sync/glue/password_change_processor.cc
index 83b8cb5..e6969b7 100644
--- a/chrome/browser/sync/glue/password_change_processor.cc
+++ b/chrome/browser/sync/glue/password_change_processor.cc
@@ -35,7 +35,8 @@
     : ChangeProcessor(error_handler),
       model_associator_(model_associator),
       password_store_(password_store),
-      expected_loop_(base::MessageLoop::current()) {
+      expected_loop_(base::MessageLoop::current()),
+      disconnected_(false) {
   DCHECK(model_associator);
   DCHECK(error_handler);
 #if defined(OS_MACOSX)
@@ -56,6 +57,10 @@
   DCHECK(expected_loop_ == base::MessageLoop::current());
   DCHECK(chrome::NOTIFICATION_LOGINS_CHANGED == type);
 
+  base::AutoLock lock(disconnect_lock_);
+  if (disconnected_)
+    return;
+
   syncer::WriteTransaction trans(FROM_HERE, share_handle());
 
   syncer::ReadNode password_root(&trans);
@@ -162,6 +167,9 @@
     int64 model_version,
     const syncer::ImmutableChangeRecordList& changes) {
   DCHECK(expected_loop_ == base::MessageLoop::current());
+  base::AutoLock lock(disconnect_lock_);
+  if (disconnected_)
+    return;
 
   syncer::ReadNode password_root(trans);
   if (password_root.InitByTagLookup(kPasswordTag) !=
@@ -220,6 +228,10 @@
 
 void PasswordChangeProcessor::CommitChangesFromSyncModel() {
   DCHECK(expected_loop_ == base::MessageLoop::current());
+  base::AutoLock lock(disconnect_lock_);
+  if (disconnected_)
+    return;
+
   ScopedStopObserving<PasswordChangeProcessor> stop_observing(this);
 
   syncer::SyncError error = model_associator_->WriteToPasswordStore(
@@ -237,9 +249,17 @@
   updated_passwords_.clear();
 }
 
+void PasswordChangeProcessor::Disconnect() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  base::AutoLock lock(disconnect_lock_);
+  disconnected_ = true;
+}
+
 void PasswordChangeProcessor::StartImpl(Profile* profile) {
-  DCHECK(expected_loop_ == base::MessageLoop::current());
-  StartObserving();
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  password_store_->ScheduleTask(
+      base::Bind(&PasswordChangeProcessor::StartObserving,
+                 base::Unretained(this)));
 }
 
 void PasswordChangeProcessor::StartObserving() {
diff --git a/chrome/browser/sync/glue/password_change_processor.h b/chrome/browser/sync/glue/password_change_processor.h
index ed4b54c..11e4372 100644
--- a/chrome/browser/sync/glue/password_change_processor.h
+++ b/chrome/browser/sync/glue/password_change_processor.h
@@ -56,6 +56,9 @@
   // thread (http://crbug.com/70658).
   virtual void CommitChangesFromSyncModel() OVERRIDE;
 
+  // Stop processing changes and wait for being destroyed.
+  void Disconnect();
+
  protected:
   virtual void StartImpl(Profile* profile) OVERRIDE;
 
@@ -82,6 +85,11 @@
 
   base::MessageLoop* expected_loop_;
 
+  // If disconnected is true, local/sync changes are dropped without modifying
+  // sync/local models.
+  bool disconnected_;
+  base::Lock disconnect_lock_;
+
   DISALLOW_COPY_AND_ASSIGN(PasswordChangeProcessor);
 };
 
diff --git a/chrome/browser/sync/glue/password_data_type_controller.cc b/chrome/browser/sync/glue/password_data_type_controller.cc
index db8df21..688c2a6 100644
--- a/chrome/browser/sync/glue/password_data_type_controller.cc
+++ b/chrome/browser/sync/glue/password_data_type_controller.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/password_manager/password_store.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/profile_sync_components_factory.h"
+#include "chrome/browser/sync/glue/password_change_processor.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "sync/api/sync_error.h"
@@ -42,7 +42,8 @@
       const tracked_objects::Location& from_here,
       const base::Closure& task) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(password_store_.get());
+  if (!password_store_)
+    return false;
   return password_store_->ScheduleTask(task);
 }
 
@@ -54,16 +55,19 @@
   return password_store_.get() != NULL;
 }
 
-void PasswordDataTypeController::CreateSyncComponents() {
+ProfileSyncComponentsFactory::SyncComponents
+PasswordDataTypeController::CreateSyncComponents() {
   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK_EQ(state(), ASSOCIATING);
-  ProfileSyncComponentsFactory::SyncComponents sync_components =
-      profile_sync_factory()->CreatePasswordSyncComponents(
-          profile_sync_service(),
-          password_store_.get(),
-          this);
-  set_model_associator(sync_components.model_associator);
-  set_change_processor(sync_components.change_processor);
+  return profile_sync_factory()->CreatePasswordSyncComponents(
+      profile_sync_service(),
+      password_store_.get(),
+      this);
+}
+
+void PasswordDataTypeController::DisconnectProcessor(
+    ChangeProcessor* processor) {
+  static_cast<PasswordChangeProcessor*>(processor)->Disconnect();
 }
 
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/glue/password_data_type_controller.h b/chrome/browser/sync/glue/password_data_type_controller.h
index 75bbf4b..d9bcdf0 100644
--- a/chrome/browser/sync/glue/password_data_type_controller.h
+++ b/chrome/browser/sync/glue/password_data_type_controller.h
@@ -35,7 +35,9 @@
       const tracked_objects::Location& from_here,
       const base::Closure& task) OVERRIDE;
   virtual bool StartModels() OVERRIDE;
-  virtual void CreateSyncComponents() OVERRIDE;
+  virtual ProfileSyncComponentsFactory::SyncComponents CreateSyncComponents()
+      OVERRIDE;
+  virtual void DisconnectProcessor(ChangeProcessor* processor) OVERRIDE;
 
  private:
   scoped_refptr<PasswordStore> password_store_;
diff --git a/chrome/browser/sync/glue/password_model_associator.cc b/chrome/browser/sync/glue/password_model_associator.cc
index f034c47..9641678 100644
--- a/chrome/browser/sync/glue/password_model_associator.cc
+++ b/chrome/browser/sync/glue/password_model_associator.cc
@@ -34,7 +34,7 @@
     : sync_service_(sync_service),
       password_store_(password_store),
       password_node_id_(syncer::kInvalidId),
-      abort_association_pending_(false),
+      abort_association_requested_(false),
       expected_loop_(base::MessageLoop::current()),
       error_handler_(error_handler) {
   DCHECK(sync_service_);
@@ -45,17 +45,14 @@
 #endif
 }
 
-PasswordModelAssociator::~PasswordModelAssociator() {}
+PasswordModelAssociator::~PasswordModelAssociator() {
+  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
 
 syncer::SyncError PasswordModelAssociator::AssociateModels(
     syncer::SyncMergeResult* local_merge_result,
     syncer::SyncMergeResult* syncer_merge_result) {
-  syncer::SyncError error;
   DCHECK(expected_loop_ == base::MessageLoop::current());
-  {
-    base::AutoLock lock(abort_association_pending_lock_);
-    abort_association_pending_ = false;
-  }
 
   // We must not be holding a transaction when we interact with the password
   // store, as it can post tasks to the UI thread which can itself be blocked
@@ -76,10 +73,14 @@
                              model_type());
   }
 
-  std::set<std::string> current_passwords;
   PasswordVector new_passwords;
   PasswordVector updated_passwords;
   {
+    base::AutoLock lock(association_lock_);
+    if (abort_association_requested_)
+      return syncer::SyncError();
+
+    std::set<std::string> current_passwords;
     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
     syncer::ReadNode password_root(&trans);
     if (password_root.InitByTagLookup(kPasswordTag) !=
@@ -94,9 +95,6 @@
     for (std::vector<content::PasswordForm*>::iterator ix =
              passwords.begin();
          ix != passwords.end(); ++ix) {
-      if (IsAbortPending()) {
-        return syncer::SyncError();
-      }
       std::string tag = MakeTag(**ix);
 
       syncer::ReadNode node(&trans);
@@ -176,14 +174,9 @@
   // We must not be holding a transaction when we interact with the password
   // store, as it can post tasks to the UI thread which can itself be blocked
   // on our transaction, resulting in deadlock. (http://crbug.com/70658)
-  error = WriteToPasswordStore(&new_passwords,
-                               &updated_passwords,
-                               NULL);
-  if (error.IsSet()) {
-    return error;
-  }
-
-  return error;
+  return WriteToPasswordStore(&new_passwords,
+                              &updated_passwords,
+                              NULL);
 }
 
 bool PasswordModelAssociator::DeleteAllNodes(
@@ -238,8 +231,8 @@
 
 void PasswordModelAssociator::AbortAssociation() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  base::AutoLock lock(abort_association_pending_lock_);
-  abort_association_pending_ = true;
+  base::AutoLock lock(association_lock_);
+  abort_association_requested_ = true;
 }
 
 bool PasswordModelAssociator::CryptoReadyIfNecessary() {
@@ -260,11 +253,6 @@
   return false;
 }
 
-bool PasswordModelAssociator::IsAbortPending() {
-  base::AutoLock lock(abort_association_pending_lock_);
-  return abort_association_pending_;
-}
-
 int64 PasswordModelAssociator::GetSyncIdFromChromeId(
     const std::string& password) {
   PasswordToSyncIdMap::const_iterator iter = id_map_.find(password);
diff --git a/chrome/browser/sync/glue/password_model_associator.h b/chrome/browser/sync/glue/password_model_associator.h
index c1e2cf3..38effd6 100644
--- a/chrome/browser/sync/glue/password_model_associator.h
+++ b/chrome/browser/sync/glue/password_model_associator.h
@@ -118,10 +118,6 @@
   static void WriteToSyncNode(const content::PasswordForm& password_form,
                               syncer::WriteNode* node);
 
-  // Called at various points in model association to determine if the
-  // user requested an abort.
-  bool IsAbortPending();
-
  private:
   typedef std::map<std::string, int64> PasswordToSyncIdMap;
   typedef std::map<int64, std::string> SyncIdToPasswordMap;
@@ -130,11 +126,9 @@
   PasswordStore* password_store_;
   int64 password_node_id_;
 
-  // Abort association pending flag and lock.  If this is set to true
-  // (via the AbortAssociation method), return from the
-  // AssociateModels method as soon as possible.
-  base::Lock abort_association_pending_lock_;
-  bool abort_association_pending_;
+  // Set true by AbortAssociation.
+  bool abort_association_requested_;
+  base::Lock association_lock_;
 
   base::MessageLoop* expected_loop_;
 
diff --git a/chrome/browser/sync/glue/password_model_worker.cc b/chrome/browser/sync/glue/password_model_worker.cc
index a3a3037..beaf5b1 100644
--- a/chrome/browser/sync/glue/password_model_worker.cc
+++ b/chrome/browser/sync/glue/password_model_worker.cc
@@ -56,6 +56,7 @@
 
 void PasswordModelWorker::RegisterForPasswordLoopDestruction() {
   base::MessageLoop::current()->AddDestructionObserver(this);
+  SetWorkingLoopToCurrent();
 }
 
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/glue/session_model_associator.cc b/chrome/browser/sync/glue/session_model_associator.cc
index 777fa96..4ff62c7 100644
--- a/chrome/browser/sync/glue/session_model_associator.cc
+++ b/chrome/browser/sync/glue/session_model_associator.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/safe_numerics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -114,7 +115,7 @@
 SessionModelAssociator::SessionModelAssociator(
     ProfileSyncService* sync_service,
     DataTypeErrorHandler* error_handler)
-    : tab_pool_(sync_service),
+    : local_tab_pool_(sync_service),
       local_session_syncid_(syncer::kInvalidId),
       sync_service_(sync_service),
       stale_session_threshold_days_(kDefaultStaleSessionThresholdDays),
@@ -132,7 +133,7 @@
 
 SessionModelAssociator::SessionModelAssociator(ProfileSyncService* sync_service,
                                                bool setup_for_test)
-    : tab_pool_(sync_service),
+    : local_tab_pool_(sync_service),
       local_session_syncid_(syncer::kInvalidId),
       sync_service_(sync_service),
       stale_session_threshold_days_(kDefaultStaleSessionThresholdDays),
@@ -236,7 +237,7 @@
           // Note: We cannot check if a tab is valid if it has no WebContents.
           // We assume any such tab is valid and leave the contents of
           // corresponding sync node unchanged.
-          if (synced_tab->GetSyncId() > syncer::kInvalidId &&
+          if (synced_tab->GetSyncId() > TabNodePool::kInvalidTabNodeID &&
               tab_id > TabNodePool::kInvalidTabID) {
             UpdateTabIdIfNecessary(synced_tab->GetSyncId(), tab_id);
             found_tabs = true;
@@ -286,7 +287,7 @@
   }
 
   // Free old sync nodes.
-  tab_pool_.FreeUnassociatedTabNodes();
+  local_tab_pool_.FreeUnassociatedTabNodes();
   // Free memory for closed windows and tabs.
   synced_session_tracker_.CleanupSession(local_tag);
 
@@ -333,32 +334,32 @@
                                           syncer::SyncError* error) {
   DCHECK(CalledOnValidThread());
   DCHECK(tab->HasWebContents());
-  int64 sync_id;
+  int tab_node_id(TabNodePool::kInvalidTabNodeID);
   SessionID::id_type tab_id = tab->GetSessionId();
   if (tab->IsBeingDestroyed()) {
     // This tab is closing.
-    TabLinksMap::iterator tab_iter = tab_map_.find(tab_id);
-    if (tab_iter == tab_map_.end()) {
+    TabLinksMap::iterator tab_iter = local_tab_map_.find(tab_id);
+    if (tab_iter == local_tab_map_.end()) {
       // We aren't tracking this tab (for example, sync setting page).
       return true;
     }
-    tab_pool_.FreeTabNode(tab_iter->second->sync_id());
-    tab_map_.erase(tab_iter);
+    local_tab_pool_.FreeTabNode(tab_iter->second->tab_node_id());
+    local_tab_map_.erase(tab_iter);
     return true;
   }
 
   if (!ShouldSyncTab(*tab))
     return true;
 
-  TabLinksMap::iterator tab_map_iter = tab_map_.find(tab_id);
+  TabLinksMap::iterator local_tab_map_iter = local_tab_map_.find(tab_id);
   TabLink* tab_link = NULL;
-  if (tab_map_iter == tab_map_.end()) {
-    sync_id = tab->GetSyncId();
+  if (local_tab_map_iter == local_tab_map_.end()) {
+    tab_node_id = tab->GetSyncId();
     // if there is an old sync node for the tab, reuse it.
-    if (!tab_pool_.IsUnassociatedTabNode(sync_id)) {
+    if (!local_tab_pool_.IsUnassociatedTabNode(tab_node_id)) {
       // This is a new tab, get a sync node for it.
-      sync_id = tab_pool_.GetFreeTabNode();
-      if (sync_id == syncer::kInvalidId) {
+      tab_node_id = local_tab_pool_.GetFreeTabNode();
+      if (tab_node_id == TabNodePool::kInvalidTabNodeID) {
         if (error) {
           *error = error_handler_->CreateAndUploadError(
               FROM_HERE,
@@ -367,20 +368,20 @@
         }
         return false;
       }
-      tab->SetSyncId(sync_id);
+      tab->SetSyncId(tab_node_id);
     }
-    tab_pool_.AssociateTabNode(sync_id, tab_id);
-    tab_link = new TabLink(sync_id, tab);
-    tab_map_[tab_id] = make_linked_ptr<TabLink>(tab_link);
+    local_tab_pool_.AssociateTabNode(tab_node_id, tab_id);
+    tab_link = new TabLink(tab_node_id, tab);
+    local_tab_map_[tab_id] = make_linked_ptr<TabLink>(tab_link);
   } else {
     // This tab is already associated with a sync node, reuse it.
     // Note: on some platforms the tab object may have changed, so we ensure
     // the tab link is up to date.
-    tab_link = tab_map_iter->second.get();
-    tab_map_iter->second->set_tab(tab);
+    tab_link = local_tab_map_iter->second.get();
+    local_tab_map_iter->second->set_tab(tab);
   }
   DCHECK(tab_link);
-  DCHECK_NE(tab_link->sync_id(), syncer::kInvalidId);
+  DCHECK_NE(tab_link->tab_node_id(), TabNodePool::kInvalidTabNodeID);
 
   DVLOG(1) << "Reloading tab " << tab_id << " from window "
            << tab->GetWindowId();
@@ -418,22 +419,57 @@
     syncer::SyncError* error) {
   DCHECK(CalledOnValidThread());
   const SyncedTabDelegate& tab_delegate = *(tab_link->tab());
-  int64 sync_id = tab_link->sync_id();
+  int tab_node_id = tab_link->tab_node_id();
   GURL old_tab_url = tab_link->url();
-
-  // Load the last stored version of this tab so we can compare changes. If this
-  // is a new tab, session_tab will be a blank/newly created SessionTab object.
-  SessionTab* session_tab =
-      synced_session_tracker_.GetTab(GetCurrentMachineTag(),
-                                     tab_delegate.GetSessionId());
-
-  SetSessionTabFromDelegate(tab_delegate, base::Time::Now(), session_tab);
-
   const GURL new_url = GetCurrentVirtualURL(tab_delegate);
   DVLOG(1) << "Local tab " << tab_delegate.GetSessionId()
            << " now has URL " << new_url.spec();
 
-  // Trigger the favicon load if needed. We do this before opening the write
+  SessionTab* session_tab = NULL;
+  {
+    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
+    syncer::WriteNode tab_node(&trans);
+    if (tab_node.InitByClientTagLookup(
+            syncer::SESSIONS,
+            TabNodePool::TabIdToTag(current_machine_tag_, tab_node_id)) !=
+        syncer::BaseNode::INIT_OK) {
+      if (error) {
+        *error = error_handler_->CreateAndUploadError(
+            FROM_HERE,
+            "Failed to look up local tab node",
+            model_type());
+      }
+      return false;
+    }
+
+    // Load the last stored version of this tab so we can compare changes. If
+    // this is a new tab, session_tab will be a new, blank SessionTab object.
+    sync_pb::SessionSpecifics specifics = tab_node.GetSessionSpecifics();
+    const int s_tab_node_id(specifics.tab_node_id());
+    DCHECK_EQ(tab_node_id, s_tab_node_id);
+    session_tab =
+        synced_session_tracker_.GetTab(GetCurrentMachineTag(),
+                                       tab_delegate.GetSessionId(),
+                                       specifics.tab_node_id());
+    SetSessionTabFromDelegate(tab_delegate, base::Time::Now(), session_tab);
+    sync_pb::SessionTab tab_s = session_tab->ToSyncData();
+
+    if (new_url == old_tab_url) {
+      // Load the old specifics and copy over the favicon data if needed.
+      // TODO(zea): remove this once favicon sync is enabled as a separate type.
+      tab_s.set_favicon(specifics.tab().favicon());
+      tab_s.set_favicon_source(specifics.tab().favicon_source());
+      tab_s.set_favicon_type(specifics.tab().favicon_type());
+    }
+    // Retain the base SessionSpecifics data (tag, tab_node_id, etc.), and just
+    // write the new SessionTabSpecifics.
+    specifics.mutable_tab()->CopyFrom(tab_s);
+
+    // Write into the actual sync model.
+    tab_node.SetSessionSpecifics(specifics);
+  }
+
+  // Trigger the favicon load if needed. We do this outside the write
   // transaction to avoid jank.
   tab_link->set_url(new_url);
   if (new_url != old_tab_url) {
@@ -445,34 +481,6 @@
   synced_session_tracker_.GetSession(GetCurrentMachineTag())->modified_time =
       base::Time::Now();
 
-  syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
-  syncer::WriteNode tab_node(&trans);
-  if (tab_node.InitByIdLookup(sync_id) != syncer::BaseNode::INIT_OK) {
-    if (error) {
-      *error = error_handler_->CreateAndUploadError(
-          FROM_HERE,
-          "Failed to look up local tab node",
-          model_type());
-    }
-    return false;
-  }
-
-  sync_pb::SessionTab tab_s = session_tab->ToSyncData();
-  sync_pb::SessionSpecifics specifics = tab_node.GetSessionSpecifics();
-  if (new_url == old_tab_url) {
-    // Load the old specifics and copy over the favicon data if needed.
-    // TODO(zea): remove this once favicon sync is enabled as a separate type.
-    tab_s.set_favicon(specifics.tab().favicon());
-    tab_s.set_favicon_source(specifics.tab().favicon_source());
-    tab_s.set_favicon_type(specifics.tab().favicon_type());
-  }
-  // Retain the base SessionSpecifics data (tag, tab_node_id, etc.), and just
-  // write the new SessionTabSpecifics.
-  specifics.mutable_tab()->CopyFrom(tab_s);
-
-  // Write into the actual sync model.
-  tab_node.SetSessionSpecifics(specifics);
-
   return true;
 }
 
@@ -533,8 +541,8 @@
   // TODO(zea): consider a separate container for tabs with outstanding favicon
   // loads so we don't have to iterate through all tabs comparing urls.
   for (std::set<GURL>::const_iterator i = urls.begin(); i != urls.end(); ++i) {
-    for (TabLinksMap::iterator tab_iter = tab_map_.begin();
-         tab_iter != tab_map_.end();
+    for (TabLinksMap::iterator tab_iter = local_tab_map_.begin();
+         tab_iter != local_tab_map_.end();
          ++tab_iter) {
       if (tab_iter->second->url() == *i)
         favicon_cache_.OnPageFaviconUpdated(*i);
@@ -550,7 +558,7 @@
 
   // Ensure that we disassociated properly, otherwise memory might leak.
   DCHECK(synced_session_tracker_.Empty());
-  DCHECK_EQ(0U, tab_pool_.Capacity());
+  DCHECK_EQ(0U, local_tab_pool_.Capacity());
 
   local_session_syncid_ = syncer::kInvalidId;
 
@@ -644,8 +652,8 @@
   DCHECK(CalledOnValidThread());
   DVLOG(1) << "Disassociating local session " << GetCurrentMachineTag();
   synced_session_tracker_.Clear();
-  tab_map_.clear();
-  tab_pool_.Clear();
+  local_tab_map_.clear();
+  local_tab_pool_.Clear();
   local_session_syncid_ = syncer::kInvalidId;
   current_machine_tag_ = "";
   current_session_name_ = "";
@@ -676,7 +684,7 @@
     prefs.SetSyncSessionsGUID(current_machine_tag_);
   }
 
-  tab_pool_.SetMachineTag(current_machine_tag_);
+  local_tab_pool_.SetMachineTag(current_machine_tag_);
 }
 
 bool SessionModelAssociator::GetSyncedFaviconForPageURL(
@@ -697,7 +705,7 @@
     syncer::WriteTransaction* trans,
     syncer::SyncError* error) {
   DCHECK(CalledOnValidThread());
-  DCHECK(tab_pool_.Empty());
+  DCHECK(local_tab_pool_.Empty());
   DCHECK_EQ(local_session_syncid_, syncer::kInvalidId);
 
   // Iterate through the nodes and associate any foreign sessions.
@@ -718,9 +726,11 @@
     const sync_pb::SessionSpecifics& specifics =
         sync_node.GetSessionSpecifics();
     const base::Time& modification_time = sync_node.GetModificationTime();
-    if (specifics.session_tag().empty()) {
+    if (specifics.session_tag().empty() ||
+           (specifics.has_tab() && (!specifics.has_tab_node_id() ||
+            !specifics.tab().has_tab_id()))) {
       // This is a corrupted node. Just delete it.
-      LOG(WARNING) << "Found node with no session tag, deleting.";
+      LOG(WARNING) << "Found invalid session node, deleting.";
       sync_node.Tombstone();
     } else if (specifics.session_tag() != GetCurrentMachineTag()) {
       AssociateForeignSpecifics(specifics, modification_time);
@@ -734,8 +744,7 @@
           current_session_name_ = specifics.header().client_name();
         }
       } else {
-        if (specifics.has_header() || !specifics.has_tab() ||
-            !specifics.has_tab_node_id() || !specifics.tab().has_tab_id()) {
+        if (specifics.has_header() || !specifics.has_tab()) {
           LOG(WARNING) << "Found invalid session node, deleting.";
           sync_node.Tombstone();
         } else {
@@ -743,7 +752,7 @@
           // reused for reassociation.
           SessionID tab_id;
           tab_id.set_id(specifics.tab().tab_id());
-          tab_pool_.AddTabNode(id, tab_id, specifics.tab_node_id());
+          local_tab_pool_.AddTabNode(specifics.tab_node_id(), tab_id);
         }
       }
     }
@@ -801,7 +810,9 @@
     const sync_pb::SessionTab& tab_s = specifics.tab();
     SessionID::id_type tab_id = tab_s.tab_id();
     SessionTab* tab =
-        synced_session_tracker_.GetTab(foreign_session_tag, tab_id);
+        synced_session_tracker_.GetTab(foreign_session_tag,
+                                       tab_id,
+                                       specifics.tab_node_id());
 
     // Update SessionTab based on protobuf.
     tab->SetFromSyncData(tab_s, modification_time);
@@ -1150,15 +1161,18 @@
 }
 
 void SessionModelAssociator::UpdateTabIdIfNecessary(
-    int64 sync_id,
+    int tab_node_id,
     SessionID::id_type new_tab_id) {
-  DCHECK_NE(sync_id, syncer::kInvalidId);
-  SessionID::id_type old_tab_id = tab_pool_.GetTabIdFromSyncId(sync_id);
+  DCHECK_NE(tab_node_id, TabNodePool::kInvalidTabNodeID);
+  SessionID::id_type old_tab_id =
+      local_tab_pool_.GetTabIdFromTabNodeId(tab_node_id);
   if (old_tab_id != new_tab_id) {
     // Rewrite tab id if required.
     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
     syncer::WriteNode tab_node(&trans);
-    if (tab_node.InitByIdLookup(sync_id) == syncer::BaseNode::INIT_OK) {
+    if (tab_node.InitByClientTagLookup(syncer::SESSIONS,
+            TabNodePool::TabIdToTag(current_machine_tag_, tab_node_id)) ==
+                syncer::BaseNode::INIT_OK) {
       sync_pb::SessionSpecifics session_specifics =
           tab_node.GetSessionSpecifics();
       DCHECK(session_specifics.has_tab());
@@ -1167,7 +1181,7 @@
         tab_s->set_tab_id(new_tab_id);
         tab_node.SetSessionSpecifics(session_specifics);
         // Update tab node pool with the new association.
-        tab_pool_.ReassociateTabNode(sync_id, new_tab_id);
+        local_tab_pool_.ReassociateTabNode(tab_node_id, new_tab_id);
       }
     }
   }
diff --git a/chrome/browser/sync/glue/session_model_associator.h b/chrome/browser/sync/glue/session_model_associator.h
index 5f46168..2b3aac4 100644
--- a/chrome/browser/sync/glue/session_model_associator.h
+++ b/chrome/browser/sync/glue/session_model_associator.h
@@ -182,7 +182,7 @@
   // entries.
   bool ShouldSyncTab(const SyncedTabDelegate& tab) const;
 
-  // Compare |urls| against |tab_map_|'s urls to see if any tabs with
+  // Compare |urls| against |local_tab_map_|'s urls to see if any tabs with
   // outstanding favicon loads can be fulfilled.
   void FaviconsUpdated(const std::set<GURL>& urls);
 
@@ -232,19 +232,19 @@
   FRIEND_TEST_ALL_PREFIXES(SyncSessionModelAssociatorTest,
                            TabNodePool);
 
-  // Keep all the links to local tab data in one place. A sync_id and tab must
-  // be passed at creation. The sync_id is not mutable after, although all other
-  // fields are.
+  // Keep all the links to local tab data in one place. A tab_node_id and tab
+  // must be passed at creation. The tab_node_id is not mutable after, although
+  // all other fields are.
   class TabLink {
    public:
-    TabLink(int64 sync_id, const SyncedTabDelegate* tab)
-      : sync_id_(sync_id),
+    TabLink(int tab_node_id, const SyncedTabDelegate* tab)
+      : tab_node_id_(tab_node_id),
         tab_(tab) {}
 
     void set_tab(const SyncedTabDelegate* tab) { tab_ = tab; }
     void set_url(const GURL& url) { url_ = url; }
 
-    int64 sync_id() const { return sync_id_; }
+    int tab_node_id() const { return tab_node_id_; }
     const SyncedTabDelegate* tab() const { return tab_; }
     const GURL& url() const { return url_; }
 
@@ -252,7 +252,7 @@
     DISALLOW_COPY_AND_ASSIGN(TabLink);
 
     // The id for the sync node this tab is stored in.
-    const int64 sync_id_;
+    const int tab_node_id_;
 
     // The tab object itself.
     const SyncedTabDelegate* tab_;
@@ -331,8 +331,10 @@
   // invalid url's.
   bool TabHasValidEntry(const SyncedTabDelegate& tab) const;
 
-  // Update the tab id of the node associated with |sync_id| to |new_tab_id|.
-  void UpdateTabIdIfNecessary(int64 sync_id, SessionID::id_type new_tab_id);
+  // Update the tab id of the node associated with |tab_node_id| to
+  // |new_tab_id|.
+  void UpdateTabIdIfNecessary(int tab_node_id,
+                              SessionID::id_type new_tab_id);
 
   // For testing only.
   void QuitLoopForSubtleTesting();
@@ -343,15 +345,15 @@
   // User-visible machine name.
   std::string current_session_name_;
 
-  // Pool of all used/available sync nodes associated with tabs.
-  TabNodePool tab_pool_;
+  // Pool of all used/available sync nodes associated with local tabs.
+  TabNodePool local_tab_pool_;
 
   // SyncID for the sync node containing all the window information for this
   // client.
   int64 local_session_syncid_;
 
   // Mapping of current open (local) tabs to their sync identifiers.
-  TabLinksMap tab_map_;
+  TabLinksMap local_tab_map_;
 
   SyncedSessionTracker synced_session_tracker_;
 
diff --git a/chrome/browser/sync/glue/session_model_associator_unittest.cc b/chrome/browser/sync/glue/session_model_associator_unittest.cc
index e5416a4..a883546 100644
--- a/chrome/browser/sync/glue/session_model_associator_unittest.cc
+++ b/chrome/browser/sync/glue/session_model_associator_unittest.cc
@@ -190,8 +190,8 @@
                      const std::vector<const content::NavigationEntry*>*());
   MOCK_CONST_METHOD0(IsPinned, bool());
   MOCK_CONST_METHOD0(HasWebContents, bool());
-  MOCK_CONST_METHOD0(GetSyncId, int64());
-  MOCK_METHOD1(SetSyncId, void(int64));
+  MOCK_CONST_METHOD0(GetSyncId, int());
+  MOCK_METHOD1(SetSyncId, void(int));
 };
 
 class SyncRefreshListener : public content::NotificationObserver {
diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc
index 3ab713f..94da4d5 100644
--- a/chrome/browser/sync/glue/sync_backend_host.cc
+++ b/chrome/browser/sync/glue/sync_backend_host.cc
@@ -158,7 +158,7 @@
   //
   // Called to perform initialization of the syncapi on behalf of
   // SyncBackendHost::Initialize.
-  void DoInitialize(const DoInitializeOptions& options);
+  void DoInitialize(scoped_ptr<DoInitializeOptions> options);
 
   // Called to perform credential update on behalf of
   // SyncBackendHost::UpdateCredentials.
@@ -202,17 +202,12 @@
   void DoFinishInitialProcessControlTypes();
 
   // The shutdown order is a bit complicated:
-  // 1) From |sync_thread_|, invoke the syncapi Shutdown call to do
-  //    a final SaveChanges, and close sqlite handles.
-  // 2) Then, from |frontend_loop_|, halt the sync_thread_ (which is
-  //    a blocking call). This causes syncapi thread-exit handlers
-  //    to run and make use of cached pointers to various components
-  //    owned implicitly by us.
-  // 3) Destroy this Core. That will delete syncapi components in a
-  //    safe order because the thread that was using them has exited
-  //    (in step 2).
-  void DoStopSyncManagerForShutdown(const base::Closure& closure);
-  void DoShutdown(bool stopping_sync);
+  // 1) Call DoStopSyncManagerForShutdown() from |frontend_loop_| to request
+  //    sync manager to stop as soon as possible.
+  // 2) Post DoShutdown() to sync loop to clean up backend state, save
+  //    directory and destroy sync manager.
+  void DoStopSyncManagerForShutdown();
+  void DoShutdown(bool sync_disabled);
   void DoDestroySyncManager();
 
   // Configuration methods that must execute on sync loop.
@@ -254,15 +249,14 @@
   // Invoked when initialization of syncapi is complete and we can start
   // our timer.
   // This must be called from the thread on which SaveChanges is intended to
-  // be run on; the host's |sync_thread_|.
+  // be run on; the host's |registrar_->sync_thread()|.
   void StartSavingChanges();
 
   // Invoked periodically to tell the syncapi to persist its state
   // by writing to disk.
-  // This is called from the thread we were created on (which is the
-  // SyncBackendHost |sync_thread_|), using a repeating timer that is kicked
-  // off as soon as the SyncManager tells us it completed
-  // initialization.
+  // This is called from the thread we were created on (which is sync thread),
+  // using a repeating timer that is kicked off as soon as the SyncManager
+  // tells us it completed initialization.
   void SaveChanges();
 
   // Name used for debugging.
@@ -275,7 +269,7 @@
   syncer::WeakHandle<SyncBackendHost> host_;
 
   // The loop where all the sync backend operations happen.
-  // Non-NULL only between calls to DoInitialize() and DoShutdown().
+  // Non-NULL only between calls to DoInitialize() and ~Core().
   base::MessageLoop* sync_loop_;
 
   // Our parent's registrar (not owned).  Non-NULL only between
@@ -294,6 +288,8 @@
   // The top-level syncapi entry point.  Lives on the sync thread.
   scoped_ptr<syncer::SyncManager> sync_manager_;
 
+  base::WeakPtrFactory<Core> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(Core);
 };
 
@@ -302,11 +298,10 @@
     Profile* profile,
     const base::WeakPtr<SyncPrefs>& sync_prefs)
     : weak_ptr_factory_(this),
-      sync_thread_("Chrome_SyncThread"),
       frontend_loop_(base::MessageLoop::current()),
       profile_(profile),
       name_(name),
-      core_(new Core(name, profile_->GetPath().Append(kSyncDataFolderName),
+      core_(new Core(name_, profile_->GetPath().Append(kSyncDataFolderName),
                      weak_ptr_factory_.GetWeakPtr())),
       initialization_state_(NOT_ATTEMPTED),
       sync_prefs_(sync_prefs),
@@ -319,7 +314,6 @@
 
 SyncBackendHost::SyncBackendHost(Profile* profile)
     : weak_ptr_factory_(this),
-      sync_thread_("Chrome_SyncThread"),
       frontend_loop_(base::MessageLoop::current()),
       profile_(profile),
       name_("Unknown"),
@@ -351,23 +345,23 @@
 
 void SyncBackendHost::Initialize(
     SyncFrontend* frontend,
+    scoped_ptr<base::Thread> sync_thread,
     const syncer::WeakHandle<syncer::JsEventHandler>& event_handler,
     const GURL& sync_service_url,
     const SyncCredentials& credentials,
     bool delete_sync_data_folder,
-    syncer::SyncManagerFactory* sync_manager_factory,
-    syncer::UnrecoverableErrorHandler* unrecoverable_error_handler,
+    scoped_ptr<syncer::SyncManagerFactory> sync_manager_factory,
+    scoped_ptr<syncer::UnrecoverableErrorHandler> unrecoverable_error_handler,
     syncer::ReportUnrecoverableErrorFunction
         report_unrecoverable_error_function) {
-  if (!sync_thread_.Start())
-    return;
+  registrar_.reset(new SyncBackendRegistrar(name_,
+                                            profile_,
+                                            sync_thread.Pass()));
+  CHECK(registrar_->sync_thread());
 
   frontend_ = frontend;
   DCHECK(frontend);
 
-  registrar_.reset(new SyncBackendRegistrar(name_,
-                                            profile_,
-                                            sync_thread_.message_loop()));
   syncer::ModelSafeRoutingInfo routing_info;
   std::vector<syncer::ModelSafeWorker*> workers;
   registrar_->GetModelSafeRoutingInfo(&routing_info);
@@ -389,12 +383,13 @@
   }
 
   initialization_state_ = CREATING_SYNC_MANAGER;
-  InitCore(DoInitializeOptions(
-      sync_thread_.message_loop(),
+
+  scoped_ptr<DoInitializeOptions> init_opts(new DoInitializeOptions(
+      registrar_->sync_thread()->message_loop(),
       registrar_.get(),
       routing_info,
       workers,
-      &extensions_activity_monitor_,
+      extensions_activity_monitor_.GetExtensionsActivity(),
       event_handler,
       sync_service_url,
       base::Bind(&MakeHttpBridgeFactory,
@@ -402,20 +397,23 @@
                  NetworkTimeTracker::BuildNotifierUpdateCallback()),
       credentials,
       invalidator_->GetInvalidatorClientId(),
-      sync_manager_factory,
+      sync_manager_factory.Pass(),
       delete_sync_data_folder,
       sync_prefs_->GetEncryptionBootstrapToken(),
       sync_prefs_->GetKeystoreEncryptionBootstrapToken(),
-      new InternalComponentsFactoryImpl(factory_switches),
-      unrecoverable_error_handler,
+      scoped_ptr<InternalComponentsFactory>(
+          new InternalComponentsFactoryImpl(factory_switches)).Pass(),
+      unrecoverable_error_handler.Pass(),
       report_unrecoverable_error_function,
       !cl->HasSwitch(switches::kSyncDisableOAuth2Token)));
+  InitCore(init_opts.Pass());
 }
 
 void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) {
-  DCHECK(sync_thread_.IsRunning());
-  sync_thread_.message_loop()->PostTask(FROM_HERE,
-      base::Bind(&SyncBackendHost::Core::DoUpdateCredentials, core_.get(),
+  DCHECK(registrar_->sync_thread()->IsRunning());
+  registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE,
+      base::Bind(&SyncBackendHost::Core::DoUpdateCredentials,
+                 core_.get(),
                  credentials));
 }
 
@@ -425,14 +423,14 @@
   syncer::ModelSafeRoutingInfo routing_info;
   registrar_->GetModelSafeRoutingInfo(&routing_info);
 
-  sync_thread_.message_loop()->PostTask(FROM_HERE,
+  registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE,
       base::Bind(&SyncBackendHost::Core::DoStartSyncing,
                  core_.get(), routing_info));
 }
 
 void SyncBackendHost::SetEncryptionPassphrase(const std::string& passphrase,
                                               bool is_explicit) {
-  DCHECK(sync_thread_.IsRunning());
+  DCHECK(registrar_->sync_thread()->IsRunning());
   if (!IsNigoriEnabled()) {
     NOTREACHED() << "SetEncryptionPassphrase must never be called when nigori"
                     " is disabled.";
@@ -451,8 +449,9 @@
          cached_passphrase_type_ == syncer::IMPLICIT_PASSPHRASE);
 
   // Post an encryption task on the syncer thread.
-  sync_thread_.message_loop()->PostTask(FROM_HERE,
-      base::Bind(&SyncBackendHost::Core::DoSetEncryptionPassphrase, core_.get(),
+  registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE,
+      base::Bind(&SyncBackendHost::Core::DoSetEncryptionPassphrase,
+                 core_.get(),
                  passphrase, is_explicit));
 }
 
@@ -479,8 +478,9 @@
     return false;
 
   // Post a decryption task on the syncer thread.
-  sync_thread_.message_loop()->PostTask(FROM_HERE,
-      base::Bind(&SyncBackendHost::Core::DoSetDecryptionPassphrase, core_.get(),
+  registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE,
+      base::Bind(&SyncBackendHost::Core::DoSetDecryptionPassphrase,
+                 core_.get(),
                  passphrase));
 
   // Since we were able to decrypt the cached pending keys with the passphrase
@@ -496,22 +496,19 @@
   return true;
 }
 
-void SyncBackendHost::StopSyncManagerForShutdown(
-    const base::Closure& closure) {
+void SyncBackendHost::StopSyncManagerForShutdown() {
   DCHECK_GT(initialization_state_, NOT_ATTEMPTED);
   if (initialization_state_ == CREATING_SYNC_MANAGER) {
     // We post here to implicitly wait for the SyncManager to be created,
     // if needed.  We have to wait, since we need to shutdown immediately,
     // and we need to tell the SyncManager so it can abort any activity
     // (net I/O, data application).
-    DCHECK(sync_thread_.IsRunning());
-    sync_thread_.message_loop()->PostTask(FROM_HERE,
-        base::Bind(
-            &SyncBackendHost::Core::DoStopSyncManagerForShutdown,
-            core_.get(),
-            closure));
+    DCHECK(registrar_->sync_thread()->IsRunning());
+    registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE,
+        base::Bind(&SyncBackendHost::Core::DoStopSyncManagerForShutdown,
+                   core_.get()));
   } else {
-    core_->DoStopSyncManagerForShutdown(closure);
+    core_->DoStopSyncManagerForShutdown();
   }
 }
 
@@ -524,44 +521,22 @@
   // Stop listening for and forwarding locally-triggered sync refresh requests.
   notification_registrar_.RemoveAll();
 
-  // Thread shutdown should occur in the following order:
-  // - Sync Thread
-  // - UI Thread (stops some time after we return from this call).
-  //
-  // In order to achieve this, we first shutdown components from the UI thread
-  // and send signals to abort components that may be busy on the sync thread.
-  // The callback (OnSyncerShutdownComplete) will happen on the sync thread,
-  // after which we'll shutdown components on the sync thread, and then be
-  // able to stop the sync loop.
-  if (sync_thread_.IsRunning()) {
-    StopSyncManagerForShutdown(
-        base::Bind(&SyncBackendRegistrar::OnSyncerShutdownComplete,
-                   base::Unretained(registrar_.get())));
+  DCHECK(registrar_->sync_thread()->IsRunning());
 
-    // Before joining the sync_thread_, we wait for the UIModelWorker to
-    // give us the green light that it is not depending on the frontend_loop_
-    // to process any more tasks. Stop() blocks until this termination
-    // condition is true.
-    base::Time stop_registrar_start_time = base::Time::Now();
-    if (registrar_)
-      registrar_->StopOnUIThread();
-    base::TimeDelta stop_registrar_time = base::Time::Now() -
-        stop_registrar_start_time;
-    UMA_HISTOGRAM_TIMES("Sync.Shutdown.StopRegistrarTime",
-                        stop_registrar_time);
-  } else {
-    // If the sync thread isn't running, then the syncer is effectively
-    // stopped.  Moreover, it implies that we never attempted initialization,
-    // so the registrar won't need stopping either.
-    DCHECK_EQ(initialization_state_, NOT_ATTEMPTED);
-    DCHECK(!registrar_.get());
-  }
+  registrar_->RequestWorkerStopOnUIThread();
+
+  StopSyncManagerForShutdown();
 }
 
-void SyncBackendHost::Shutdown(bool sync_disabled) {
+scoped_ptr<base::Thread> SyncBackendHost::Shutdown(ShutdownOption option) {
   // StopSyncingForShutdown() (which nulls out |frontend_|) should be
   // called first.
   DCHECK(!frontend_);
+  DCHECK(registrar_->sync_thread()->IsRunning());
+
+  bool sync_disabled = (option == DISABLE_AND_CLAIM_THREAD);
+  bool sync_thread_claimed =
+      (option == DISABLE_AND_CLAIM_THREAD || option == STOP_AND_CLAIM_THREAD);
 
   if (invalidation_handler_registered_) {
     if (sync_disabled) {
@@ -572,37 +547,25 @@
   }
   invalidation_handler_registered_ = false;
 
-  // TODO(tim): DCHECK(registrar_->StoppedOnUIThread()) would be nice.
-  if (sync_thread_.IsRunning()) {
-    sync_thread_.message_loop()->PostTask(FROM_HERE,
-        base::Bind(&SyncBackendHost::Core::DoShutdown, core_.get(),
-                   sync_disabled));
-  }
+  // Shut down and destroy sync manager.
+  registrar_->sync_thread()->message_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&SyncBackendHost::Core::DoShutdown,
+                 core_.get(), sync_disabled));
+  core_ = NULL;
 
-  // Stop will return once the thread exits, which will be after DoShutdown
-  // runs. DoShutdown needs to run from sync_thread_ because the sync backend
-  // requires any thread that opened sqlite handles to relinquish them
-  // personally. We need to join threads, because otherwise the main Chrome
-  // thread (ui loop) can exit before DoShutdown finishes, at which point
-  // virtually anything the sync backend does (or the post-back to
-  // frontend_loop_ by our Core) will epically fail because the CRT won't be
-  // initialized.
-  // Since we are blocking the UI thread here, we need to turn ourselves in
-  // with the ThreadRestriction police.  For sentencing and how we plan to fix
-  // this, see bug 19757.
-  base::Time stop_thread_start_time = base::Time::Now();
-  {
-    base::ThreadRestrictions::ScopedAllowIO allow_io;
-    sync_thread_.Stop();
-  }
-  base::TimeDelta stop_sync_thread_time = base::Time::Now() -
-      stop_thread_start_time;
-  UMA_HISTOGRAM_TIMES("Sync.Shutdown.StopSyncThreadTime",
-                      stop_sync_thread_time);
+  // Worker cleanup.
+  SyncBackendRegistrar* detached_registrar = registrar_.release();
+  detached_registrar->sync_thread()->message_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&SyncBackendRegistrar::Shutdown,
+                 base::Unretained(detached_registrar)));
 
-  registrar_.reset();
   js_backend_.Reset();
-  core_ = NULL;  // Releases reference to core_.
+  if (sync_thread_claimed)
+    return detached_registrar->ReleaseSyncThread();
+  else
+    return scoped_ptr<base::Thread>();
 }
 
 void SyncBackendHost::UnregisterInvalidationIds() {
@@ -727,7 +690,7 @@
 }
 
 void SyncBackendHost::EnableEncryptEverything() {
-  sync_thread_.message_loop()->PostTask(FROM_HERE,
+  registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE,
      base::Bind(&SyncBackendHost::Core::DoEnableEncryptEverything,
                 core_.get()));
 }
@@ -793,9 +756,10 @@
   return core_->synced_device_tracker();
 }
 
-void SyncBackendHost::InitCore(const DoInitializeOptions& options) {
-  sync_thread_.message_loop()->PostTask(FROM_HERE,
-      base::Bind(&SyncBackendHost::Core::DoInitialize, core_.get(), options));
+void SyncBackendHost::InitCore(scoped_ptr<DoInitializeOptions> options) {
+  registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE,
+      base::Bind(&SyncBackendHost::Core::DoInitialize,
+                 core_.get(), base::Passed(&options)));
 }
 
 void SyncBackendHost::RequestConfigureSyncer(
@@ -814,7 +778,7 @@
   config_types.to_purge = to_purge;
   config_types.to_journal = to_journal;
   config_types.to_unapply = to_unapply;
-  sync_thread_.message_loop()->PostTask(FROM_HERE,
+  registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE,
        base::Bind(&SyncBackendHost::Core::DoConfigureSyncer,
                   core_.get(),
                   reason,
@@ -876,7 +840,7 @@
 
   // Kick off the next step in SyncBackendHost initialization by downloading
   // any necessary control types.
-  sync_thread_.message_loop()->PostTask(
+  registrar_->sync_thread()->message_loop()->PostTask(
       FROM_HERE,
       base::Bind(&SyncBackendHost::Core::DoDownloadControlTypes,
                  core_.get(),
@@ -892,7 +856,7 @@
 
   content::Details<const syncer::ModelTypeSet> state_details(details);
   const syncer::ModelTypeSet& types = *(state_details.ptr());
-  sync_thread_.message_loop()->PostTask(FROM_HERE,
+  registrar_->sync_thread()->message_loop()->PostTask(FROM_HERE,
       base::Bind(&SyncBackendHost::Core::DoRefreshTypes, core_.get(), types));
 }
 
@@ -901,18 +865,18 @@
     SyncBackendRegistrar* registrar,
     const syncer::ModelSafeRoutingInfo& routing_info,
     const std::vector<syncer::ModelSafeWorker*>& workers,
-    syncer::ExtensionsActivityMonitor* extensions_activity_monitor,
+    const scoped_refptr<syncer::ExtensionsActivity>& extensions_activity,
     const syncer::WeakHandle<syncer::JsEventHandler>& event_handler,
     const GURL& service_url,
     MakeHttpBridgeFactoryFn make_http_bridge_factory_fn,
     const syncer::SyncCredentials& credentials,
     const std::string& invalidator_client_id,
-    syncer::SyncManagerFactory* sync_manager_factory,
+    scoped_ptr<syncer::SyncManagerFactory> sync_manager_factory,
     bool delete_sync_data_folder,
     const std::string& restored_key_for_bootstrapping,
     const std::string& restored_keystore_key_for_bootstrapping,
-    InternalComponentsFactory* internal_components_factory,
-    syncer::UnrecoverableErrorHandler* unrecoverable_error_handler,
+    scoped_ptr<InternalComponentsFactory> internal_components_factory,
+    scoped_ptr<syncer::UnrecoverableErrorHandler> unrecoverable_error_handler,
     syncer::ReportUnrecoverableErrorFunction
         report_unrecoverable_error_function,
     bool use_oauth2_token)
@@ -920,19 +884,19 @@
       registrar(registrar),
       routing_info(routing_info),
       workers(workers),
-      extensions_activity_monitor(extensions_activity_monitor),
+      extensions_activity(extensions_activity),
       event_handler(event_handler),
       service_url(service_url),
       make_http_bridge_factory_fn(make_http_bridge_factory_fn),
       credentials(credentials),
       invalidator_client_id(invalidator_client_id),
-      sync_manager_factory(sync_manager_factory),
+      sync_manager_factory(sync_manager_factory.Pass()),
       delete_sync_data_folder(delete_sync_data_folder),
       restored_key_for_bootstrapping(restored_key_for_bootstrapping),
       restored_keystore_key_for_bootstrapping(
           restored_keystore_key_for_bootstrapping),
-      internal_components_factory(internal_components_factory),
-      unrecoverable_error_handler(unrecoverable_error_handler),
+      internal_components_factory(internal_components_factory.Pass()),
+      unrecoverable_error_handler(unrecoverable_error_handler.Pass()),
       report_unrecoverable_error_function(
           report_unrecoverable_error_function),
       use_oauth2_token(use_oauth2_token) {
@@ -947,13 +911,13 @@
       sync_data_folder_path_(sync_data_folder_path),
       host_(backend),
       sync_loop_(NULL),
-      registrar_(NULL) {
+      registrar_(NULL),
+      weak_ptr_factory_(this) {
   DCHECK(backend.get());
 }
 
 SyncBackendHost::Core::~Core() {
   DCHECK(!sync_manager_.get());
-  DCHECK(!sync_loop_);
 }
 
 void SyncBackendHost::Core::OnSyncCycleCompleted(
@@ -990,9 +954,9 @@
       syncer::ModelTypeSet(),
       routing_info,
       base::Bind(&SyncBackendHost::Core::DoInitialProcessControlTypes,
-                 this),
+                 weak_ptr_factory_.GetWeakPtr()),
       base::Bind(&SyncBackendHost::Core::OnControlTypesDownloadRetry,
-                 this));
+                 weak_ptr_factory_.GetWeakPtr()));
 }
 
 void SyncBackendHost::Core::DoRefreshTypes(syncer::ModelTypeSet types) {
@@ -1030,7 +994,8 @@
   // Sync manager initialization is complete, so we can schedule recurring
   // SaveChanges.
   sync_loop_->PostTask(FROM_HERE,
-                       base::Bind(&Core::StartSavingChanges, this));
+                       base::Bind(&Core::StartSavingChanges,
+                                  weak_ptr_factory_.GetWeakPtr()));
 
   host_.Call(FROM_HERE,
              &SyncBackendHost::HandleSyncManagerInitializationOnFrontendLoop,
@@ -1158,14 +1123,15 @@
   sync_manager_->OnIncomingInvalidation(invalidation_map);
 }
 
-void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) {
+void SyncBackendHost::Core::DoInitialize(
+    scoped_ptr<DoInitializeOptions> options) {
   DCHECK(!sync_loop_);
-  sync_loop_ = options.sync_loop;
+  sync_loop_ = options->sync_loop;
   DCHECK(sync_loop_);
 
   // Blow away the partial or corrupt sync data folder before doing any more
   // initialization, if necessary.
-  if (options.delete_sync_data_folder) {
+  if (options->delete_sync_data_folder) {
     DeleteSyncDataFolder();
   }
 
@@ -1176,30 +1142,29 @@
   }
 
   DCHECK(!registrar_);
-  registrar_ = options.registrar;
+  registrar_ = options->registrar;
   DCHECK(registrar_);
 
-  sync_manager_ = options.sync_manager_factory->CreateSyncManager(name_);
+  sync_manager_ = options->sync_manager_factory->CreateSyncManager(name_);
   sync_manager_->AddObserver(this);
   sync_manager_->Init(sync_data_folder_path_,
-                      options.event_handler,
-                      options.service_url.host() + options.service_url.path(),
-                      options.service_url.EffectiveIntPort(),
-                      options.service_url.SchemeIsSecure(),
-                      options.make_http_bridge_factory_fn.Run().Pass(),
-                      options.workers,
-                      options.extensions_activity_monitor,
-                      options.registrar /* as SyncManager::ChangeDelegate */,
-                      options.credentials,
-                      options.invalidator_client_id,
-                      options.restored_key_for_bootstrapping,
-                      options.restored_keystore_key_for_bootstrapping,
-                      scoped_ptr<InternalComponentsFactory>(
-                          options.internal_components_factory),
+                      options->event_handler,
+                      options->service_url.host() + options->service_url.path(),
+                      options->service_url.EffectiveIntPort(),
+                      options->service_url.SchemeIsSecure(),
+                      options->make_http_bridge_factory_fn.Run().Pass(),
+                      options->workers,
+                      options->extensions_activity,
+                      options->registrar /* as SyncManager::ChangeDelegate */,
+                      options->credentials,
+                      options->invalidator_client_id,
+                      options->restored_key_for_bootstrapping,
+                      options->restored_keystore_key_for_bootstrapping,
+                      options->internal_components_factory.get(),
                       &encryptor_,
-                      options.unrecoverable_error_handler,
-                      options.report_unrecoverable_error_function,
-                      options.use_oauth2_token);
+                      options->unrecoverable_error_handler.Pass(),
+                      options->report_unrecoverable_error_function,
+                      options->use_oauth2_token);
 
   // |sync_manager_| may end up being NULL here in tests (in
   // synchronous initialization mode).
@@ -1282,7 +1247,7 @@
                               sync_manager_->cache_guid()));
   synced_device_tracker_->InitLocalDeviceInfo(
       base::Bind(&SyncBackendHost::Core::DoFinishInitialProcessControlTypes,
-                 this));
+                 weak_ptr_factory_.GetWeakPtr()));
 }
 
 void SyncBackendHost::Core::DoFinishInitialProcessControlTypes() {
@@ -1309,13 +1274,9 @@
   sync_manager_->GetEncryptionHandler()->EnableEncryptEverything();
 }
 
-void SyncBackendHost::Core::DoStopSyncManagerForShutdown(
-    const base::Closure& closure) {
-  if (sync_manager_) {
-    sync_manager_->StopSyncingForShutdown(closure);
-  } else {
-    sync_loop_->PostTask(FROM_HERE, closure);
-  }
+void SyncBackendHost::Core::DoStopSyncManagerForShutdown() {
+  if (sync_manager_)
+    sync_manager_->StopSyncingForShutdown();
 }
 
 void SyncBackendHost::Core::DoShutdown(bool sync_disabled) {
@@ -1331,9 +1292,8 @@
   if (sync_disabled)
     DeleteSyncDataFolder();
 
-  sync_loop_ = NULL;
-
   host_.Reset();
+  weak_ptr_factory_.InvalidateWeakPtrs();
 }
 
 void SyncBackendHost::Core::DoDestroySyncManager() {
@@ -1362,11 +1322,11 @@
       config_types.to_unapply,
       routing_info,
       base::Bind(&SyncBackendHost::Core::DoFinishConfigureDataTypes,
-                 this,
+                 weak_ptr_factory_.GetWeakPtr(),
                  config_types.to_download,
                  ready_task),
       base::Bind(&SyncBackendHost::Core::DoRetryConfiguration,
-                 this,
+                 weak_ptr_factory_.GetWeakPtr(),
                  retry_callback));
 }
 
@@ -1521,7 +1481,7 @@
 }
 
 void SyncBackendHost::OnInvalidatorStateChange(syncer::InvalidatorState state) {
-  sync_thread_.message_loop()->PostTask(
+  registrar_->sync_thread()->message_loop()->PostTask(
       FROM_HERE,
       base::Bind(&SyncBackendHost::Core::DoOnInvalidatorStateChange,
                  core_.get(),
@@ -1539,7 +1499,7 @@
     invalidator_->AcknowledgeInvalidation(it->first, it->second.ack_handle);
   }
 
-  sync_thread_.message_loop()->PostTask(
+  registrar_->sync_thread()->message_loop()->PostTask(
       FROM_HERE,
       base::Bind(&SyncBackendHost::Core::DoOnIncomingInvalidation,
                  core_.get(),
@@ -1639,6 +1599,10 @@
   frontend_->OnConnectionStatusChange(status);
 }
 
+base::MessageLoop* SyncBackendHost::GetSyncLoopForTesting() {
+  return registrar_->sync_thread()->message_loop();
+}
+
 #undef SDVLOG
 
 #undef SLOG
diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h
index a539ec6..113ad42 100644
--- a/chrome/browser/sync/glue/sync_backend_host.h
+++ b/chrome/browser/sync/glue/sync_backend_host.h
@@ -16,7 +16,7 @@
 #include "base/threading/thread.h"
 #include "chrome/browser/invalidation/invalidation_service.h"
 #include "chrome/browser/sync/glue/backend_data_type_configurer.h"
-#include "chrome/browser/sync/glue/chrome_extensions_activity_monitor.h"
+#include "chrome/browser/sync/glue/extensions_activity_monitor.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "google_apis/gaia/google_service_auth_error.h"
@@ -32,6 +32,7 @@
 #include "sync/notifier/invalidation_handler.h"
 #include "sync/protocol/encryption.pb.h"
 #include "sync/protocol/sync_protocol_error.h"
+#include "sync/util/extensions_activity.h"
 #include "url/gurl.h"
 
 class Profile;
@@ -176,12 +177,13 @@
   // Note: |unrecoverable_error_handler| may be invoked from any thread.
   void Initialize(
       SyncFrontend* frontend,
+      scoped_ptr<base::Thread> sync_thread,
       const syncer::WeakHandle<syncer::JsEventHandler>& event_handler,
       const GURL& service_url,
       const syncer::SyncCredentials& credentials,
       bool delete_sync_data_folder,
-      syncer::SyncManagerFactory* sync_manager_factory,
-      syncer::UnrecoverableErrorHandler* unrecoverable_error_handler,
+      scoped_ptr<syncer::SyncManagerFactory> sync_manager_factory,
+      scoped_ptr<syncer::UnrecoverableErrorHandler> unrecoverable_error_handler,
       syncer::ReportUnrecoverableErrorFunction
           report_unrecoverable_error_function);
 
@@ -223,10 +225,19 @@
   virtual void StopSyncingForShutdown();
 
   // Called on |frontend_loop_| to kick off shutdown.
-  // |sync_disabled| indicates if syncing is being disabled or not.
   // See the implementation and Core::DoShutdown for details.
-  // Must be called *after* StopSyncingForShutdown.
-  void Shutdown(bool sync_disabled);
+  // Must be called *after* StopSyncingForShutdown. Caller should claim sync
+  // thread using STOP_AND_CLAIM_THREAD or DISABLE_AND_CLAIM_THREAD if sync
+  // backend might be recreated later because otherwise:
+  // * sync loop may be stopped on main loop and cause it to be blocked.
+  // * new/old backend may interfere with each other if new backend is created
+  //   before old one finishes cleanup.
+  enum ShutdownOption {
+    STOP,                      // Stop syncing and let backend stop sync thread.
+    STOP_AND_CLAIM_THREAD,     // Stop syncing and return sync thread.
+    DISABLE_AND_CLAIM_THREAD,  // Disable sync and return sync thread.
+  };
+  scoped_ptr<base::Thread> Shutdown(ShutdownOption option);
 
   // Removes all current registrations from the backend on the
   // InvalidationService.
@@ -293,6 +304,8 @@
   // Fetches the DeviceInfo tracker.
   virtual SyncedDeviceTracker* GetSyncedDeviceTracker() const;
 
+  base::MessageLoop* GetSyncLoopForTesting();
+
  protected:
   // The types and functions below are protected so that test
   // subclasses can use them.
@@ -310,18 +323,20 @@
         SyncBackendRegistrar* registrar,
         const syncer::ModelSafeRoutingInfo& routing_info,
         const std::vector<syncer::ModelSafeWorker*>& workers,
-        syncer::ExtensionsActivityMonitor* extensions_activity_monitor,
+        const scoped_refptr<syncer::ExtensionsActivity>& extensions_activity,
         const syncer::WeakHandle<syncer::JsEventHandler>& event_handler,
         const GURL& service_url,
         MakeHttpBridgeFactoryFn make_http_bridge_factory_fn,
         const syncer::SyncCredentials& credentials,
         const std::string& invalidator_client_id,
-        syncer::SyncManagerFactory* sync_manager_factory,
+        scoped_ptr<syncer::SyncManagerFactory> sync_manager_factory,
         bool delete_sync_data_folder,
         const std::string& restored_key_for_bootstrapping,
         const std::string& restored_keystore_key_for_bootstrapping,
-        syncer::InternalComponentsFactory* internal_components_factory,
-        syncer::UnrecoverableErrorHandler* unrecoverable_error_handler,
+        scoped_ptr<syncer::InternalComponentsFactory>
+            internal_components_factory,
+        scoped_ptr<syncer::UnrecoverableErrorHandler>
+            unrecoverable_error_handler,
         syncer::ReportUnrecoverableErrorFunction
             report_unrecoverable_error_function,
         bool use_oauth2_token);
@@ -331,27 +346,27 @@
     SyncBackendRegistrar* registrar;
     syncer::ModelSafeRoutingInfo routing_info;
     std::vector<syncer::ModelSafeWorker*> workers;
-    syncer::ExtensionsActivityMonitor* extensions_activity_monitor;
+    scoped_refptr<syncer::ExtensionsActivity> extensions_activity;
     syncer::WeakHandle<syncer::JsEventHandler> event_handler;
     GURL service_url;
     // Overridden by tests.
     MakeHttpBridgeFactoryFn make_http_bridge_factory_fn;
     syncer::SyncCredentials credentials;
     const std::string invalidator_client_id;
-    syncer::SyncManagerFactory* const sync_manager_factory;
+    scoped_ptr<syncer::SyncManagerFactory> sync_manager_factory;
     std::string lsid;
     bool delete_sync_data_folder;
     std::string restored_key_for_bootstrapping;
     std::string restored_keystore_key_for_bootstrapping;
-    syncer::InternalComponentsFactory* internal_components_factory;
-    syncer::UnrecoverableErrorHandler* unrecoverable_error_handler;
+    scoped_ptr<syncer::InternalComponentsFactory> internal_components_factory;
+    scoped_ptr<syncer::UnrecoverableErrorHandler> unrecoverable_error_handler;
     syncer::ReportUnrecoverableErrorFunction
         report_unrecoverable_error_function;
     bool use_oauth2_token;
   };
 
   // Allows tests to perform alternate core initialization work.
-  virtual void InitCore(const DoInitializeOptions& options);
+  virtual void InitCore(scoped_ptr<DoInitializeOptions> options);
 
   // Request the syncer to reconfigure with the specfied params.
   // Virtual for testing.
@@ -510,15 +525,12 @@
 
   // Handles stopping the core's SyncManager, accounting for whether
   // initialization is done yet.
-  void StopSyncManagerForShutdown(const base::Closure& closure);
+  void StopSyncManagerForShutdown();
 
   base::WeakPtrFactory<SyncBackendHost> weak_ptr_factory_;
 
   content::NotificationRegistrar notification_registrar_;
 
-  // A thread where all the sync operations happen.
-  base::Thread sync_thread_;
-
   // A reference to the MessageLoop used to construct |this|, so we know how
   // to safely talk back to the SyncFrontend.
   base::MessageLoop* const frontend_loop_;
@@ -528,14 +540,16 @@
   // Name used for debugging (set from profile_->GetDebugName()).
   const std::string name_;
 
-  // Our core, which communicates directly to the syncapi.
+  // Our core, which communicates directly to the syncapi. Use refptr instead
+  // of WeakHandle because |core_| is created on UI loop but released on
+  // sync loop.
   scoped_refptr<Core> core_;
 
   InitializationState initialization_state_;
 
   const base::WeakPtr<SyncPrefs> sync_prefs_;
 
-  ChromeExtensionsActivityMonitor extensions_activity_monitor_;
+  ExtensionsActivityMonitor extensions_activity_monitor_;
 
   scoped_ptr<SyncBackendRegistrar> registrar_;
 
diff --git a/chrome/browser/sync/glue/sync_backend_host_unittest.cc b/chrome/browser/sync/glue/sync_backend_host_unittest.cc
index 90eadee..2fd3ccf 100644
--- a/chrome/browser/sync/glue/sync_backend_host_unittest.cc
+++ b/chrome/browser/sync/glue/sync_backend_host_unittest.cc
@@ -93,25 +93,19 @@
 
 class FakeSyncManagerFactory : public syncer::SyncManagerFactory {
  public:
-  FakeSyncManagerFactory() : fake_manager_(NULL) {}
+  explicit FakeSyncManagerFactory(FakeSyncManager** fake_manager)
+     : fake_manager_(fake_manager) {
+    *fake_manager_ = NULL;
+  }
   virtual ~FakeSyncManagerFactory() {}
 
   // SyncManagerFactory implementation.  Called on the sync thread.
   virtual scoped_ptr<SyncManager> CreateSyncManager(
       std::string name) OVERRIDE {
-    DCHECK(!fake_manager_);
-    fake_manager_ = new FakeSyncManager(initial_sync_ended_types_,
-                                        progress_marker_types_,
-                                        configure_fail_types_);
-    return scoped_ptr<SyncManager>(fake_manager_);
-  }
-
-  // Returns NULL until CreateSyncManager() is called on the sync
-  // thread.  Called on the main thread, but only after
-  // OnBackendInitialized() is called (which is strictly after
-  // CreateSyncManager is called on the sync thread).
-  FakeSyncManager* fake_manager() {
-    return fake_manager_;
+    *fake_manager_ = new FakeSyncManager(initial_sync_ended_types_,
+                                         progress_marker_types_,
+                                         configure_fail_types_);
+    return scoped_ptr<SyncManager>(*fake_manager_);
   }
 
   void set_initial_sync_ended_types(syncer::ModelTypeSet types) {
@@ -130,7 +124,7 @@
   syncer::ModelTypeSet initial_sync_ended_types_;
   syncer::ModelTypeSet progress_marker_types_;
   syncer::ModelTypeSet configure_fail_types_;
-  FakeSyncManager* fake_manager_;
+  FakeSyncManager** fake_manager_;
 };
 
 class SyncBackendHostTest : public testing::Test {
@@ -151,6 +145,8 @@
     credentials_.email = "user@example.com";
     credentials_.sync_token = "sync_token";
 
+    fake_manager_factory_.reset(new FakeSyncManagerFactory(&fake_manager_));
+
     // These types are always implicitly enabled.
     enabled_types_.PutAll(syncer::ControlTypes());
 
@@ -169,7 +165,7 @@
   virtual void TearDown() OVERRIDE {
     if (backend_) {
       backend_->StopSyncingForShutdown();
-      backend_->Shutdown(false);
+      backend_->Shutdown(SyncBackendHost::STOP);
     }
     backend_.reset();
     sync_prefs_.reset();
@@ -186,14 +182,17 @@
   void InitializeBackend(bool expect_success) {
     EXPECT_CALL(mock_frontend_, OnBackendInitialized(_, _, expect_success)).
         WillOnce(InvokeWithoutArgs(QuitMessageLoop));
-    backend_->Initialize(&mock_frontend_,
-                         syncer::WeakHandle<syncer::JsEventHandler>(),
-                         GURL(std::string()),
-                         credentials_,
-                         true,
-                         &fake_manager_factory_,
-                         &handler_,
-                         NULL);
+    backend_->Initialize(
+        &mock_frontend_,
+        scoped_ptr<base::Thread>(),
+        syncer::WeakHandle<syncer::JsEventHandler>(),
+        GURL(std::string()),
+        credentials_,
+        true,
+        fake_manager_factory_.PassAs<syncer::SyncManagerFactory>(),
+        scoped_ptr<syncer::UnrecoverableErrorHandler>(
+            new syncer::TestUnrecoverableErrorHandler).Pass(),
+        NULL);
     base::RunLoop run_loop;
     BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
                                    run_loop.QuitClosure(),
@@ -202,7 +201,6 @@
     // |fake_manager_factory_|'s fake_manager() is set on the sync
     // thread, but we can rely on the message loop barriers to
     // guarantee that we see the updated value.
-    fake_manager_ = fake_manager_factory_.fake_manager();
     DCHECK(fake_manager_);
   }
 
@@ -255,12 +253,11 @@
   content::TestBrowserThreadBundle thread_bundle_;
   StrictMock<MockSyncFrontend> mock_frontend_;
   syncer::SyncCredentials credentials_;
-  syncer::TestUnrecoverableErrorHandler handler_;
   scoped_ptr<TestingProfile> profile_;
   scoped_ptr<SyncPrefs> sync_prefs_;
   scoped_ptr<SyncBackendHost> backend_;
+  scoped_ptr<FakeSyncManagerFactory> fake_manager_factory_;
   FakeSyncManager* fake_manager_;
-  FakeSyncManagerFactory fake_manager_factory_;
   syncer::ModelTypeSet enabled_types_;
 };
 
@@ -302,8 +299,8 @@
 TEST_F(SyncBackendHostTest, Restart) {
   sync_prefs_->SetSyncSetupCompleted();
   syncer::ModelTypeSet all_but_nigori = enabled_types_;
-  fake_manager_factory_.set_progress_marker_types(enabled_types_);
-  fake_manager_factory_.set_initial_sync_ended_types(enabled_types_);
+  fake_manager_factory_->set_progress_marker_types(enabled_types_);
+  fake_manager_factory_->set_initial_sync_ended_types(enabled_types_);
   InitializeBackend(true);
   EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty());
   EXPECT_TRUE(Intersection(fake_manager_->GetAndResetCleanedTypes(),
@@ -333,8 +330,8 @@
   syncer::ModelTypeSet partial_types(syncer::NIGORI, syncer::BOOKMARKS);
   syncer::ModelTypeSet full_types =
       Difference(enabled_types_, partial_types);
-  fake_manager_factory_.set_progress_marker_types(enabled_types_);
-  fake_manager_factory_.set_initial_sync_ended_types(full_types);
+  fake_manager_factory_->set_progress_marker_types(enabled_types_);
+  fake_manager_factory_->set_initial_sync_ended_types(full_types);
 
   // Bringing up the backend should purge all partial types, then proceed to
   // download the Nigori.
@@ -512,8 +509,8 @@
   // Set sync manager behavior before passing it down. All types have progress
   // markers and initial sync ended except the new types.
   syncer::ModelTypeSet old_types = enabled_types_;
-  fake_manager_factory_.set_progress_marker_types(old_types);
-  fake_manager_factory_.set_initial_sync_ended_types(old_types);
+  fake_manager_factory_->set_progress_marker_types(old_types);
+  fake_manager_factory_->set_initial_sync_ended_types(old_types);
   syncer::ModelTypeSet new_types(syncer::APP_SETTINGS,
                                  syncer::EXTENSION_SETTINGS);
   enabled_types_.PutAll(new_types);
@@ -552,8 +549,8 @@
   syncer::ModelTypeSet partial_types(syncer::NIGORI, syncer::BOOKMARKS);
   syncer::ModelTypeSet full_types =
       Difference(enabled_types_, partial_types);
-  fake_manager_factory_.set_progress_marker_types(old_types);
-  fake_manager_factory_.set_initial_sync_ended_types(full_types);
+  fake_manager_factory_->set_progress_marker_types(old_types);
+  fake_manager_factory_->set_initial_sync_ended_types(full_types);
   syncer::ModelTypeSet new_types(syncer::APP_SETTINGS,
                                  syncer::EXTENSION_SETTINGS);
   enabled_types_.PutAll(new_types);
@@ -605,8 +602,8 @@
   syncer::ModelTypeSet new_types(syncer::EXPERIMENTS, syncer::DEVICE_INFO);
   syncer::ModelTypeSet old_types =
       Difference(enabled_types_, new_types);
-  fake_manager_factory_.set_progress_marker_types(old_types);
-  fake_manager_factory_.set_initial_sync_ended_types(old_types);
+  fake_manager_factory_->set_progress_marker_types(old_types);
+  fake_manager_factory_->set_initial_sync_ended_types(old_types);
 
   // Bringing up the backend should download the new types without downloading
   // any old types.
@@ -628,7 +625,7 @@
 // be successful, but it returned no results.  This means that the usual
 // download retry logic will not be invoked.
 TEST_F(SyncBackendHostTest, SilentlyFailToDownloadControlTypes) {
-  fake_manager_factory_.set_configure_fail_types(syncer::ModelTypeSet::All());
+  fake_manager_factory_->set_configure_fail_types(syncer::ModelTypeSet::All());
   InitializeBackend(false);
 }
 
@@ -669,7 +666,7 @@
   fake_manager_->WaitForSyncThread();
   EXPECT_FALSE(types.Equals(fake_manager_->GetLastRefreshRequestTypes()));
 
-  backend_->Shutdown(false);
+  backend_->Shutdown(SyncBackendHost::STOP);
   backend_.reset();
 }
 
diff --git a/chrome/browser/sync/glue/sync_backend_registrar.cc b/chrome/browser/sync/glue/sync_backend_registrar.cc
index ab24e14..84fcd27 100644
--- a/chrome/browser/sync/glue/sync_backend_registrar.cc
+++ b/chrome/browser/sync/glue/sync_backend_registrar.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/sync/glue/ui_model_worker.h"
 #include "content/public/browser/browser_thread.h"
 #include "sync/internal_api/public/engine/passive_model_worker.h"
+#include "sync/internal_api/public/user_share.h"
 
 using content::BrowserThread;
 
@@ -54,27 +55,43 @@
 }  // namespace
 
 SyncBackendRegistrar::SyncBackendRegistrar(
-    const std::string& name, Profile* profile,
-    base::MessageLoop* sync_loop) :
+    const std::string& name,
+    Profile* profile,
+    scoped_ptr<base::Thread> sync_thread) :
     name_(name),
-    profile_(profile),
-    sync_loop_(sync_loop),
-    ui_worker_(new UIModelWorker(this)),
-    stopped_on_ui_thread_(false) {
+    profile_(profile) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   CHECK(profile_);
-  DCHECK(sync_loop_);
+
+  sync_thread_ = sync_thread.Pass();
+  if (!sync_thread_) {
+    sync_thread_.reset(new base::Thread("Chrome_SyncThread"));
+    CHECK(sync_thread_->Start());
+  }
+
   workers_[syncer::GROUP_DB] = new DatabaseModelWorker(this);
+  workers_[syncer::GROUP_DB]->RegisterForLoopDestruction();
+
   workers_[syncer::GROUP_FILE] = new FileModelWorker(this);
-  workers_[syncer::GROUP_UI] = ui_worker_;
-  workers_[syncer::GROUP_PASSIVE] = new syncer::PassiveModelWorker(sync_loop_,
-                                                                   this);
+  workers_[syncer::GROUP_FILE]->RegisterForLoopDestruction();
+
+  workers_[syncer::GROUP_UI] = new UIModelWorker(this);
+  workers_[syncer::GROUP_UI]->RegisterForLoopDestruction();
+
+  // GROUP_PASSIVE worker does work on sync_loop_. But sync_loop_ is not
+  // stopped until all workers have stopped. To break the cycle, use UI loop
+  // instead.
+  workers_[syncer::GROUP_PASSIVE] =
+      new syncer::PassiveModelWorker(sync_thread_->message_loop(), this);
+  workers_[syncer::GROUP_PASSIVE]->RegisterForLoopDestruction();
 
   HistoryService* history_service =
       HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS);
   if (history_service) {
     workers_[syncer::GROUP_HISTORY] =
         new HistoryModelWorker(history_service->AsWeakPtr(), this);
+    workers_[syncer::GROUP_HISTORY]->RegisterForLoopDestruction();
+
   }
 
   scoped_refptr<PasswordStore> password_store =
@@ -82,20 +99,17 @@
   if (password_store.get()) {
     workers_[syncer::GROUP_PASSWORD] =
         new PasswordModelWorker(password_store, this);
+    workers_[syncer::GROUP_PASSWORD]->RegisterForLoopDestruction();
   }
 }
 
-SyncBackendRegistrar::~SyncBackendRegistrar() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(stopped_on_ui_thread_);
-}
-
 void SyncBackendRegistrar::SetInitialTypes(syncer::ModelTypeSet initial_types) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   base::AutoLock lock(lock_);
 
-  // This function should be called only once, shortly after construction.  The
-  // routing info at that point is expected to be emtpy.
+
+  // This function should be called only once, shortly after construction. The
+  // routing info at that point is expected to be empty.
   DCHECK(routing_info_.empty());
 
   // Set our initial state to reflect the current status of the sync directory.
@@ -176,16 +190,13 @@
   return last_configured_types_;
 }
 
-void SyncBackendRegistrar::StopOnUIThread() {
+void SyncBackendRegistrar::RequestWorkerStopOnUIThread() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!stopped_on_ui_thread_);
-  ui_worker_->Stop();
-  stopped_on_ui_thread_ = true;
-}
-
-void SyncBackendRegistrar::OnSyncerShutdownComplete() {
-  DCHECK_EQ(base::MessageLoop::current(), sync_loop_);
-  ui_worker_->OnSyncerShutdownComplete();
+  base::AutoLock lock(lock_);
+  for (WorkerMap::const_iterator it = workers_.begin();
+       it != workers_.end(); ++it) {
+    it->second->RequestStop();
+  }
 }
 
 void SyncBackendRegistrar::ActivateDataType(
@@ -193,6 +204,8 @@
     syncer::ModelSafeGroup group,
     ChangeProcessor* change_processor,
     syncer::UserShare* user_share) {
+  DVLOG(1) << "Activate: " << syncer::ModelTypeToString(type);
+
   CHECK(IsOnThreadForGroup(type, group));
   base::AutoLock lock(lock_);
   // Ensure that the given data type is in the PASSIVE group.
@@ -213,6 +226,8 @@
 }
 
 void SyncBackendRegistrar::DeactivateDataType(syncer::ModelType type) {
+  DVLOG(1) << "Deactivate: " << syncer::ModelTypeToString(type);
+
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || IsControlType(type));
   base::AutoLock lock(lock_);
 
@@ -282,8 +297,8 @@
 ChangeProcessor* SyncBackendRegistrar::GetProcessorUnsafe(
     syncer::ModelType type) const {
   lock_.AssertAcquired();
-  std::map<syncer::ModelType, ChangeProcessor*>::const_iterator it =
-      processors_.find(type);
+  std::map<syncer::ModelType, ChangeProcessor*>::const_iterator
+      it = processors_.find(type);
 
   // Until model association happens for a datatype, it will not
   // appear in the processors list.  During this time, it is OK to
@@ -304,8 +319,60 @@
                             GetGroupForModelType(model_type, routing_info_));
 }
 
+SyncBackendRegistrar::~SyncBackendRegistrar() {
+  DCHECK(workers_.empty());
+}
+
 void SyncBackendRegistrar::OnWorkerLoopDestroyed(syncer::ModelSafeGroup group) {
-  // Do nothing for now.
+  RemoveWorker(group);
+}
+
+void SyncBackendRegistrar::OnWorkerUnregistrationDone(
+    syncer::ModelSafeGroup group) {
+  RemoveWorker(group);
+}
+
+void SyncBackendRegistrar::RemoveWorker(syncer::ModelSafeGroup group) {
+  DVLOG(1) << "Remove " << ModelSafeGroupToString(group) << " worker.";
+
+  bool last_worker = false;
+  {
+    base::AutoLock al(lock_);
+    WorkerMap::iterator it = workers_.find(group);
+    CHECK(it != workers_.end());
+    stopped_workers_.push_back(it->second);
+    workers_.erase(it);
+    last_worker = workers_.empty();
+  }
+
+  if (last_worker) {
+    // Self-destruction after last worker.
+    DVLOG(1) << "Destroy registrar on loop of "
+        << ModelSafeGroupToString(group);
+    delete this;
+  }
+}
+
+scoped_ptr<base::Thread> SyncBackendRegistrar::ReleaseSyncThread() {
+  return sync_thread_.Pass();
+}
+
+void SyncBackendRegistrar::Shutdown() {
+  // All data types should have been deactivated by now.
+  DCHECK(processors_.empty());
+
+  // Unregister worker from observing loop destruction.
+  base::AutoLock al(lock_);
+  for (WorkerMap::iterator it = workers_.begin();
+      it != workers_.end(); ++it) {
+    it->second->UnregisterForLoopDestruction(
+        base::Bind(&SyncBackendRegistrar::OnWorkerUnregistrationDone,
+                   base::Unretained(this)));
+  }
+}
+
+base::Thread* SyncBackendRegistrar::sync_thread() {
+  return sync_thread_.get();
 }
 
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/glue/sync_backend_registrar.h b/chrome/browser/sync/glue/sync_backend_registrar.h
index f7ea1fb..efe07e2 100644
--- a/chrome/browser/sync/glue/sync_backend_registrar.h
+++ b/chrome/browser/sync/glue/sync_backend_registrar.h
@@ -12,6 +12,7 @@
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
 #include "sync/internal_api/public/base/model_type.h"
 #include "sync/internal_api/public/engine/model_safe_worker.h"
 #include "sync/internal_api/public/sync_manager.h"
@@ -41,19 +42,19 @@
   // |sync_loop|.  Must be created on the UI thread.
   SyncBackendRegistrar(const std::string& name,
                        Profile* profile,
-                       base::MessageLoop* sync_loop);
+                       scoped_ptr<base::Thread> sync_thread);
 
   // SyncBackendRegistrar must be destroyed as follows:
   //
-  //   1) On the sync thread, call OnSyncerShutdownComplete() after
-  //      the syncer is shutdown.
-  //   2) Meanwhile, on the UI thread, call StopOnUIThread(), which
-  //      blocks until OnSyncerShutdownComplete() is called.
-  //   3) Destroy the SyncBackendRegistrar.
-  //
-  // This is to handle the complicated shutdown requirements of the
-  // UIModelWorker (since the UI thread is both the main thread and a
-  // thread which the syncer pushes changes to).
+  //   1) On the UI thread, call RequestWorkerStopOnUIThread().
+  //   2) UI posts task to shut down syncer on sync thread.
+  //   3) If sync is disabled, call ReleaseSyncThread() on the UI thread.
+  //   3) UI posts SyncBackendRegistrar::ShutDown() on sync thread to
+  //      unregister workers from observing destruction of their working loops.
+  //   4) Workers notify registrar when unregistration finishes or working
+  //      loops are destroyed. Registrar destroys itself on last worker
+  //      notification. Sync thread will be stopped if ownership was not
+  //      released.
   virtual ~SyncBackendRegistrar();
 
   // Informs the SyncBackendRegistrar of the currently enabled set of types.
@@ -80,10 +81,7 @@
   syncer::ModelTypeSet GetLastConfiguredTypes() const;
 
   // Must be called from the UI thread. (See destructor comment.)
-  void StopOnUIThread();
-
-  // Must be called from the sync thread. (See destructor comment.)
-  void OnSyncerShutdownComplete();
+  void RequestWorkerStopOnUIThread();
 
   // Activates the given data type (which should belong to the given
   // group) and starts the given change processor.  Must be called
@@ -117,9 +115,25 @@
   // syncer::WorkerLoopDestructionObserver implementation.
   virtual void OnWorkerLoopDestroyed(syncer::ModelSafeGroup group) OVERRIDE;
 
+  // Release ownership of |sync_thread_|. Called when sync is disabled.
+  scoped_ptr<base::Thread> ReleaseSyncThread();
+
+  // Unregister workers from loop destruction observation.
+  void Shutdown();
+
+  base::Thread* sync_thread();
+
  private:
   typedef std::map<syncer::ModelSafeGroup,
-                   scoped_refptr<syncer::ModelSafeWorker> > WorkerMap;
+      scoped_refptr<syncer::ModelSafeWorker> > WorkerMap;
+  typedef std::map<syncer::ModelType, ChangeProcessor*>
+      ProcessorMap;
+
+  // Callback after workers unregister from observing destruction of their
+  // working loops.
+  void OnWorkerUnregistrationDone(syncer::ModelSafeGroup group);
+
+  void RemoveWorker(syncer::ModelSafeGroup group);
 
   // Returns the change processor for the given model, or NULL if none
   // exists.  Must be called from |group|'s native thread.
@@ -140,12 +154,6 @@
 
   Profile* const profile_;
 
-  base::MessageLoop* const sync_loop_;
-
-  const scoped_refptr<UIModelWorker> ui_worker_;
-
-  bool stopped_on_ui_thread_;
-
   // Protects all variables below.
   mutable base::Lock lock_;
 
@@ -163,12 +171,21 @@
   syncer::ModelSafeRoutingInfo routing_info_;
 
   // The change processors that handle the different data types.
-  std::map<syncer::ModelType, ChangeProcessor*> processors_;
+  ProcessorMap processors_;
 
   // The types that were enabled as of the last configuration. Updated on each
   // call to ConfigureDataTypes as well as SetInitialTypes.
   syncer::ModelTypeSet last_configured_types_;
 
+  // Parks stopped workers because they may still be referenced by syncer.
+  std::vector<scoped_refptr<syncer::ModelSafeWorker> > stopped_workers_;
+
+  // Declare |sync_thread_| at the end so that it will be destroyed before
+  // objects above because tasks on sync thread depend on those objects,
+  // e.g. Shutdown() depends on |lock_|, SyncManager::Init() depends on
+  // workers, etc.
+  scoped_ptr<base::Thread> sync_thread_;
+
   DISALLOW_COPY_AND_ASSIGN(SyncBackendRegistrar);
 };
 
diff --git a/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc b/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc
index 979f9a5..eed132c 100644
--- a/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc
+++ b/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc
@@ -34,18 +34,59 @@
 using syncer::ModelType;
 using syncer::ModelTypeFromInt;
 
+void TriggerChanges(SyncBackendRegistrar* registrar, ModelType type) {
+  registrar->OnChangesApplied(type, 0, NULL,
+                              syncer::ImmutableChangeRecordList());
+  registrar->OnChangesComplete(type);
+}
+
 class SyncBackendRegistrarTest : public testing::Test {
+ public:
+  void TestNonUIDataTypeActivationAsync(ChangeProcessor* processor,
+                                        base::WaitableEvent* done) {
+    registrar_->ActivateDataType(AUTOFILL,
+                                 syncer::GROUP_DB,
+                                 processor,
+                                 test_user_share_.user_share());
+    syncer::ModelSafeRoutingInfo expected_routing_info;
+    expected_routing_info[AUTOFILL] = syncer::GROUP_DB;
+    ExpectRoutingInfo(registrar_.get(), expected_routing_info);
+    ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet(AUTOFILL));
+    TriggerChanges(registrar_.get(), AUTOFILL);
+    done->Signal();
+  }
+
  protected:
-  SyncBackendRegistrarTest() : ui_thread_(BrowserThread::UI, &loop_) {}
+  SyncBackendRegistrarTest() :
+    sync_thread_(NULL),
+    ui_thread_(BrowserThread::UI, &ui_loop_),
+    db_thread_(BrowserThread::DB),
+    file_thread_(BrowserThread::FILE),
+    io_thread_(BrowserThread::IO) {}
 
   virtual ~SyncBackendRegistrarTest() {}
 
   virtual void SetUp() {
+    db_thread_.Start();
+    file_thread_.Start();
+    io_thread_.Start();
     test_user_share_.SetUp();
+    registrar_.reset(new SyncBackendRegistrar("test", &profile_,
+                                              scoped_ptr<base::Thread>()));
+    sync_thread_ = registrar_->sync_thread();
   }
 
   virtual void TearDown() {
+    registrar_->RequestWorkerStopOnUIThread();
     test_user_share_.TearDown();
+    sync_thread_->message_loop()->PostTask(
+        FROM_HERE,
+        base::Bind(&SyncBackendRegistrar::Shutdown,
+                   base::Unretained(registrar_.release())));
+    sync_thread_->message_loop()->RunUntilIdle();
+    io_thread_.Stop();
+    file_thread_.Stop();
+    db_thread_.Stop();
   }
 
   void ExpectRoutingInfo(
@@ -61,42 +102,41 @@
     for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
       ModelType model_type = ModelTypeFromInt(i);
       EXPECT_EQ(types.Has(model_type),
-                registrar.IsTypeActivatedForTest(model_type));
+                registrar_->IsTypeActivatedForTest(model_type));
     }
   }
 
-  base::MessageLoop loop_;
+  base::MessageLoop ui_loop_;
   syncer::TestUserShare test_user_share_;
+  TestingProfile profile_;
+  scoped_ptr<SyncBackendRegistrar> registrar_;
 
- private:
+  base::Thread* sync_thread_;
   content::TestBrowserThread ui_thread_;
+  content::TestBrowserThread db_thread_;
+  content::TestBrowserThread file_thread_;
+  content::TestBrowserThread io_thread_;
 };
 
 TEST_F(SyncBackendRegistrarTest, ConstructorEmpty) {
-  TestingProfile profile;
-  SyncBackendRegistrar registrar("test", &profile, &loop_);
-  registrar.SetInitialTypes(ModelTypeSet());
-  EXPECT_FALSE(registrar.IsNigoriEnabled());
+  registrar_->SetInitialTypes(ModelTypeSet());
+  EXPECT_FALSE(registrar_->IsNigoriEnabled());
   {
     std::vector<syncer::ModelSafeWorker*> workers;
-    registrar.GetWorkers(&workers);
+    registrar_->GetWorkers(&workers);
     EXPECT_EQ(4u, workers.size());
   }
-  ExpectRoutingInfo(&registrar, syncer::ModelSafeRoutingInfo());
-  ExpectHasProcessorsForTypes(registrar, ModelTypeSet());
-  registrar.OnSyncerShutdownComplete();
-  registrar.StopOnUIThread();
+  ExpectRoutingInfo(registrar_.get(), syncer::ModelSafeRoutingInfo());
+  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
 }
 
 TEST_F(SyncBackendRegistrarTest, ConstructorNonEmpty) {
-  TestingProfile profile;
   const ModelTypeSet initial_types(BOOKMARKS, NIGORI, PASSWORDS);
-  SyncBackendRegistrar registrar("test", &profile, &loop_);
-  registrar.SetInitialTypes(initial_types);
-  EXPECT_TRUE(registrar.IsNigoriEnabled());
+  registrar_->SetInitialTypes(initial_types);
+  EXPECT_TRUE(registrar_->IsNigoriEnabled());
   {
     std::vector<syncer::ModelSafeWorker*> workers;
-    registrar.GetWorkers(&workers);
+    registrar_->GetWorkers(&workers);
     EXPECT_EQ(4u, workers.size());
   }
   {
@@ -104,71 +144,56 @@
     expected_routing_info[BOOKMARKS] = syncer::GROUP_PASSIVE;
     expected_routing_info[NIGORI] = syncer::GROUP_PASSIVE;
     // Passwords dropped because of no password store.
-    ExpectRoutingInfo(&registrar, expected_routing_info);
+    ExpectRoutingInfo(registrar_.get(), expected_routing_info);
   }
-  ExpectHasProcessorsForTypes(registrar, ModelTypeSet());
-  registrar.OnSyncerShutdownComplete();
-  registrar.StopOnUIThread();
+  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
 }
 
 TEST_F(SyncBackendRegistrarTest, ConfigureDataTypes) {
-  TestingProfile profile;
-  SyncBackendRegistrar registrar("test", &profile, &loop_);
-  registrar.SetInitialTypes(ModelTypeSet());
+  registrar_->SetInitialTypes(ModelTypeSet());
 
   // Add.
   const ModelTypeSet types1(BOOKMARKS, NIGORI, AUTOFILL);
   EXPECT_TRUE(
-      registrar.ConfigureDataTypes(types1, ModelTypeSet()).Equals(types1));
+      registrar_->ConfigureDataTypes(types1, ModelTypeSet()).Equals(types1));
   {
     syncer::ModelSafeRoutingInfo expected_routing_info;
     expected_routing_info[BOOKMARKS] = syncer::GROUP_PASSIVE;
     expected_routing_info[NIGORI] = syncer::GROUP_PASSIVE;
     expected_routing_info[AUTOFILL] = syncer::GROUP_PASSIVE;
-    ExpectRoutingInfo(&registrar, expected_routing_info);
+    ExpectRoutingInfo(registrar_.get(), expected_routing_info);
   }
-  ExpectHasProcessorsForTypes(registrar, ModelTypeSet());
-  EXPECT_TRUE(types1.Equals(registrar.GetLastConfiguredTypes()));
+  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
+  EXPECT_TRUE(types1.Equals(registrar_->GetLastConfiguredTypes()));
 
   // Add and remove.
   const ModelTypeSet types2(PREFERENCES, THEMES);
-  EXPECT_TRUE(registrar.ConfigureDataTypes(types2, types1).Equals(types2));
+  EXPECT_TRUE(registrar_->ConfigureDataTypes(types2, types1).Equals(types2));
   {
     syncer::ModelSafeRoutingInfo expected_routing_info;
     expected_routing_info[PREFERENCES] = syncer::GROUP_PASSIVE;
     expected_routing_info[THEMES] = syncer::GROUP_PASSIVE;
-    ExpectRoutingInfo(&registrar, expected_routing_info);
+    ExpectRoutingInfo(registrar_.get(), expected_routing_info);
   }
-  ExpectHasProcessorsForTypes(registrar, ModelTypeSet());
-  EXPECT_TRUE(types2.Equals(registrar.GetLastConfiguredTypes()));
+  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
+  EXPECT_TRUE(types2.Equals(registrar_->GetLastConfiguredTypes()));
 
   // Remove.
-  EXPECT_TRUE(registrar.ConfigureDataTypes(ModelTypeSet(), types2).Empty());
-  ExpectRoutingInfo(&registrar, syncer::ModelSafeRoutingInfo());
-  ExpectHasProcessorsForTypes(registrar, ModelTypeSet());
-  EXPECT_TRUE(ModelTypeSet().Equals(registrar.GetLastConfiguredTypes()));
-
-  registrar.OnSyncerShutdownComplete();
-  registrar.StopOnUIThread();
-}
-
-void TriggerChanges(SyncBackendRegistrar* registrar, ModelType type) {
-  registrar->OnChangesApplied(type, 0, NULL,
-                              syncer::ImmutableChangeRecordList());
-  registrar->OnChangesComplete(type);
+  EXPECT_TRUE(registrar_->ConfigureDataTypes(ModelTypeSet(), types2).Empty());
+  ExpectRoutingInfo(registrar_.get(), syncer::ModelSafeRoutingInfo());
+  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
+  EXPECT_TRUE(ModelTypeSet().Equals(registrar_->GetLastConfiguredTypes()));
 }
 
 TEST_F(SyncBackendRegistrarTest, ActivateDeactivateUIDataType) {
   InSequence in_sequence;
-  TestingProfile profile;
-  SyncBackendRegistrar registrar("test", &profile, &loop_);
-  registrar.SetInitialTypes(ModelTypeSet());
+  registrar_->SetInitialTypes(ModelTypeSet());
 
   // Should do nothing.
-  TriggerChanges(&registrar, BOOKMARKS);
+  TriggerChanges(registrar_.get(), BOOKMARKS);
 
   StrictMock<ChangeProcessorMock> change_processor_mock;
-  EXPECT_CALL(change_processor_mock, StartImpl(&profile));
+  EXPECT_CALL(change_processor_mock, StartImpl(&profile_));
   EXPECT_CALL(change_processor_mock, IsRunning())
       .WillRepeatedly(Return(true));
   EXPECT_CALL(change_processor_mock, ApplyChangesFromSyncModel(NULL, _, _));
@@ -180,42 +205,40 @@
 
   const ModelTypeSet types(BOOKMARKS);
   EXPECT_TRUE(
-      registrar.ConfigureDataTypes(types, ModelTypeSet()).Equals(types));
-  registrar.ActivateDataType(BOOKMARKS, syncer::GROUP_UI,
+      registrar_->ConfigureDataTypes(types, ModelTypeSet()).Equals(types));
+  registrar_->ActivateDataType(BOOKMARKS, syncer::GROUP_UI,
                              &change_processor_mock,
                              test_user_share_.user_share());
   {
     syncer::ModelSafeRoutingInfo expected_routing_info;
     expected_routing_info[BOOKMARKS] = syncer::GROUP_UI;
-    ExpectRoutingInfo(&registrar, expected_routing_info);
+    ExpectRoutingInfo(registrar_.get(), expected_routing_info);
   }
-  ExpectHasProcessorsForTypes(registrar, types);
+  ExpectHasProcessorsForTypes(*registrar_, types);
 
-  TriggerChanges(&registrar, BOOKMARKS);
+  TriggerChanges(registrar_.get(), BOOKMARKS);
 
-  registrar.DeactivateDataType(BOOKMARKS);
-  ExpectRoutingInfo(&registrar, syncer::ModelSafeRoutingInfo());
-  ExpectHasProcessorsForTypes(registrar, ModelTypeSet());
+  registrar_->DeactivateDataType(BOOKMARKS);
+  ExpectRoutingInfo(registrar_.get(), syncer::ModelSafeRoutingInfo());
+  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
 
   // Should do nothing.
-  TriggerChanges(&registrar, BOOKMARKS);
+  TriggerChanges(registrar_.get(), BOOKMARKS);
+}
 
-  registrar.OnSyncerShutdownComplete();
-  registrar.StopOnUIThread();
+void ActiviateDoneOnDb(base::WaitableEvent* done) {
+  done->Signal();
 }
 
 TEST_F(SyncBackendRegistrarTest, ActivateDeactivateNonUIDataType) {
-  content::TestBrowserThread db_thread(BrowserThread::DB, &loop_);
   InSequence in_sequence;
-  TestingProfile profile;
-  SyncBackendRegistrar registrar("test", &profile, &loop_);
-  registrar.SetInitialTypes(ModelTypeSet());
+  registrar_->SetInitialTypes(ModelTypeSet());
 
   // Should do nothing.
-  TriggerChanges(&registrar, AUTOFILL);
+  TriggerChanges(registrar_.get(), AUTOFILL);
 
   StrictMock<ChangeProcessorMock> change_processor_mock;
-  EXPECT_CALL(change_processor_mock, StartImpl(&profile));
+  EXPECT_CALL(change_processor_mock, StartImpl(&profile_));
   EXPECT_CALL(change_processor_mock, IsRunning())
       .WillRepeatedly(Return(true));
   EXPECT_CALL(change_processor_mock, ApplyChangesFromSyncModel(NULL, _, _));
@@ -227,28 +250,24 @@
 
   const ModelTypeSet types(AUTOFILL);
   EXPECT_TRUE(
-      registrar.ConfigureDataTypes(types, ModelTypeSet()).Equals(types));
-  registrar.ActivateDataType(AUTOFILL, syncer::GROUP_DB,
-                             &change_processor_mock,
-                             test_user_share_.user_share());
-  {
-    syncer::ModelSafeRoutingInfo expected_routing_info;
-    expected_routing_info[AUTOFILL] = syncer::GROUP_DB;
-    ExpectRoutingInfo(&registrar, expected_routing_info);
-  }
-  ExpectHasProcessorsForTypes(registrar, types);
+      registrar_->ConfigureDataTypes(types, ModelTypeSet()).Equals(types));
 
-  TriggerChanges(&registrar, AUTOFILL);
+  base::WaitableEvent done(false, false);
+  BrowserThread::PostTask(
+      BrowserThread::DB,
+      FROM_HERE,
+      base::Bind(&SyncBackendRegistrarTest::TestNonUIDataTypeActivationAsync,
+                 base::Unretained(this),
+                 &change_processor_mock,
+                 &done));
+  done.Wait();
 
-  registrar.DeactivateDataType(AUTOFILL);
-  ExpectRoutingInfo(&registrar, syncer::ModelSafeRoutingInfo());
-  ExpectHasProcessorsForTypes(registrar, ModelTypeSet());
+  registrar_->DeactivateDataType(AUTOFILL);
+  ExpectRoutingInfo(registrar_.get(), syncer::ModelSafeRoutingInfo());
+  ExpectHasProcessorsForTypes(*registrar_, ModelTypeSet());
 
   // Should do nothing.
-  TriggerChanges(&registrar, AUTOFILL);
-
-  registrar.OnSyncerShutdownComplete();
-  registrar.StopOnUIThread();
+  TriggerChanges(registrar_.get(), AUTOFILL);
 }
 
 }  // namespace
diff --git a/chrome/browser/sync/glue/synced_session_tracker.cc b/chrome/browser/sync/glue/synced_session_tracker.cc
index 7499adb..402800c 100644
--- a/chrome/browser/sync/glue/synced_session_tracker.cc
+++ b/chrome/browser/sync/glue/synced_session_tracker.cc
@@ -87,6 +87,23 @@
   return true;
 }
 
+bool SyncedSessionTracker::LookupTabNodeIds(
+    const std::string& session_tag, std::set<int>* tab_node_ids) {
+  tab_node_ids->clear();
+  SyncedTabMap::const_iterator tab_map_iter =
+      synced_tab_map_.find(session_tag);
+  if (tab_map_iter == synced_tab_map_.end())
+    return false;
+
+  IDToSessionTabMap::const_iterator tab_iter = tab_map_iter->second.begin();
+  while (tab_iter != tab_map_iter->second.end()) {
+    if (tab_iter->second.tab_node_id != TabNodePool::kInvalidTabNodeID)
+      tab_node_ids->insert(tab_iter->second.tab_node_id);
+    ++tab_iter;
+  }
+  return true;
+}
+
 SyncedSession* SyncedSessionTracker::GetSession(
     const std::string& session_tag) {
   SyncedSession* synced_session = NULL;
@@ -225,7 +242,7 @@
     window_ptr = new SessionWindow();
     window_ptr->window_id.set_id(window_id);
     synced_window_map_[session_tag][window_id] =
-        SessionWindowWrapper(window_ptr, true);
+        SessionWindowWrapper(window_ptr, IS_OWNED);
     DVLOG(1) << "Putting new window " << window_id  << " at " << window_ptr
              << "in " << (session_tag == local_session_tag_ ?
                           "local session" : session_tag);
@@ -241,7 +258,23 @@
                                           SessionID::id_type window_id,
                                           SessionID::id_type tab_id,
                                           size_t tab_index) {
-  SessionTab* tab_ptr = GetTab(session_tag, tab_id);
+  // We're called here for two reasons. 1) We've received an update to the
+  // SessionWindow information of a SessionHeader node for a foreign session,
+  // and 2) The SessionHeader node for our local session changed. In both cases
+  // we need to update our tracking state to reflect the change.
+  //
+  // Because the SessionHeader nodes are separate from the individual tab nodes
+  // and we don't store tab_node_ids in the header / SessionWindow specifics,
+  // the tab_node_ids are not always available when processing headers.
+  // We know that we will eventually process (via GetTab) every single tab node
+  // in the system, so we permit ourselves to use kInvalidTabNodeID here and
+  // rely on the later update to build the mapping (or a restart).
+  // TODO(tim): Bug 98892. Update comment when Sync API conversion finishes to
+  // mention that in the meantime, the only ill effect is that we may not be
+  // able to fully clean up a stale foreign session, but it will get garbage
+  // collected eventually.
+  SessionTab* tab_ptr = GetTabImpl(
+      session_tag, tab_id, TabNodePool::kInvalidTabNodeID);
   unmapped_tabs_.erase(tab_ptr);
   synced_tab_map_[session_tag][tab_id].owned = true;
   tab_ptr->window_id.set_id(window_id);
@@ -259,12 +292,47 @@
 
 SessionTab* SyncedSessionTracker::GetTab(
     const std::string& session_tag,
-    SessionID::id_type tab_id) {
+    SessionID::id_type tab_id,
+    int tab_node_id) {
+  DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
+  return GetTabImpl(session_tag, tab_id, tab_node_id);
+}
+
+SessionTab* SyncedSessionTracker::GetTabImpl(
+    const std::string& session_tag,
+    SessionID::id_type tab_id,
+    int tab_node_id) {
   SessionTab* tab_ptr = NULL;
   IDToSessionTabMap::iterator iter =
       synced_tab_map_[session_tag].find(tab_id);
   if (iter != synced_tab_map_[session_tag].end()) {
     tab_ptr = iter->second.tab_ptr;
+    if (tab_node_id != TabNodePool::kInvalidTabNodeID &&
+        tab_id != TabNodePool::kInvalidTabID) {
+      // We likely created this tab as an out-of-order update to the header
+      // node for this session before actually associating the tab itself, so
+      // the tab node id wasn't available at the time.  Update it.
+
+      // TODO(shashishekhar): Make sure the following constraint works.
+      // All nodes either have unique tab_ids or have kInvalidTabID as their
+      // tab_id. There can be multiple nodes associated with kInvalidTabID but
+      // there is always one-to-one mapping between valid tab_ids and
+      // tab_node_ids.
+
+      if (iter->second.tab_node_id != tab_node_id &&
+          iter->second.tab_node_id != TabNodePool::kInvalidTabNodeID) {
+        // We are updating tab_node_id for a valid tab_id, ideally this should
+        // never happen, but there are a few existing foreign sessions that
+        // may violate this constraint.
+        // TODO(shashishekhar): Introduce a DCHECK here to enforce this
+        // constraint in future.
+        DLOG(ERROR)
+            << "Updating tab_node_id for " << session_tag << " tab: " << tab_id
+            << " from: " << iter->second.tab_node_id << " to: " << tab_node_id;
+      }
+      iter->second.tab_node_id = tab_node_id;
+    }
+
     if (VLOG_IS_ON(1)) {
       std::string title;
       if (tab_ptr->navigations.size() > 0) {
@@ -279,7 +347,9 @@
   } else {
     tab_ptr = new SessionTab();
     tab_ptr->tab_id.set_id(tab_id);
-    synced_tab_map_[session_tag][tab_id] = SessionTabWrapper(tab_ptr, false);
+    synced_tab_map_[session_tag][tab_id] = SessionTabWrapper(tab_ptr,
+                                                             NOT_OWNED,
+                                                             tab_node_id);
     unmapped_tabs_.insert(tab_ptr);
     DVLOG(1) << "Getting "
              << (session_tag == local_session_tag_ ?
diff --git a/chrome/browser/sync/glue/synced_session_tracker.h b/chrome/browser/sync/glue/synced_session_tracker.h
index 782a257..4ef7db2 100644
--- a/chrome/browser/sync/glue/synced_session_tracker.h
+++ b/chrome/browser/sync/glue/synced_session_tracker.h
@@ -15,6 +15,7 @@
 #include "chrome/browser/sessions/session_id.h"
 #include "chrome/browser/sessions/session_types.h"
 #include "chrome/browser/sync/glue/synced_session.h"
+#include "chrome/browser/sync/glue/tab_node_pool.h"
 
 namespace browser_sync {
 
@@ -103,8 +104,23 @@
   // Returns a pointer to the SessionTab object associated with |tab_id| for
   // the session specified with |session_tag|. If none exists, creates one.
   // Ownership of the SessionTab remains within the SyncedSessionTracker.
+  // |tab_node_id| must be a valid node id for the node backing this tab.
   SessionTab* GetTab(const std::string& session_tag,
-                     SessionID::id_type tab_id);
+                     SessionID::id_type tab_id,
+                     int tab_node_id);
+
+  // Fills |tab_node_ids| with the tab node ids (see GetTab) for all the tabs*
+  // associated with the session having tag |session_tag|.
+  // Returns false if we don't have any record of the session.  If no tabs were
+  // found as part of the session, the return value will be true but
+  // |tab_node_ids| will be empty.
+  //
+  // * - note that this only returns the ids we're aware of; it's possible we
+  // don't have the latest tab state from a foreign session and it's also
+  // possible we just haven't updated the tab_node_id for a tab yet, so the
+  // result list should not be treated as authoritative.
+  bool LookupTabNodeIds(const std::string& session_tag,
+                        std::set<int>* tab_node_ids);
 
   // Free the memory for all dynamically allocated objects and clear the
   // tracking structures.
@@ -136,22 +152,42 @@
   // Note, we pair pointers with bools so that we can track what is owned and
   // what can be deleted (see ResetSessionTracking(..) and CleanupSession(..)
   // above).
+  // The wrappers also serve as a convenient place to augment state stored in
+  // SessionTab for sync purposes, such as |tab_node_id|.
+  // IsOwned is used as a wrapper constructor parameter for readability.
+  enum OwnedState {
+    IS_OWNED,
+    NOT_OWNED
+  };
   struct SessionTabWrapper {
-    SessionTabWrapper() : tab_ptr(NULL), owned(false) {}
-    SessionTabWrapper(SessionTab* tab_ptr, bool owned)
+    SessionTabWrapper() : tab_ptr(NULL),
+                          owned(false),
+                          tab_node_id(TabNodePool::kInvalidTabNodeID) {}
+    SessionTabWrapper(SessionTab* tab_ptr, OwnedState owned, int tab_node_id)
         : tab_ptr(tab_ptr),
-          owned(owned) {}
+          owned(owned == IS_OWNED),
+          tab_node_id(tab_node_id) {}
     SessionTab* tab_ptr;
+
+    // This is used as part of a mark-and-sweep approach to garbage
+    // collection for closed tabs that are no longer "in use", or "owned".
+    // ResetSessionTracking will clear |owned| bits, and if it is not claimed
+    // by a window by the time CleanupSession is called it will be deleted.
     bool owned;
+
+    // This lets us identify the sync node that is "backing" this tab in the
+    // sync model, whether it is part of a local or foreign session. The
+    // "tab node id" is described in session_specifics.proto.
+    int tab_node_id;
   };
   typedef std::map<SessionID::id_type, SessionTabWrapper> IDToSessionTabMap;
   typedef std::map<std::string, IDToSessionTabMap> SyncedTabMap;
 
   struct SessionWindowWrapper {
     SessionWindowWrapper() : window_ptr(NULL), owned(false) {}
-    SessionWindowWrapper(SessionWindow* window_ptr, bool owned)
+    SessionWindowWrapper(SessionWindow* window_ptr, OwnedState owned)
         : window_ptr(window_ptr),
-          owned(owned) {}
+          owned(owned == IS_OWNED) {}
     SessionWindow* window_ptr;
     bool owned;
   };
@@ -165,6 +201,11 @@
   bool DeleteOldSessionWindowIfNecessary(SessionWindowWrapper window_wrapper);
   bool DeleteOldSessionTabIfNecessary(SessionTabWrapper tab_wrapper);
 
+  // Implementation for GetTab(...) above, permits invalid tab_node_id.
+  SessionTab* GetTabImpl(const std::string& session_tag,
+                         SessionID::id_type tab_id,
+                         int tab_node_id);
+
   // Per client mapping of tab id's to their SessionTab objects.
   // Key: session tag.
   // Value: Tab id to SessionTabWrapper map.
diff --git a/chrome/browser/sync/glue/synced_session_tracker_unittest.cc b/chrome/browser/sync/glue/synced_session_tracker_unittest.cc
index 64187dc..7b50cbd 100644
--- a/chrome/browser/sync/glue/synced_session_tracker_unittest.cc
+++ b/chrome/browser/sync/glue/synced_session_tracker_unittest.cc
@@ -29,8 +29,8 @@
 
 TEST_F(SyncedSessionTrackerTest, GetTabUnmapped) {
   SyncedSessionTracker tracker;
-  SessionTab* tab = tracker.GetTab("tag", 0);
-  ASSERT_EQ(tab, tracker.GetTab("tag", 0));
+  SessionTab* tab = tracker.GetTab("tag", 0, 0);
+  ASSERT_EQ(tab, tracker.GetTab("tag", 0, 0));
   // Should clean up memory on its own.
 }
 
@@ -49,7 +49,7 @@
   SyncedSession* session = tracker.GetSession("tag");
   ASSERT_EQ(1U, session->windows.size());
   ASSERT_EQ(1U, session->windows[10]->tabs.size());
-  ASSERT_EQ(tracker.GetTab("tag", 15), session->windows[10]->tabs[0]);
+  ASSERT_EQ(tracker.GetTab("tag", 15, 1), session->windows[10]->tabs[0]);
   // Should clean up memory on its own.
 }
 
@@ -61,7 +61,7 @@
   tracker.GetSession("tag2");
   tracker.PutWindowInSession("tag1", 0);
   tracker.PutTabInWindow("tag1", 0, 15, 0);
-  SessionTab* tab = tracker.GetTab("tag1", 15);
+  SessionTab* tab = tracker.GetTab("tag1", 15, 1);
   ASSERT_TRUE(tab);
   tab->navigations.push_back(
       sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
@@ -110,16 +110,16 @@
   ASSERT_TRUE(tracker.Empty());
   ASSERT_EQ(0U, tracker.num_synced_sessions());
   ASSERT_EQ(0U, tracker.num_synced_tabs(tag1));
-  tabs1.push_back(tracker.GetTab(tag1, 0));
-  tabs1.push_back(tracker.GetTab(tag1, 1));
-  tabs1.push_back(tracker.GetTab(tag1, 2));
+  tabs1.push_back(tracker.GetTab(tag1, 0, 0));
+  tabs1.push_back(tracker.GetTab(tag1, 1, 1));
+  tabs1.push_back(tracker.GetTab(tag1, 2, 2));
   ASSERT_EQ(3U, tracker.num_synced_tabs(tag1));
   ASSERT_EQ(0U, tracker.num_synced_sessions());
-  temp_tab = tracker.GetTab(tag1, 0);  // Already created.
+  temp_tab = tracker.GetTab(tag1, 0, 0);  // Already created.
   ASSERT_EQ(3U, tracker.num_synced_tabs(tag1));
   ASSERT_EQ(0U, tracker.num_synced_sessions());
   ASSERT_EQ(tabs1[0], temp_tab);
-  tabs2.push_back(tracker.GetTab(tag2, 0));
+  tabs2.push_back(tracker.GetTab(tag2, 0, 0));
   ASSERT_EQ(1U, tracker.num_synced_tabs(tag2));
   ASSERT_EQ(0U, tracker.num_synced_sessions());
   ASSERT_FALSE(tracker.DeleteSession(tag3));
@@ -177,12 +177,66 @@
       // More attempts than tabs means we'll sometimes get the same tabs,
       // sometimes have to allocate new tabs.
       int rand_tab_num = base::RandInt(0, kMaxTabs);
-      SessionTab* tab = tracker.GetTab(tag, rand_tab_num);
+      SessionTab* tab = tracker.GetTab(tag, rand_tab_num, rand_tab_num + 1);
       ASSERT_TRUE(tab);
     }
   }
 }
 
+TEST_F(SyncedSessionTrackerTest, LookupTabNodeIds) {
+  SyncedSessionTracker tracker;
+  std::set<int> result;
+  std::string tag1 = "session1";
+  std::string tag2 = "session2";
+  std::string tag3 = "session3";
+
+  tracker.GetTab(tag1, 1, 1);
+  tracker.GetTab(tag1, 2, 2);
+  EXPECT_TRUE(tracker.LookupTabNodeIds(tag1, &result));
+  EXPECT_EQ(2U, result.size());
+  EXPECT_FALSE(result.end() == result.find(1));
+  EXPECT_FALSE(result.end() == result.find(2));
+  EXPECT_FALSE(tracker.LookupTabNodeIds(tag2, &result));
+
+  tracker.PutWindowInSession(tag1, 0);
+  tracker.PutTabInWindow(tag1, 0, 3, 0);
+  EXPECT_TRUE(tracker.LookupTabNodeIds(tag1, &result));
+  EXPECT_EQ(2U, result.size());
+
+  tracker.GetTab(tag1, 3, 3);
+  EXPECT_TRUE(tracker.LookupTabNodeIds(tag1, &result));
+  EXPECT_EQ(3U, result.size());
+  EXPECT_FALSE(result.end() == result.find(3));
+
+  tracker.GetTab(tag2, 1, 21);
+  tracker.GetTab(tag2, 2, 22);
+  EXPECT_TRUE(tracker.LookupTabNodeIds(tag2, &result));
+  EXPECT_EQ(2U, result.size());
+  EXPECT_FALSE(result.end() == result.find(21));
+  EXPECT_FALSE(result.end() == result.find(22));
+  EXPECT_TRUE(tracker.LookupTabNodeIds(tag1, &result));
+  EXPECT_EQ(3U, result.size());
+  EXPECT_FALSE(result.end() == result.find(1));
+  EXPECT_FALSE(result.end() == result.find(2));
+
+  EXPECT_FALSE(tracker.LookupTabNodeIds(tag3, &result));
+  tracker.PutWindowInSession(tag3, 1);
+  tracker.PutTabInWindow(tag3, 1, 5, 0);
+  EXPECT_TRUE(tracker.LookupTabNodeIds(tag3, &result));
+  EXPECT_TRUE(result.empty());
+  EXPECT_TRUE(tracker.DeleteSession(tag3));
+  EXPECT_FALSE(tracker.LookupTabNodeIds(tag3, &result));
+
+  EXPECT_TRUE(tracker.DeleteSession(tag1));
+  EXPECT_FALSE(tracker.LookupTabNodeIds(tag1, &result));
+  EXPECT_TRUE(tracker.LookupTabNodeIds(tag2, &result));
+  EXPECT_EQ(2U, result.size());
+  EXPECT_FALSE(result.end() == result.find(21));
+  EXPECT_FALSE(result.end() == result.find(22));
+  EXPECT_TRUE(tracker.DeleteSession(tag2));
+  EXPECT_FALSE(tracker.LookupTabNodeIds(tag2, &result));
+}
+
 TEST_F(SyncedSessionTrackerTest, SessionTracking) {
   SyncedSessionTracker tracker;
   ASSERT_TRUE(tracker.Empty());
@@ -194,8 +248,8 @@
   tracker.PutWindowInSession(tag1, 0);
   tracker.PutTabInWindow(tag1, 0, 0, 0);
   tracker.PutTabInWindow(tag1, 0, 1, 1);
-  tracker.GetTab(tag1, 2)->window_id.set_id(0);  // Will be an unmapped tab.
-  tracker.GetTab(tag1, 3)->window_id.set_id(0);  // Will be an unmapped tab.
+  tracker.GetTab(tag1, 2, 3U)->window_id.set_id(0);  // Will be unmapped.
+  tracker.GetTab(tag1, 3, 4U)->window_id.set_id(0);  // Will be unmapped.
   tracker.PutWindowInSession(tag1, 1);
   tracker.PutTabInWindow(tag1, 1, 4, 0);
   tracker.PutTabInWindow(tag1, 1, 5, 1);
@@ -213,10 +267,12 @@
   ASSERT_EQ(1U, tracker.num_synced_tabs(tag2));
 
   // Reset tracking and get the current windows/tabs.
-  // We simulate moving a tab from one window to another, then closing the first
-  // window (including its one remaining tab), and opening a new tab on the
-  // remaining window.
-  tracker.GetTab(tag1, 6);  // New tab, arrived before meta node so unmapped.
+  // We simulate moving a tab from one window to another, then closing the
+  // first window (including its one remaining tab), and opening a new tab
+  // on the remaining window.
+
+  // New tab, arrived before meta node so unmapped.
+  tracker.GetTab(tag1, 6, 7U);
   tracker.ResetSessionTracking(tag1);
   tracker.PutWindowInSession(tag1, 0);
   tracker.PutTabInWindow(tag1, 0, 0, 0);
diff --git a/chrome/browser/sync/glue/synced_tab_delegate.h b/chrome/browser/sync/glue/synced_tab_delegate.h
index 2f46b76..a4033a4 100644
--- a/chrome/browser/sync/glue/synced_tab_delegate.h
+++ b/chrome/browser/sync/glue/synced_tab_delegate.h
@@ -55,8 +55,8 @@
   virtual bool HasWebContents() const = 0;
 
   // Session sync related methods.
-  virtual int64 GetSyncId() const = 0;
-  virtual void SetSyncId(int64 sync_id) = 0;
+  virtual int GetSyncId() const = 0;
+  virtual void SetSyncId(int sync_id) = 0;
   // Returns the SyncedTabDelegate associated with WebContents.
   static SyncedTabDelegate* ImplFromWebContents(
       content::WebContents* web_contents);
diff --git a/chrome/browser/sync/glue/synced_tab_delegate_android.cc b/chrome/browser/sync/glue/synced_tab_delegate_android.cc
index 1b5f26d..300b90a 100644
--- a/chrome/browser/sync/glue/synced_tab_delegate_android.cc
+++ b/chrome/browser/sync/glue/synced_tab_delegate_android.cc
@@ -106,11 +106,11 @@
       ->GetBlockedNavigations();
 }
 
-int64 SyncedTabDelegateAndroid::GetSyncId() const {
+int SyncedTabDelegateAndroid::GetSyncId() const {
   return tab_android_->GetSyncId();
 }
 
-void SyncedTabDelegateAndroid::SetSyncId(int64 sync_id) {
+void SyncedTabDelegateAndroid::SetSyncId(int sync_id) {
   tab_android_->SetSyncId(sync_id);
 }
 
diff --git a/chrome/browser/sync/glue/synced_tab_delegate_android.h b/chrome/browser/sync/glue/synced_tab_delegate_android.h
index 91d0972..3550277 100644
--- a/chrome/browser/sync/glue/synced_tab_delegate_android.h
+++ b/chrome/browser/sync/glue/synced_tab_delegate_android.h
@@ -38,8 +38,8 @@
   virtual content::NavigationEntry* GetActiveEntry() const OVERRIDE;
   virtual bool IsPinned() const OVERRIDE;
   virtual bool HasWebContents() const OVERRIDE;
-  virtual int64 GetSyncId() const OVERRIDE;
-  virtual void SetSyncId(int64 sync_id) OVERRIDE;
+  virtual int GetSyncId() const OVERRIDE;
+  virtual void SetSyncId(int sync_id) OVERRIDE;
 
   // Managed user related methods.
 
diff --git a/chrome/browser/sync/glue/tab_node_pool.cc b/chrome/browser/sync/glue/tab_node_pool.cc
index 2b41851..4d981f3 100644
--- a/chrome/browser/sync/glue/tab_node_pool.cc
+++ b/chrome/browser/sync/glue/tab_node_pool.cc
@@ -24,46 +24,50 @@
 const size_t TabNodePool::kFreeNodesHighWatermark = 100;
 
 TabNodePool::TabNodePool(ProfileSyncService* sync_service)
-    : max_used_tab_node_id_(0), sync_service_(sync_service) {}
+    : max_used_tab_node_id_(kInvalidTabNodeID),
+      sync_service_(sync_service) {}
+
+// static
+// We start vending tab node IDs at 0.
+const int TabNodePool::kInvalidTabNodeID = -1;
 
 TabNodePool::~TabNodePool() {}
 
 // Static
 std::string TabNodePool::TabIdToTag(
-    const std::string machine_tag,
-    size_t tab_node_id) {
-  return base::StringPrintf("%s %" PRIuS "", machine_tag.c_str(), tab_node_id);
+    const std::string machine_tag, int tab_node_id) {
+  return base::StringPrintf("%s %d", machine_tag.c_str(), tab_node_id);
 }
 
-void TabNodePool::AddTabNode(int64 sync_id,
-                             const SessionID& tab_id,
-                             size_t tab_node_id) {
-  DCHECK_GT(sync_id, syncer::kInvalidId);
+void TabNodePool::AddTabNode(int tab_node_id,
+                             const SessionID& tab_id) {
+  DCHECK_GT(tab_node_id, kInvalidTabNodeID);
   DCHECK_GT(tab_id.id(), kInvalidTabID);
-  DCHECK(syncid_tabid_map_.find(sync_id) == syncid_tabid_map_.end());
-  unassociated_nodes_.insert(sync_id);
+  DCHECK(nodeid_tabid_map_.find(tab_node_id) == nodeid_tabid_map_.end());
+  unassociated_nodes_.insert(tab_node_id);
   if (max_used_tab_node_id_ < tab_node_id)
     max_used_tab_node_id_ = tab_node_id;
 }
 
-void TabNodePool::AssociateTabNode(int64 sync_id, SessionID::id_type tab_id) {
-  DCHECK_GT(sync_id, 0);
+void TabNodePool::AssociateTabNode(int tab_node_id,
+                                   SessionID::id_type tab_id) {
+  DCHECK_GT(tab_node_id, kInvalidTabNodeID);
   // Remove sync node if it is in unassociated nodes pool.
-  std::set<int64>::iterator u_it = unassociated_nodes_.find(sync_id);
+  std::set<int>::iterator u_it = unassociated_nodes_.find(tab_node_id);
   if (u_it != unassociated_nodes_.end()) {
     unassociated_nodes_.erase(u_it);
   } else {
     // This is a new node association, the sync node should be free.
     // Remove node from free node pool and then associate it with the tab.
-    std::set<int64>::iterator it = free_nodes_pool_.find(sync_id);
+    std::set<int>::iterator it = free_nodes_pool_.find(tab_node_id);
     DCHECK(it != free_nodes_pool_.end());
     free_nodes_pool_.erase(it);
   }
-  DCHECK(syncid_tabid_map_.find(sync_id) == syncid_tabid_map_.end());
-  syncid_tabid_map_[sync_id] = tab_id;
+  DCHECK(nodeid_tabid_map_.find(tab_node_id) == nodeid_tabid_map_.end());
+  nodeid_tabid_map_[tab_node_id] = tab_id;
 }
 
-int64 TabNodePool::GetFreeTabNode() {
+int TabNodePool::GetFreeTabNode() {
   DCHECK_GT(machine_tag_.length(), 0U);
   if (free_nodes_pool_.empty()) {
     // Tab pool has no free nodes, allocate new one.
@@ -72,9 +76,9 @@
     if (root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS)) !=
                              syncer::BaseNode::INIT_OK) {
       LOG(ERROR) << kNoSessionsFolderError;
-      return syncer::kInvalidId;
+      return kInvalidTabNodeID;
     }
-    size_t tab_node_id = ++max_used_tab_node_id_;
+    int tab_node_id = ++max_used_tab_node_id_;
     std::string tab_node_tag = TabIdToTag(machine_tag_, tab_node_id);
     syncer::WriteNode tab_node(&trans);
     syncer::WriteNode::InitUniqueByCreationResult result =
@@ -82,7 +86,7 @@
     if (result != syncer::WriteNode::INIT_SUCCESS) {
       LOG(ERROR) << "Could not create new node with tag "
                  << tab_node_tag << "!";
-      return syncer::kInvalidId;
+      return kInvalidTabNodeID;
     }
     // We fill the new node with just enough data so that in case of a crash/bug
     // we can identify the node as our own on re-association and reuse it.
@@ -93,26 +97,26 @@
     tab_node.SetSessionSpecifics(specifics);
 
     // Grow the pool by 1 since we created a new node.
-    DVLOG(1) << "Adding sync node " << tab_node.GetId()
-             << " to tab syncid pool";
-    free_nodes_pool_.insert(tab_node.GetId());
-    return tab_node.GetId();
+    DVLOG(1) << "Adding sync node " << tab_node_id
+             << " to tab node id pool";
+    free_nodes_pool_.insert(tab_node_id);
+    return tab_node_id;
   } else {
     // Return the next free node.
     return *free_nodes_pool_.begin();
   }
 }
 
-void TabNodePool::FreeTabNode(int64 sync_id) {
-  SyncIDToTabIDMap::iterator it = syncid_tabid_map_.find(sync_id);
-  DCHECK(it != syncid_tabid_map_.end());
-  syncid_tabid_map_.erase(it);
-  FreeTabNodeInternal(sync_id);
+void TabNodePool::FreeTabNode(int tab_node_id) {
+  TabNodeIDToTabIDMap::iterator it = nodeid_tabid_map_.find(tab_node_id);
+  DCHECK(it != nodeid_tabid_map_.end());
+  nodeid_tabid_map_.erase(it);
+  FreeTabNodeInternal(tab_node_id);
 }
 
-void TabNodePool::FreeTabNodeInternal(int64 sync_id) {
-  DCHECK(free_nodes_pool_.find(sync_id) == free_nodes_pool_.end());
-  free_nodes_pool_.insert(sync_id);
+void TabNodePool::FreeTabNodeInternal(int tab_node_id) {
+  DCHECK(free_nodes_pool_.find(tab_node_id) == free_nodes_pool_.end());
+  free_nodes_pool_.insert(tab_node_id);
 
   // If number of free nodes exceed kFreeNodesHighWatermark,
   // delete sync nodes till number reaches kFreeNodesLowWatermark.
@@ -120,11 +124,13 @@
   // clients: http://crbug.com/259918. Newer versions do not need this.
   if (free_nodes_pool_.size() > kFreeNodesHighWatermark) {
     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
-    for (std::set<int64>::iterator free_it = free_nodes_pool_.begin();
+    for (std::set<int>::iterator free_it = free_nodes_pool_.begin();
          free_it != free_nodes_pool_.end();) {
       syncer::WriteNode tab_node(&trans);
-      if (tab_node.InitByIdLookup(*free_it) != syncer::BaseNode::INIT_OK) {
-        LOG(ERROR) << "Could not find sync node with id: " << *free_it;
+      const std::string tab_node_tag = TabIdToTag(machine_tag_, *free_it);
+      if (tab_node.InitByClientTagLookup(syncer::SESSIONS, tab_node_tag) !=
+          syncer::BaseNode::INIT_OK) {
+        LOG(ERROR) << "Could not find sync node with tag: " << tab_node_tag;
         return;
       }
       free_nodes_pool_.erase(free_it++);
@@ -136,32 +142,34 @@
   }
 }
 
-bool TabNodePool::IsUnassociatedTabNode(int64 sync_id) {
-  return unassociated_nodes_.find(sync_id) != unassociated_nodes_.end();
+bool TabNodePool::IsUnassociatedTabNode(int tab_node_id) {
+  return unassociated_nodes_.find(tab_node_id) != unassociated_nodes_.end();
 }
 
-void TabNodePool::ReassociateTabNode(int64 sync_id, SessionID::id_type tab_id) {
+void TabNodePool::ReassociateTabNode(int tab_node_id,
+                                     SessionID::id_type tab_id) {
   // Remove from list of unassociated sync_nodes if present.
-  std::set<int64>::iterator it = unassociated_nodes_.find(sync_id);
+  std::set<int>::iterator it = unassociated_nodes_.find(tab_node_id);
   if (it != unassociated_nodes_.end()) {
     unassociated_nodes_.erase(it);
   } else {
-    // sync_id must be an already associated node.
-    DCHECK(syncid_tabid_map_.find(sync_id) != syncid_tabid_map_.end());
+    // tab_node_id must be an already associated node.
+    DCHECK(nodeid_tabid_map_.find(tab_node_id) != nodeid_tabid_map_.end());
   }
-  syncid_tabid_map_[sync_id] = tab_id;
+  nodeid_tabid_map_[tab_node_id] = tab_id;
 }
 
-SessionID::id_type TabNodePool::GetTabIdFromSyncId(int64 sync_id) const {
-  SyncIDToTabIDMap::const_iterator it = syncid_tabid_map_.find(sync_id);
-  if (it != syncid_tabid_map_.end()) {
+SessionID::id_type TabNodePool::GetTabIdFromTabNodeId(
+    int tab_node_id) const {
+  TabNodeIDToTabIDMap::const_iterator it = nodeid_tabid_map_.find(tab_node_id);
+  if (it != nodeid_tabid_map_.end()) {
     return it->second;
   }
   return kInvalidTabID;
 }
 
 void TabNodePool::FreeUnassociatedTabNodes() {
-  for (std::set<int64>::iterator it = unassociated_nodes_.begin();
+  for (std::set<int>::iterator it = unassociated_nodes_.begin();
        it != unassociated_nodes_.end();) {
     FreeTabNodeInternal(*it);
     unassociated_nodes_.erase(it++);
@@ -173,18 +181,18 @@
 void TabNodePool::Clear() {
   unassociated_nodes_.clear();
   free_nodes_pool_.clear();
-  syncid_tabid_map_.clear();
-  max_used_tab_node_id_ = 0;
+  nodeid_tabid_map_.clear();
+  max_used_tab_node_id_ = kInvalidTabNodeID;
 }
 
 size_t TabNodePool::Capacity() const {
-  return syncid_tabid_map_.size() + unassociated_nodes_.size() +
+  return nodeid_tabid_map_.size() + unassociated_nodes_.size() +
          free_nodes_pool_.size();
 }
 
 bool TabNodePool::Empty() const { return free_nodes_pool_.empty(); }
 
-bool TabNodePool::Full() { return syncid_tabid_map_.empty(); }
+bool TabNodePool::Full() { return nodeid_tabid_map_.empty(); }
 
 void TabNodePool::SetMachineTag(const std::string& machine_tag) {
   machine_tag_ = machine_tag;
diff --git a/chrome/browser/sync/glue/tab_node_pool.h b/chrome/browser/sync/glue/tab_node_pool.h
index 7727ba3..6435175 100644
--- a/chrome/browser/sync/glue/tab_node_pool.h
+++ b/chrome/browser/sync/glue/tab_node_pool.h
@@ -17,18 +17,13 @@
 
 namespace browser_sync {
 
-// A pool for managing free/used tab sync nodes. Performs lazy creation
-// of sync nodes when necessary.
+// A pool for managing free/used tab sync nodes for the *local* session.
+// Performs lazy creation of sync nodes when necessary.
 // Note: We make use of the following "id's"
-// - a sync_id: an int64 used in |syncer::InitByIdLookup|
 // - a tab_id: created by session service, unique to this client
 // - a tab_node_id: the id for a particular sync tab node. This is used
 //   to generate the sync tab node tag through:
 //       tab_tag = StringPrintf("%s_%ui", local_session_tag, tab_node_id);
-// tab_node_id and sync_id are both unique to a particular sync node. The
-// difference is that tab_node_id is controlled by the model associator and
-// is used when creating a new sync node, which returns the sync_id, created
-// by the sync db.
 //
 // A sync node can be in one of the three states:
 // 1. Associated   : Sync node is used and associated with a tab.
@@ -55,42 +50,45 @@
   // Maximum limit of FreeNodes allowed on the client.
   static const size_t kFreeNodesHighWatermark;
 
+  static const int kInvalidTabNodeID;
+
   // Build a sync tag from tab_node_id.
   static std::string TabIdToTag(const std::string machine_tag,
-                                size_t tab_node_id);
+                                int tab_node_id);
 
-  // Returns the sync_id for the next free tab node. If none are available,
+  // Returns the tab_node_id for the next free tab node. If none are available,
   // creates a new tab node and adds it to free nodes pool. The free node can
   // then be used to associate with a tab by calling AssociateTabNode.
   // Note: The node is considered free until it has been associated. Repeated
-  // calls to GetFreeTabNode will return the same sync_id until node has been
+  // calls to GetFreeTabNode will return the same id until node has been
   // associated.
-  int64 GetFreeTabNode();
+  int GetFreeTabNode();
 
-  // Removes association for |sync_id| and returns it to the free node pool.
-  void FreeTabNode(int64 sync_id);
+  // Removes association for |tab_node_id| and returns it to the free node pool.
+  void FreeTabNode(int tab_node_id);
 
-  // Associates |sync_id| with |tab_id|. |sync_id| should either be unassociated
-  // or free. If |sync_id| is free, |sync_id| is removed from the free node pool
-  // In order to associate a non free sync node, use ReassociateTabNode.
-  void AssociateTabNode(int64 sync_id, SessionID::id_type tab_id);
+  // Associates |tab_node_id| with |tab_id|. |tab_node_id| should either be
+  // unassociated or free. If |tab_node_id| is free, |tab_node_id| is removed
+  // from the free node pool In order to associate a non free sync node,
+  // use ReassociateTabNode.
+  void AssociateTabNode(int tab_node_id, SessionID::id_type tab_id);
 
-  // Adds |sync_id| as an unassociated sync node.
+  // Adds |tab_node_id| as an unassociated sync node.
   // Note: this should only be called when we discover tab sync nodes from
   // previous sessions, not for freeing tab nodes we created through
   // GetFreeTabNode (use FreeTabNode below for that).
-  void AddTabNode(int64 sync_id, const SessionID& tab_id, size_t tab_node_id);
+  void AddTabNode(int tab_node_id, const SessionID& tab_id);
 
-  // Returns the tab_id for |sync_id| if it is associated else returns
+  // Returns the tab_id for |tab_node_id| if it is associated else returns
   // kInvalidTabID.
-  SessionID::id_type GetTabIdFromSyncId(int64 sync_id) const;
+  SessionID::id_type GetTabIdFromTabNodeId(int tab_node_id) const;
 
-  // Reassociates |sync_id| with |tab_id|. |sync_id| must be either associated
-  // with a tab or in the set of unassociated nodes.
-  void ReassociateTabNode(int64 sync_id, SessionID::id_type tab_id);
+  // Reassociates |tab_node_id| with |tab_id|. |tab_node_id| must be either
+  // associated with a tab or in the set of unassociated nodes.
+  void ReassociateTabNode(int tab_node_id, SessionID::id_type tab_id);
 
-  // Returns true if |sync_id| is an unassociated tab node.
-  bool IsUnassociatedTabNode(int64 sync_id);
+  // Returns true if |tab_node_id| is an unassociated tab node.
+  bool IsUnassociatedTabNode(int tab_node_id);
 
   // Returns any unassociated nodes to the free node pool.
   void FreeUnassociatedTabNodes();
@@ -112,28 +110,28 @@
 
  private:
   friend class SyncTabNodePoolTest;
-  typedef std::map<int64, SessionID::id_type> SyncIDToTabIDMap;
+  typedef std::map<int, SessionID::id_type> TabNodeIDToTabIDMap;
 
-  // Adds |sync_id| to free node pool.
-  void FreeTabNodeInternal(int64 sync_id);
+  // Adds |tab_node_id| to free node pool.
+  void FreeTabNodeInternal(int tab_node_id);
 
-  // Stores mapping of sync_ids associated with tab_ids, these are the used
+  // Stores mapping of node ids associated with tab_ids, these are the used
   // nodes of tab node pool.
   // The nodes in the map can be returned to free tab node pool by calling
-  // FreeTabNode(sync_id).
-  SyncIDToTabIDMap syncid_tabid_map_;
+  // FreeTabNode(tab_node_id).
+  TabNodeIDToTabIDMap nodeid_tabid_map_;
 
-  // The sync ids for the set of free sync nodes.
-  std::set<int64> free_nodes_pool_;
+  // The node ids for the set of free sync nodes.
+  std::set<int> free_nodes_pool_;
 
-  // The sync ids that are added to pool using AddTabNode and are currently
+  // The node ids that are added to pool using AddTabNode and are currently
   // not associated with any tab. They can be reassociated using
   // ReassociateTabNode.
-  std::set<int64> unassociated_nodes_;
+  std::set<int> unassociated_nodes_;
 
   // The maximum used tab_node id for a sync node. A new sync node will always
   // be created with max_used_tab_node_id_ + 1.
-  size_t max_used_tab_node_id_;
+  int max_used_tab_node_id_;
 
   // The machine tag associated with this tab pool. Used in the title of new
   // sync nodes.
diff --git a/chrome/browser/sync/glue/tab_node_pool_unittest.cc b/chrome/browser/sync/glue/tab_node_pool_unittest.cc
index 4487df6..093e520 100644
--- a/chrome/browser/sync/glue/tab_node_pool_unittest.cc
+++ b/chrome/browser/sync/glue/tab_node_pool_unittest.cc
@@ -12,7 +12,7 @@
  protected:
   SyncTabNodePoolTest() : pool_(NULL) { pool_.SetMachineTag("tag"); }
 
-  size_t GetMaxUsedTabNodeId() const { return pool_.max_used_tab_node_id_; }
+  int GetMaxUsedTabNodeId() const { return pool_.max_used_tab_node_id_; }
 
   TabNodePool pool_;
 };
@@ -23,69 +23,67 @@
   // max_used_tab_node_ always increases.
   SessionID session_id;
   session_id.set_id(1);
-  pool_.AddTabNode(4, session_id, 10);
-  EXPECT_EQ(10u, GetMaxUsedTabNodeId());
+  pool_.AddTabNode(10, session_id);
+  EXPECT_EQ(10, GetMaxUsedTabNodeId());
   session_id.set_id(2);
-  pool_.AddTabNode(5, session_id, 1);
-  EXPECT_EQ(10u, GetMaxUsedTabNodeId());
+  pool_.AddTabNode(1, session_id);
+  EXPECT_EQ(10, GetMaxUsedTabNodeId());
   session_id.set_id(3);
-  pool_.AddTabNode(6, session_id, 1000);
-  EXPECT_EQ(1000u, GetMaxUsedTabNodeId());
-  pool_.ReassociateTabNode(6, 500);
+  pool_.AddTabNode(1000, session_id);
+  EXPECT_EQ(1000, GetMaxUsedTabNodeId());
+  pool_.ReassociateTabNode(1000, 500);
+
   // Freeing a tab node does not change max_used_tab_node_id_.
-  pool_.FreeTabNode(6);
+  pool_.FreeTabNode(1000);
   pool_.FreeUnassociatedTabNodes();
-  EXPECT_EQ(1000u, GetMaxUsedTabNodeId());
+  EXPECT_EQ(1000, GetMaxUsedTabNodeId());
   for (int i = 0; i < 3; ++i) {
     pool_.AssociateTabNode(pool_.GetFreeTabNode(), i + 1);
-    EXPECT_EQ(1000u, GetMaxUsedTabNodeId());
+    EXPECT_EQ(1000, GetMaxUsedTabNodeId());
   }
 
-  EXPECT_EQ(1000u, GetMaxUsedTabNodeId());
+  EXPECT_EQ(1000, GetMaxUsedTabNodeId());
 }
 
 TEST_F(SyncTabNodePoolTest, OldTabNodesAddAndRemove) {
   // VerifyOldTabNodes are added.
-  // sync_id =4, tab_node_id = 1, tab_id = 1
+  // tab_node_id = 1, tab_id = 1
   SessionID session_id;
   session_id.set_id(1);
-  pool_.AddTabNode(4, session_id, 1);
-  // sync_id = 5, tab_node_id = 5, tab_id = 2
+  pool_.AddTabNode(1, session_id);
+  // tab_node_id = 2, tab_id = 2
   session_id.set_id(2);
-  pool_.AddTabNode(5, session_id, 2);
+  pool_.AddTabNode(2, session_id);
   EXPECT_EQ(2u, pool_.Capacity());
   EXPECT_TRUE(pool_.Empty());
-  EXPECT_TRUE(pool_.IsUnassociatedTabNode(4));
-  pool_.ReassociateTabNode(4, 2);
+  EXPECT_TRUE(pool_.IsUnassociatedTabNode(1));
+  pool_.ReassociateTabNode(1, 2);
   EXPECT_TRUE(pool_.Empty());
   // Check FreeUnassociatedTabNodes returns the node to free node pool_.
   pool_.FreeUnassociatedTabNodes();
-  // 5 should be returned to free node pool_.
+  // 2 should be returned to free node pool_.
   EXPECT_EQ(2u, pool_.Capacity());
-  // Should be able to free 4.
-  pool_.FreeTabNode(4);
+  // Should be able to free 1.
+  pool_.FreeTabNode(1);
   EXPECT_FALSE(pool_.Empty());
   EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(4, pool_.GetFreeTabNode());
-  pool_.AssociateTabNode(4, 1);
-  EXPECT_EQ(5, pool_.GetFreeTabNode());
-  pool_.AssociateTabNode(5, 1);
+  EXPECT_EQ(1, pool_.GetFreeTabNode());
+  pool_.AssociateTabNode(1, 1);
+  EXPECT_EQ(2, pool_.GetFreeTabNode());
+  pool_.AssociateTabNode(2, 1);
   EXPECT_TRUE(pool_.Empty());
   EXPECT_FALSE(pool_.Full());
 }
 
 TEST_F(SyncTabNodePoolTest, OldTabNodesReassociation) {
   // VerifyOldTabNodes are reassociated correctly.
-  // sync_id =4, tab_node_id = 1, tab_id = 1
   SessionID session_id;
   session_id.set_id(1);
-  pool_.AddTabNode(4, session_id, 1);
-  // sync_id = 5, tab_node_id = 2, tab_id = 2
+  pool_.AddTabNode(4, session_id);
   session_id.set_id(2);
-  pool_.AddTabNode(5, session_id, 2);
-  // sync_id = 6, tab_node_id = 3, tab_id =3
+  pool_.AddTabNode(5, session_id);
   session_id.set_id(3);
-  pool_.AddTabNode(6, session_id, 3);
+  pool_.AddTabNode(6, session_id);
   EXPECT_EQ(3u, pool_.Capacity());
   EXPECT_TRUE(pool_.Empty());
   EXPECT_TRUE(pool_.IsUnassociatedTabNode(4));
@@ -102,7 +100,7 @@
   // Free all nodes
   pool_.FreeTabNode(4);
   EXPECT_TRUE(pool_.Full());
-  std::set<int64> free_sync_ids;
+  std::set<int> free_sync_ids;
   for (int i = 0; i < 3; ++i) {
     free_sync_ids.insert(pool_.GetFreeTabNode());
     // GetFreeTabNode will return the same value till the node is
@@ -125,9 +123,9 @@
 TEST_F(SyncTabNodePoolTest, AddGet) {
   SessionID session_id;
   session_id.set_id(1);
-  pool_.AddTabNode(5, session_id, 1);
+  pool_.AddTabNode(5, session_id);
   session_id.set_id(2);
-  pool_.AddTabNode(10, session_id, 2);
+  pool_.AddTabNode(10, session_id);
   pool_.FreeUnassociatedTabNodes();
   EXPECT_FALSE(pool_.Empty());
   EXPECT_TRUE(pool_.Full());
@@ -148,9 +146,9 @@
   EXPECT_EQ(0U, pool_.Capacity());
   SessionID session_id;
   session_id.set_id(1);
-  pool_.AddTabNode(5, session_id, 1);
+  pool_.AddTabNode(5, session_id);
   session_id.set_id(2);
-  pool_.AddTabNode(10, session_id, 2);
+  pool_.AddTabNode(10, session_id);
   // Free added nodes.
   pool_.FreeUnassociatedTabNodes();
   EXPECT_FALSE(pool_.Empty());
diff --git a/chrome/browser/sync/glue/typed_url_change_processor.cc b/chrome/browser/sync/glue/typed_url_change_processor.cc
index 1a86800..ca97e32 100644
--- a/chrome/browser/sync/glue/typed_url_change_processor.cc
+++ b/chrome/browser/sync/glue/typed_url_change_processor.cc
@@ -43,7 +43,8 @@
       profile_(profile),
       model_associator_(model_associator),
       history_backend_(history_backend),
-      expected_loop_(base::MessageLoop::current()) {
+      backend_loop_(base::MessageLoop::current()),
+      disconnected_(false) {
   DCHECK(model_associator);
   DCHECK(history_backend);
   DCHECK(error_handler);
@@ -55,14 +56,18 @@
 }
 
 TypedUrlChangeProcessor::~TypedUrlChangeProcessor() {
-  DCHECK(expected_loop_ == base::MessageLoop::current());
+  DCHECK(backend_loop_ == base::MessageLoop::current());
 }
 
 void TypedUrlChangeProcessor::Observe(
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  DCHECK(expected_loop_ == base::MessageLoop::current());
+  DCHECK(backend_loop_ == base::MessageLoop::current());
+
+  base::AutoLock al(disconnect_lock_);
+  if (disconnected_)
+    return;
 
   DVLOG(1) << "Observed typed_url change.";
   if (type == chrome::NOTIFICATION_HISTORY_URLS_MODIFIED) {
@@ -239,7 +244,11 @@
     const syncer::BaseTransaction* trans,
     int64 model_version,
     const syncer::ImmutableChangeRecordList& changes) {
-  DCHECK(expected_loop_ == base::MessageLoop::current());
+  DCHECK(backend_loop_ == base::MessageLoop::current());
+
+  base::AutoLock al(disconnect_lock_);
+  if (disconnected_)
+    return;
 
   syncer::ReadNode typed_url_root(trans);
   if (typed_url_root.InitByTagLookup(kTypedUrlTag) !=
@@ -295,7 +304,11 @@
 }
 
 void TypedUrlChangeProcessor::CommitChangesFromSyncModel() {
-  DCHECK(expected_loop_ == base::MessageLoop::current());
+  DCHECK(backend_loop_ == base::MessageLoop::current());
+
+  base::AutoLock al(disconnect_lock_);
+  if (disconnected_)
+    return;
 
   // Make sure we stop listening for changes while we're modifying the backend,
   // so we don't try to re-apply these changes to the sync DB.
@@ -317,14 +330,23 @@
                            model_associator_->GetErrorPercentage());
 }
 
+void TypedUrlChangeProcessor::Disconnect() {
+  base::AutoLock al(disconnect_lock_);
+  disconnected_ = true;
+}
+
 void TypedUrlChangeProcessor::StartImpl(Profile* profile) {
-  DCHECK(expected_loop_ == base::MessageLoop::current());
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK_EQ(profile, profile_);
-  StartObserving();
+  DCHECK(history_backend_);
+  DCHECK(backend_loop_);
+  backend_loop_->PostTask(FROM_HERE,
+                          base::Bind(&TypedUrlChangeProcessor::StartObserving,
+                                     base::Unretained(this)));
 }
 
 void TypedUrlChangeProcessor::StartObserving() {
-  DCHECK(expected_loop_ == base::MessageLoop::current());
+  DCHECK(backend_loop_ == base::MessageLoop::current());
   DCHECK(profile_);
   notification_registrar_.Add(
       this, chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
@@ -338,7 +360,7 @@
 }
 
 void TypedUrlChangeProcessor::StopObserving() {
-  DCHECK(expected_loop_ == base::MessageLoop::current());
+  DCHECK(backend_loop_ == base::MessageLoop::current());
   DCHECK(profile_);
   notification_registrar_.Remove(
       this, chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
diff --git a/chrome/browser/sync/glue/typed_url_change_processor.h b/chrome/browser/sync/glue/typed_url_change_processor.h
index 823c45c..a5e447e 100644
--- a/chrome/browser/sync/glue/typed_url_change_processor.h
+++ b/chrome/browser/sync/glue/typed_url_change_processor.h
@@ -67,6 +67,9 @@
   // jank.
   virtual void CommitChangesFromSyncModel() OVERRIDE;
 
+  // Stop processing changes and wait for being destroyed.
+  void Disconnect();
+
  protected:
   virtual void StartImpl(Profile* profile) OVERRIDE;
 
@@ -101,11 +104,10 @@
   // WebDataService which is kept alive by our data type controller
   // holding a reference.
   history::HistoryBackend* history_backend_;
+  base::MessageLoop* backend_loop_;
 
   content::NotificationRegistrar notification_registrar_;
 
-  base::MessageLoop* expected_loop_;
-
   scoped_ptr<content::NotificationService> notification_service_;
 
   // The set of pending changes that will be written out on the next
@@ -116,6 +118,9 @@
   TypedUrlModelAssociator::TypedUrlVisitVector pending_new_visits_;
   history::VisitVector pending_deleted_visits_;
 
+  bool disconnected_;
+  base::Lock disconnect_lock_;
+
   DISALLOW_COPY_AND_ASSIGN(TypedUrlChangeProcessor);
 };
 
diff --git a/chrome/browser/sync/glue/typed_url_data_type_controller.cc b/chrome/browser/sync/glue/typed_url_data_type_controller.cc
index 6a5d4a7..fe7d137 100644
--- a/chrome/browser/sync/glue/typed_url_data_type_controller.cc
+++ b/chrome/browser/sync/glue/typed_url_data_type_controller.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/glue/typed_url_change_processor.h"
 #include "chrome/browser/sync/profile_sync_components_factory.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/common/pref_names.h"
@@ -125,23 +126,20 @@
   }
 }
 
-void TypedUrlDataTypeController::CreateSyncComponents() {
+ProfileSyncComponentsFactory::SyncComponents
+TypedUrlDataTypeController::CreateSyncComponents() {
   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK_EQ(state(), ASSOCIATING);
   DCHECK(backend_);
-  ProfileSyncComponentsFactory::SyncComponents sync_components =
-      profile_sync_factory()->CreateTypedUrlSyncComponents(
-          profile_sync_service(),
-          backend_,
-          this);
-  set_model_associator(sync_components.model_associator);
-  set_change_processor(sync_components.change_processor);
+  return profile_sync_factory()->CreateTypedUrlSyncComponents(
+      profile_sync_service(),
+      backend_,
+      this);
 }
 
-void TypedUrlDataTypeController::StopModels() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(state() == STOPPING || state() == NOT_RUNNING || state() == DISABLED);
-  DVLOG(1) << "TypedUrlDataTypeController::StopModels(): State = " << state();
+void TypedUrlDataTypeController::DisconnectProcessor(
+    ChangeProcessor* processor) {
+  static_cast<TypedUrlChangeProcessor*>(processor)->Disconnect();
 }
 
 TypedUrlDataTypeController::~TypedUrlDataTypeController() {}
diff --git a/chrome/browser/sync/glue/typed_url_data_type_controller.h b/chrome/browser/sync/glue/typed_url_data_type_controller.h
index 9afd3e0..95b087b 100644
--- a/chrome/browser/sync/glue/typed_url_data_type_controller.h
+++ b/chrome/browser/sync/glue/typed_url_data_type_controller.h
@@ -44,8 +44,9 @@
   virtual bool PostTaskOnBackendThread(
       const tracked_objects::Location& from_here,
       const base::Closure& task) OVERRIDE;
-  virtual void CreateSyncComponents() OVERRIDE;
-  virtual void StopModels() OVERRIDE;
+  virtual ProfileSyncComponentsFactory::SyncComponents CreateSyncComponents()
+      OVERRIDE;
+  virtual void DisconnectProcessor(ChangeProcessor* processor) OVERRIDE;
 
  private:
   virtual ~TypedUrlDataTypeController();
diff --git a/chrome/browser/sync/glue/typed_url_model_associator.cc b/chrome/browser/sync/glue/typed_url_model_associator.cc
index 89f0e33..b4cabc0 100644
--- a/chrome/browser/sync/glue/typed_url_model_associator.cc
+++ b/chrome/browser/sync/glue/typed_url_model_associator.cc
@@ -65,7 +65,7 @@
     : sync_service_(sync_service),
       history_backend_(history_backend),
       expected_loop_(base::MessageLoop::current()),
-      pending_abort_(false),
+      abort_requested_(false),
       error_handler_(error_handler),
       num_db_accesses_(0),
       num_db_errors_(0) {
@@ -173,43 +173,50 @@
 
 syncer::SyncError TypedUrlModelAssociator::DoAssociateModels() {
   DVLOG(1) << "Associating TypedUrl Models";
-  syncer::SyncError error;
   DCHECK(expected_loop_ == base::MessageLoop::current());
-  if (IsAbortPending())
-    return syncer::SyncError();
+
   history::URLRows typed_urls;
   ++num_db_accesses_;
-  if (!history_backend_->GetAllTypedURLs(&typed_urls)) {
-    ++num_db_errors_;
-    return error_handler_->CreateAndUploadError(
-        FROM_HERE,
-        "Could not get the typed_url entries.",
-        model_type());
-  }
-
-  // Get all the visits.
-  std::map<history::URLID, history::VisitVector> visit_vectors;
-  for (history::URLRows::iterator ix = typed_urls.begin();
-       ix != typed_urls.end();) {
-    if (IsAbortPending())
-      return syncer::SyncError();
-    DCHECK_EQ(0U, visit_vectors.count(ix->id()));
-    if (!FixupURLAndGetVisits(&(*ix), &(visit_vectors[ix->id()])) ||
-        ShouldIgnoreUrl(ix->url()) ||
-        ShouldIgnoreVisits(visit_vectors[ix->id()])) {
-      // Ignore this URL if we couldn't load the visits or if there's some
-      // other problem with it (it was empty, or imported and never visited).
-      ix = typed_urls.erase(ix);
-    } else {
-      ++ix;
-    }
-  }
+  bool query_succeeded =
+      history_backend_ && history_backend_->GetAllTypedURLs(&typed_urls);
 
   history::URLRows new_urls;
   TypedUrlVisitVector new_visits;
   TypedUrlUpdateVector updated_urls;
-
   {
+    base::AutoLock au(abort_lock_);
+    if (abort_requested_) {
+      return syncer::SyncError(FROM_HERE,
+                               syncer::SyncError::DATATYPE_ERROR,
+                               "Association was aborted.",
+                               model_type());
+    }
+
+    // Must lock and check first to make sure |error_handler_| is valid.
+    if (!query_succeeded) {
+      ++num_db_errors_;
+      return error_handler_->CreateAndUploadError(
+          FROM_HERE,
+          "Could not get the typed_url entries.",
+          model_type());
+    }
+
+    // Get all the visits.
+    std::map<history::URLID, history::VisitVector> visit_vectors;
+    for (history::URLRows::iterator ix = typed_urls.begin();
+         ix != typed_urls.end();) {
+      DCHECK_EQ(0U, visit_vectors.count(ix->id()));
+      if (!FixupURLAndGetVisits(&(*ix), &(visit_vectors[ix->id()])) ||
+          ShouldIgnoreUrl(ix->url()) ||
+          ShouldIgnoreVisits(visit_vectors[ix->id()])) {
+        // Ignore this URL if we couldn't load the visits or if there's some
+        // other problem with it (it was empty, or imported and never visited).
+        ix = typed_urls.erase(ix);
+      } else {
+        ++ix;
+      }
+    }
+
     syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
     syncer::ReadNode typed_url_root(&trans);
     if (typed_url_root.InitByTagLookup(kTypedUrlTag) !=
@@ -224,8 +231,6 @@
     std::set<std::string> current_urls;
     for (history::URLRows::iterator ix = typed_urls.begin();
          ix != typed_urls.end(); ++ix) {
-      if (IsAbortPending())
-        return syncer::SyncError();
       std::string tag = ix->url().spec();
       // Empty URLs should be filtered out by ShouldIgnoreUrl() previously.
       DCHECK(!tag.empty());
@@ -311,8 +316,6 @@
     std::vector<int64> obsolete_nodes;
     int64 sync_child_id = typed_url_root.GetFirstChildId();
     while (sync_child_id != syncer::kInvalidId) {
-      if (IsAbortPending())
-        return syncer::SyncError();
       syncer::ReadNode sync_child_node(&trans);
       if (sync_child_node.InitByIdLookup(sync_child_id) !=
               syncer::BaseNode::INIT_OK) {
@@ -372,8 +375,6 @@
       for (std::vector<int64>::const_iterator it = obsolete_nodes.begin();
            it != obsolete_nodes.end();
            ++it) {
-          if (IsAbortPending())
-            return syncer::SyncError();
         syncer::WriteNode sync_node(&trans);
         if (sync_node.InitByIdLookup(*it) != syncer::BaseNode::INIT_OK) {
           return error_handler_->CreateAndUploadError(
@@ -392,7 +393,7 @@
   // to worry about the sync model getting out of sync, because changes are
   // propagated to the ChangeProcessor on this thread.
   WriteToHistoryBackend(&new_urls, &updated_urls, &new_visits, NULL);
-  return error;
+  return syncer::SyncError();
 }
 
 void TypedUrlModelAssociator::UpdateFromSyncDB(
@@ -480,13 +481,8 @@
 }
 
 void TypedUrlModelAssociator::AbortAssociation() {
-  base::AutoLock lock(pending_abort_lock_);
-  pending_abort_ = true;
-}
-
-bool TypedUrlModelAssociator::IsAbortPending() {
-  base::AutoLock lock(pending_abort_lock_);
-  return pending_abort_;
+  base::AutoLock lock(abort_lock_);
+  abort_requested_ = true;
 }
 
 bool TypedUrlModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
diff --git a/chrome/browser/sync/glue/typed_url_model_associator.h b/chrome/browser/sync/glue/typed_url_model_associator.h
index 269ec03..5f9cd38 100644
--- a/chrome/browser/sync/glue/typed_url_model_associator.h
+++ b/chrome/browser/sync/glue/typed_url_model_associator.h
@@ -170,9 +170,6 @@
   bool ShouldIgnoreUrl(const GURL& url);
 
  protected:
-  // Returns true if pending_abort_ is true. Overridable by tests.
-  virtual bool IsAbortPending();
-
   // Helper function that clears our error counters (used to reset stats after
   // model association so we can track model association errors separately).
   // Overridden by tests.
@@ -192,11 +189,8 @@
 
   base::MessageLoop* expected_loop_;
 
-  // Lock to ensure exclusive access to the pending_abort_ flag.
-  base::Lock pending_abort_lock_;
-
-  // Set to true if there's a pending abort.
-  bool pending_abort_;
+  bool abort_requested_;
+  base::Lock abort_lock_;
 
   DataTypeErrorHandler* error_handler_; // Guaranteed to outlive datatypes.
 
diff --git a/chrome/browser/sync/glue/typed_url_model_associator_unittest.cc b/chrome/browser/sync/glue/typed_url_model_associator_unittest.cc
index 39d3de2..c777163 100644
--- a/chrome/browser/sync/glue/typed_url_model_associator_unittest.cc
+++ b/chrome/browser/sync/glue/typed_url_model_associator_unittest.cc
@@ -67,36 +67,27 @@
 
 class TestTypedUrlModelAssociator : public TypedUrlModelAssociator {
  public:
-  TestTypedUrlModelAssociator(base::WaitableEvent* startup,
-                              base::WaitableEvent* aborted)
-      : TypedUrlModelAssociator(&mock_, NULL, NULL),
-        startup_(startup),
-        aborted_(aborted) {}
-  virtual bool IsAbortPending() OVERRIDE {
-    // Let the main thread know that we've been started up, and block until
-    // they've called Abort().
-    startup_->Signal();
-    EXPECT_TRUE(aborted_->TimedWait(TestTimeouts::action_timeout()));
-    return TypedUrlModelAssociator::IsAbortPending();
-  }
+  TestTypedUrlModelAssociator()
+      : TypedUrlModelAssociator(&mock_, NULL, NULL) {}
  private:
   ProfileSyncServiceMock mock_;
-  base::WaitableEvent* startup_;
-  base::WaitableEvent* aborted_;
 };
 
-static void CreateModelAssociator(base::WaitableEvent* startup,
-                                  base::WaitableEvent* aborted,
-                                  base::WaitableEvent* done,
-                                  TypedUrlModelAssociator** associator) {
+static void CreateModelAssociatorAsync(base::WaitableEvent* startup,
+                                       base::WaitableEvent* aborted,
+                                       base::WaitableEvent* done,
+                                       TypedUrlModelAssociator** associator) {
   // Grab the done lock - when we exit, this will be released and allow the
   // test to finish.
-  *associator = new TestTypedUrlModelAssociator(startup, aborted);
-  // AssociateModels should be aborted and should return false.
-  syncer::SyncError error = (*associator)->AssociateModels(NULL, NULL);
+  *associator = new TestTypedUrlModelAssociator();
 
-  // TODO(lipalani): crbug.com/122690 fix this when fixing abort.
-  // EXPECT_TRUE(error.IsSet());
+  // Signal frontend to call AbortAssociation and proceed after it's called.
+  startup->Signal();
+  aborted->Wait();
+  syncer::SyncError error = (*associator)->AssociateModels(NULL, NULL);
+  EXPECT_TRUE(error.IsSet());
+  EXPECT_EQ("datatype error was encountered: Association was aborted.",
+            error.message());
   delete *associator;
   done->Signal();
 }
@@ -433,7 +424,7 @@
   // model association.
   db_thread.Start();
   base::Closure callback = base::Bind(
-      &CreateModelAssociator, &startup, &aborted, &done, &associator);
+      &CreateModelAssociatorAsync, &startup, &aborted, &done, &associator);
   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, callback);
   // Wait for the model associator to get created and start assocation.
   ASSERT_TRUE(startup.TimedWait(TestTimeouts::action_timeout()));
diff --git a/chrome/browser/sync/glue/ui_model_worker.cc b/chrome/browser/sync/glue/ui_model_worker.cc
index c58d358..2d8bc7d 100644
--- a/chrome/browser/sync/glue/ui_model_worker.cc
+++ b/chrome/browser/sync/glue/ui_model_worker.cc
@@ -21,7 +21,6 @@
 // A simple callback to signal a waitable event after running a closure.
 void CallDoWorkAndSignalCallback(const syncer::WorkCallback& work,
                                  base::WaitableEvent* work_done,
-                                 UIModelWorker* const scheduler,
                                  syncer::SyncerError* error_info) {
   if (work.is_null()) {
     // This can happen during tests or cases where there are more than just the
@@ -37,60 +36,23 @@
 
   *error_info = work.Run();
 
-  // Notify the UIModelWorker that scheduled us that we have run
-  // successfully.
-  scheduler->OnTaskCompleted();
   work_done->Signal();  // Unblock the syncer thread that scheduled us.
 }
 
 }  // namespace
 
 UIModelWorker::UIModelWorker(syncer::WorkerLoopDestructionObserver* observer)
-    : syncer::ModelSafeWorker(observer),
-      state_(WORKING),
-      syncapi_has_shutdown_(false),
-      syncapi_event_(&lock_) {
-}
-
-void UIModelWorker::Stop() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  base::AutoLock lock(lock_);
-  DCHECK_EQ(state_, WORKING);
-
-  // We're on our own now, the beloved UI MessageLoop is no longer running.
-  // Any tasks scheduled or to be scheduled on the UI MessageLoop will not run.
-  state_ = RUNNING_MANUAL_SHUTDOWN_PUMP;
-
-  // Drain any final tasks manually until the SyncerThread tells us it has
-  // totally finished. There should only ever be 0 or 1 tasks Run() here.
-  while (!syncapi_has_shutdown_) {
-    if (!pending_work_.is_null())
-      pending_work_.Run();  // OnTaskCompleted will set reset |pending_work_|.
-
-    // http://crbug.com/19757
-    base::ThreadRestrictions::ScopedAllowWait allow_wait;
-    // Wait for either a new task or SyncerThread termination.
-    syncapi_event_.Wait();
-  }
-
-  state_ = STOPPED;
+    : syncer::ModelSafeWorker(observer) {
 }
 
 void UIModelWorker::RegisterForLoopDestruction() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   base::MessageLoop::current()->AddDestructionObserver(this);
+  SetWorkingLoopToCurrent();
 }
 
 syncer::SyncerError UIModelWorker::DoWorkAndWaitUntilDoneImpl(
     const syncer::WorkCallback& work) {
-  // In most cases, this method is called in WORKING state. It is possible this
-  // gets called when we are in the RUNNING_MANUAL_SHUTDOWN_PUMP state, because
-  // the UI loop has initiated shutdown but the syncer hasn't got the memo yet.
-  // This is fine, the work will get scheduled and run normally or run by our
-  // code handling this case in Stop(). Note there _no_ way we can be in here
-  // with state_ = STOPPED, so it is safe to read / compare in this case.
-  CHECK_NE(ANNOTATE_UNPROTECTED_READ(state_), STOPPED);
   syncer::SyncerError error_info;
   if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
     DLOG(WARNING) << "DoWorkAndWaitUntilDone called from "
@@ -98,25 +60,16 @@
     return work.Run();
   }
 
-  {
-    // We lock only to avoid PostTask'ing a NULL pending_work_ (because it
-    // could get Run() in Stop() and call OnTaskCompleted before we post).
-    // The task is owned by the message loop as per usual.
-    base::AutoLock lock(lock_);
-    DCHECK(pending_work_.is_null());
-    pending_work_ = base::Bind(&CallDoWorkAndSignalCallback, work,
-                               work_done_or_stopped(),
-                               base::Unretained(this), &error_info);
-    if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, pending_work_)) {
-      DLOG(WARNING) << "Could not post work to UI loop.";
-      error_info = syncer::CANNOT_DO_WORK;
-      pending_work_.Reset();
-      syncapi_event_.Signal();
-      return error_info;
-    }
+  if (!BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&CallDoWorkAndSignalCallback,
+                 work, work_done_or_stopped(), &error_info))) {
+    DLOG(WARNING) << "Could not post work to UI loop.";
+    error_info = syncer::CANNOT_DO_WORK;
+    return error_info;
   }
-  syncapi_event_.Signal();  // Notify that the syncapi produced work for us.
   work_done_or_stopped()->Wait();
+
   return error_info;
 }
 
@@ -124,21 +77,7 @@
   return syncer::GROUP_UI;
 }
 
-void UIModelWorker::OnSyncerShutdownComplete() {
-  base::AutoLock lock(lock_);
-  // The SyncerThread has terminated and we are no longer needed by syncapi.
-  // The UI loop initiated shutdown and is (or will be) waiting in Stop().
-  // We could either be WORKING or RUNNING_MANUAL_SHUTDOWN_PUMP, depending
-  // on where we timeslice the UI thread in Stop; but we can't be STOPPED,
-  // because that would imply OnSyncerShutdownComplete already signaled.
-  DCHECK_NE(state_, STOPPED);
-
-  syncapi_has_shutdown_ = true;
-  syncapi_event_.Signal();
-}
-
 UIModelWorker::~UIModelWorker() {
-  DCHECK_EQ(state_, STOPPED);
 }
 
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/glue/ui_model_worker.h b/chrome/browser/sync/glue/ui_model_worker.h
index 8f71433..02b8cd3 100644
--- a/chrome/browser/sync/glue/ui_model_worker.h
+++ b/chrome/browser/sync/glue/ui_model_worker.h
@@ -17,86 +17,21 @@
 // A syncer::ModelSafeWorker for UI models (e.g. bookmarks) that
 // accepts work requests from the syncapi that need to be fulfilled
 // from the MessageLoop home to the native model.
-//
-// Lifetime note: Instances of this class will generally be owned by the
-// SyncerThread. When the SyncerThread _object_ is destroyed, the
-// UIModelWorker will be destroyed. The SyncerThread object is destroyed
-// after the actual syncer pthread has exited.
 class UIModelWorker : public syncer::ModelSafeWorker {
  public:
   explicit UIModelWorker(syncer::WorkerLoopDestructionObserver* observer);
 
-  // Called by the UI thread on shutdown of the sync service. Blocks until
-  // the UIModelWorker has safely met termination conditions, namely that
-  // no task scheduled by CallDoWorkFromModelSafeThreadAndWait remains un-
-  // processed and that syncapi will not schedule any further work for us to do.
-  void Stop();
-
   // syncer::ModelSafeWorker implementation. Called on syncapi SyncerThread.
   virtual void RegisterForLoopDestruction() OVERRIDE;
   virtual syncer::ModelSafeGroup GetModelSafeGroup() OVERRIDE;
 
-  // Upon receiving this idempotent call, the syncer::ModelSafeWorker can
-  // assume no work will ever be scheduled again from now on. If it has any work
-  // that it has not yet completed, it must make sure to run it as soon as
-  // possible as the Syncer is trying to shut down. Called from the CoreThread.
-  void OnSyncerShutdownComplete();
-
-  // Callback from |pending_work_| to notify us that it has been run.
-  // Called on ui loop.
-  void OnTaskCompleted() { pending_work_.Reset(); }
-
  protected:
   virtual syncer::SyncerError DoWorkAndWaitUntilDoneImpl(
       const syncer::WorkCallback& work) OVERRIDE;
 
  private:
-  // The life-cycle of a UIModelWorker in three states.
-  enum State {
-    // We hit the ground running in this state and remain until
-    // the UI loop calls Stop().
-    WORKING,
-    // Stop() sequence has been initiated, but we have not received word that
-    // the SyncerThread has terminated and doesn't need us anymore. Since the
-    // UI MessageLoop is not running at this point, we manually process any
-    // last pending_task_ that the Syncer throws at us, effectively dedicating
-    // the UI thread to terminating the Syncer.
-    RUNNING_MANUAL_SHUTDOWN_PUMP,
-    // We have come to a complete stop, no scheduled work remains, and no work
-    // will be scheduled from now until our destruction.
-    STOPPED,
-  };
-
   virtual ~UIModelWorker();
 
-  // This is set by the UI thread, but is not explicitly thread safe, so only
-  // read this value from other threads when you know it is absolutely safe.
-  State state_;
-
-  // We keep a reference to any task we have scheduled so we can gracefully
-  // force them to run if the syncer is trying to shutdown.
-  base::Closure pending_work_;
-
-  // Set by the SyncCoreThread when Syncapi shutdown has completed and the
-  // SyncerThread has terminated, so no more work will be scheduled. Read by
-  // the UI thread in Stop().
-  bool syncapi_has_shutdown_;
-
-  // We use a Lock for all data members and a ConditionVariable to synchronize.
-  // We do this instead of using a WaitableEvent and a bool condition in order
-  // to guard against races that could arise due to the fact that the lack of a
-  // barrier permits instructions to be reordered by compiler optimizations.
-  // Possible or not, that route makes for very fragile code due to existence
-  // of theoretical races.
-  base::Lock lock_;
-
-  // Used as a barrier at shutdown to ensure the SyncerThread terminates before
-  // we allow the UI thread to return from Stop(). This gets signalled whenever
-  // one of two events occur: a new pending_work_ task was scheduled, or the
-  // SyncerThread has terminated. We only care about (1) when we are in Stop(),
-  // because we have to manually Run() the task.
-  base::ConditionVariable syncapi_event_;
-
   DISALLOW_COPY_AND_ASSIGN(UIModelWorker);
 };
 
diff --git a/chrome/browser/sync/glue/ui_model_worker_unittest.cc b/chrome/browser/sync/glue/ui_model_worker_unittest.cc
index dba85e0..4d4ce74 100644
--- a/chrome/browser/sync/glue/ui_model_worker_unittest.cc
+++ b/chrome/browser/sync/glue/ui_model_worker_unittest.cc
@@ -18,8 +18,6 @@
 using syncer::SyncerError;
 using content::BrowserThread;
 
-// Various boilerplate, primarily for the StopWithPendingWork test.
-
 class UIModelWorkerVisitor {
  public:
   UIModelWorkerVisitor(base::WaitableEvent* was_run,
@@ -59,27 +57,6 @@
   DISALLOW_COPY_AND_ASSIGN(Syncer);
 };
 
-// A callback run from the CoreThread to simulate terminating syncapi.
-void FakeSyncapiShutdownCallback(base::Thread* syncer_thread,
-                                 UIModelWorker* worker,
-                                 base::WaitableEvent** jobs,
-                                 size_t job_count) {
-  base::WaitableEvent all_jobs_done(false, false);
-
-  // In real life, we would try and close a sync directory, which would
-  // result in the syncer calling it's own destructor, which results in
-  // the SyncerThread::HaltSyncer being called, which sets the
-  // syncer in RequestEarlyExit mode and waits until the Syncer finishes
-  // SyncShare to remove the syncer from its watch. Here we just manually
-  // wait until all outstanding jobs are done to simulate what happens in
-  // SyncerThread::HaltSyncer.
-  all_jobs_done.WaitMany(jobs, job_count);
-
-  // These two calls are made from SyncBackendHost::Core::DoShutdown.
-  syncer_thread->Stop();
-  worker->OnSyncerShutdownComplete();
-}
-
 class SyncUIModelWorkerTest : public testing::Test {
  public:
   SyncUIModelWorkerTest() : faux_syncer_thread_("FauxSyncerThread"),
@@ -117,96 +94,5 @@
   // We are on the UI thread, so run our loop to process the
   // (hopefully) scheduled task from a SyncShare invocation.
   base::MessageLoop::current()->Run();
-
-  bmw()->OnSyncerShutdownComplete();
-  bmw()->Stop();
   syncer_thread()->Stop();
 }
-
-TEST_F(SyncUIModelWorkerTest, StopWithPendingWork) {
-  // What we want to set up is the following:
-  // ("ui_thread" is the thread we are currently executing on)
-  // 1 - simulate the user shutting down the browser, and the ui thread needing
-  //     to terminate the core thread.
-  // 2 - the core thread is where the syncapi is accessed from, and so it needs
-  //     to shut down the SyncerThread.
-  // 3 - the syncer is waiting on the UIModelWorker to
-  //     perform a task for it.
-  // The UIModelWorker's manual shutdown pump will save the day, as the
-  // UI thread is not actually trying to join() the core thread, it is merely
-  // waiting for the SyncerThread to give it work or to finish. After that, it
-  // will join the core thread which should succeed as the SyncerThread has left
-  // the building. Unfortunately this test as written is not provably decidable,
-  // as it will always halt on success, but it may not on failure (namely if
-  // the task scheduled by the Syncer is _never_ run).
-  core_thread()->Start();
-  base::WaitableEvent v_ran(false, false);
-  scoped_ptr<UIModelWorkerVisitor> v(new UIModelWorkerVisitor(
-       &v_ran, false));
-  base::WaitableEvent* jobs[] = { &v_ran };
-
-  // The current message loop is not running, so queue a task to cause
-  // UIModelWorker::Stop() to play a crucial role. See comment below.
-  syncer_thread()->message_loop()->PostTask(FROM_HERE,
-      base::Bind(&Syncer::SyncShare, base::Unretained(syncer()), v.get()));
-
-  // This is what gets the core_thread blocked on the syncer_thread.
-  core_thread()->message_loop()->PostTask(FROM_HERE,
-      base::Bind(&FakeSyncapiShutdownCallback, syncer_thread(),
-                 base::Unretained(bmw()),
-                 static_cast<base::WaitableEvent**>(jobs), 1));
-
-  // This is what gets the UI thread blocked until NotifyExitRequested,
-  // which is called when FakeSyncapiShutdownCallback runs and deletes the
-  // syncer.
-  bmw()->Stop();
-
-  EXPECT_FALSE(syncer_thread()->IsRunning());
-  core_thread()->Stop();
-}
-
-TEST_F(SyncUIModelWorkerTest, HypotheticalManualPumpFlooding) {
-  // This situation should not happen in real life because the Syncer should
-  // never send more than one CallDoWork notification after early_exit_requested
-  // has been set, but our UIModelWorker is built to handle this case
-  // nonetheless. It may be needed in the future, and since we support it and
-  // it is not actually exercised in the wild this test is essential.
-  // It is identical to above except we schedule more than one visitor.
-  core_thread()->Start();
-
-  // Our ammunition.
-  base::WaitableEvent fox1_ran(false, false);
-  scoped_ptr<UIModelWorkerVisitor> fox1(new UIModelWorkerVisitor(
-      &fox1_ran, false));
-  base::WaitableEvent fox2_ran(false, false);
-  scoped_ptr<UIModelWorkerVisitor> fox2(new UIModelWorkerVisitor(
-      &fox2_ran, false));
-  base::WaitableEvent fox3_ran(false, false);
-  scoped_ptr<UIModelWorkerVisitor> fox3(new UIModelWorkerVisitor(
-      &fox3_ran, false));
-  base::WaitableEvent* jobs[] = { &fox1_ran, &fox2_ran, &fox3_ran };
-
-  // The current message loop is not running, so queue a task to cause
-  // UIModelWorker::Stop() to play a crucial role. See comment below.
-  syncer_thread()->message_loop()->PostTask(FROM_HERE,
-      base::Bind(&Syncer::SyncShare, base::Unretained(syncer()), fox1.get()));
-  syncer_thread()->message_loop()->PostTask(FROM_HERE,
-      base::Bind(&Syncer::SyncShare, base::Unretained(syncer()), fox2.get()));
-
-  // This is what gets the core_thread blocked on the syncer_thread.
-  core_thread()->message_loop()->PostTask(FROM_HERE,
-      base::Bind(&FakeSyncapiShutdownCallback, syncer_thread(),
-                 base::Unretained(bmw()),
-                 static_cast<base::WaitableEvent**>(jobs), 3));
-  syncer_thread()->message_loop()->PostTask(FROM_HERE,
-      base::Bind(&Syncer::SyncShare, base::Unretained(syncer()), fox3.get()));
-
-  // This is what gets the UI thread blocked until NotifyExitRequested,
-  // which is called when FakeSyncapiShutdownCallback runs and deletes the
-  // syncer.
-  bmw()->Stop();
-
-  // Was the thread killed?
-  EXPECT_FALSE(syncer_thread()->IsRunning());
-  core_thread()->Stop();
-}
diff --git a/chrome/browser/sync/profile_sync_components_factory_impl.cc b/chrome/browser/sync/profile_sync_components_factory_impl.cc
index 7d796f0..b637a72 100644
--- a/chrome/browser/sync/profile_sync_components_factory_impl.cc
+++ b/chrome/browser/sync/profile_sync_components_factory_impl.cc
@@ -21,8 +21,6 @@
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
-#include "chrome/browser/spellchecker/spellcheck_factory.h"
-#include "chrome/browser/spellchecker/spellcheck_service.h"
 #include "chrome/browser/sync/glue/autofill_data_type_controller.h"
 #include "chrome/browser/sync/glue/autofill_profile_data_type_controller.h"
 #include "chrome/browser/sync/glue/bookmark_change_processor.h"
@@ -63,9 +61,9 @@
 #include "sync/api/syncable_service.h"
 
 #if defined(ENABLE_MANAGED_USERS)
-#include "chrome/browser/managed_mode/managed_user_registration_service.h"
-#include "chrome/browser/managed_mode/managed_user_registration_service_factory.h"
 #include "chrome/browser/managed_mode/managed_user_service.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service.h"
+#include "chrome/browser/managed_mode/managed_user_sync_service_factory.h"
 #include "chrome/browser/policy/managed_mode_policy_provider.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector_factory.h"
@@ -76,6 +74,11 @@
 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
 #endif
 
+#if defined(ENABLE_SPELLCHECK)
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#endif
+
 using browser_sync::AutofillDataTypeController;
 using browser_sync::AutofillProfileDataTypeController;
 using browser_sync::BookmarkChangeProcessor;
@@ -259,8 +262,6 @@
   }
 
 #if !defined(OS_ANDROID)
-  // Synced Notifications sync datatype is disabled by default.
-  // TODO(petewil): Switch to enabled by default once datatype support is done.
   if (notifier::ChromeNotifierServiceFactory::UseSyncedNotifications(
           command_line_)) {
     pss->RegisterDataTypeController(
@@ -279,7 +280,7 @@
 
 #if defined(ENABLE_MANAGED_USERS)
   if (ManagedUserService::AreManagedUsersEnabled()) {
-    if (ManagedUserService::ProfileIsManaged(profile_)) {
+    if (profile_->IsManaged()) {
       pss->RegisterDataTypeController(
           new UIDataTypeController(
               syncer::MANAGED_USER_SETTINGS, this, profile_, pss));
@@ -379,9 +380,11 @@
           : base::WeakPtr<syncer::SyncableService>();
     }
 #endif
+#if defined(ENABLE_SPELLCHECK)
     case syncer::DICTIONARY:
       return SpellcheckServiceFactory::GetForProfile(profile_)->
           GetCustomDictionary()->AsWeakPtr();
+#endif
     case syncer::FAVICON_IMAGES:
     case syncer::FAVICON_TRACKING: {
       browser_sync::SessionModelAssociator* model_associator =
@@ -396,7 +399,7 @@
       return policy::ProfilePolicyConnectorFactory::GetForProfile(profile_)->
           managed_mode_policy_provider()->AsWeakPtr();
     case syncer::MANAGED_USERS:
-      return ManagedUserRegistrationServiceFactory::GetForProfile(profile_)->
+      return ManagedUserSyncServiceFactory::GetForProfile(profile_)->
           AsWeakPtr();
 #endif
     default:
diff --git a/chrome/browser/sync/profile_sync_components_factory_impl_unittest.cc b/chrome/browser/sync/profile_sync_components_factory_impl_unittest.cc
index cfe58ce..eefd2dd 100644
--- a/chrome/browser/sync/profile_sync_components_factory_impl_unittest.cc
+++ b/chrome/browser/sync/profile_sync_components_factory_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/sync/profile_sync_components_factory_impl.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/chrome_version_info.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -54,7 +55,15 @@
     datatypes.push_back(syncer::TYPED_URLS);
     datatypes.push_back(syncer::FAVICON_TRACKING);
     datatypes.push_back(syncer::FAVICON_IMAGES);
-    return datatypes;
+
+    // If SyncedNotifications are enabled, count them too.
+    chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
+    if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN ||
+        channel == chrome::VersionInfo::CHANNEL_DEV ||
+        channel == chrome::VersionInfo::CHANNEL_CANARY)
+      datatypes.push_back(syncer::SYNCED_NOTIFICATIONS);
+
+  return datatypes;
   }
 
   // Returns the number of default datatypes.
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index c045d86..51f6c0d 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -408,6 +408,20 @@
   return scoped_ptr<browser_sync::DeviceInfo>();
 }
 
+ScopedVector<browser_sync::DeviceInfo>
+    ProfileSyncService::GetAllSignedInDevices() const {
+  ScopedVector<browser_sync::DeviceInfo> devices;
+  if (backend_) {
+    browser_sync::SyncedDeviceTracker* device_tracker =
+        backend_->GetSyncedDeviceTracker();
+    if (device_tracker) {
+      // TODO(lipalani) - Make device tracker return a scoped vector.
+      device_tracker->GetAllSyncedDeviceInfo(&devices);
+    }
+  }
+  return devices.Pass();
+}
+
 void ProfileSyncService::GetDataTypeControllerStates(
   browser_sync::DataTypeController::StateMap* state_map) const {
     for (browser_sync::DataTypeController::TypeMap::const_iterator iter =
@@ -472,18 +486,21 @@
   if (delete_stale_data)
     ClearStaleErrors();
 
-  backend_unrecoverable_error_handler_.reset(
-    new browser_sync::BackendUnrecoverableErrorHandler(
-        MakeWeakHandle(weak_factory_.GetWeakPtr())));
+  scoped_ptr<syncer::UnrecoverableErrorHandler>
+      backend_unrecoverable_error_handler(
+          new browser_sync::BackendUnrecoverableErrorHandler(
+              MakeWeakHandle(weak_factory_.GetWeakPtr())));
 
   backend_->Initialize(
       this,
-      MakeWeakHandle(sync_js_controller_.AsWeakPtr()),
+      sync_thread_.Pass(),
+      GetJsEventHandler(),
       sync_service_url_,
       credentials,
       delete_stale_data,
-      &sync_manager_factory_,
-      backend_unrecoverable_error_handler_.get(),
+      scoped_ptr<syncer::SyncManagerFactory>(
+          new syncer::SyncManagerFactory).Pass(),
+      backend_unrecoverable_error_handler.Pass(),
       &browser_sync::ChromeReportUnrecoverableError);
 }
 
@@ -721,18 +738,21 @@
   if (profile_)
     SigninGlobalError::GetForProfile(profile_)->RemoveProvider(this);
 
-  ShutdownImpl(false);
+  ShutdownImpl(browser_sync::SyncBackendHost::STOP);
+
+  if (sync_thread_)
+    sync_thread_->Stop();
 }
 
-void ProfileSyncService::ShutdownImpl(bool sync_disabled) {
-  // First, we spin down the backend and wait for it to stop syncing completely
-  // before we Stop the data type manager.  This is to avoid a late sync cycle
-  // applying changes to the sync db that wouldn't get applied via
-  // ChangeProcessors, leading to back-from-the-dead bugs.
+void ProfileSyncService::ShutdownImpl(
+    browser_sync::SyncBackendHost::ShutdownOption option) {
+  if (!backend_)
+    return;
+
+  // First, we spin down the backend to stop change processing as soon as
+  // possible.
   base::Time shutdown_start_time = base::Time::Now();
-  if (backend_) {
-    backend_->StopSyncingForShutdown();
-  }
+  backend_->StopSyncingForShutdown();
 
   // Stop all data type controllers, if needed.  Note that until Stop
   // completes, it is possible in theory to have a ChangeProcessor apply a
@@ -758,8 +778,7 @@
   // shutting it down.
   scoped_ptr<SyncBackendHost> doomed_backend(backend_.release());
   if (doomed_backend) {
-    doomed_backend->Shutdown(sync_disabled);
-
+    sync_thread_ = doomed_backend->Shutdown(option);
     doomed_backend.reset();
   }
   base::TimeDelta shutdown_time = base::Time::Now() - shutdown_start_time;
@@ -797,7 +816,7 @@
   // PSS clients don't think we're set up while we're shutting down.
   sync_prefs_.ClearPreferences();
   ClearUnrecoverableError();
-  ShutdownImpl(true);
+  ShutdownImpl(browser_sync::SyncBackendHost::DISABLE_AND_CLAIM_THREAD);
 }
 
 bool ProfileSyncService::HasSyncSetupCompleted() const {
@@ -878,8 +897,11 @@
 
   // Shut all data types down.
   base::MessageLoop::current()->PostTask(FROM_HERE,
-      base::Bind(&ProfileSyncService::ShutdownImpl, weak_factory_.GetWeakPtr(),
-                 delete_sync_database));
+      base::Bind(&ProfileSyncService::ShutdownImpl,
+                 weak_factory_.GetWeakPtr(),
+                 delete_sync_database ?
+                     browser_sync::SyncBackendHost::DISABLE_AND_CLAIM_THREAD :
+                     browser_sync::SyncBackendHost::STOP_AND_CLAIM_THREAD));
 }
 
 // TODO(zea): Move this logic into the DataTypeController/DataTypeManager.
@@ -1242,7 +1264,7 @@
       // Sync disabled by domain admin. we should stop syncing until next
       // restart.
       sync_disabled_by_admin_ = true;
-      ShutdownImpl(true);
+      ShutdownImpl(browser_sync::SyncBackendHost::DISABLE_AND_CLAIM_THREAD);
       break;
     default:
       NOTREACHED();
@@ -1837,11 +1859,7 @@
     return;
   request_access_token_retry_timer_.Stop();
   OAuth2TokenService::ScopeSet oauth2_scopes;
-  bool is_managed = false;
-#if defined(ENABLE_MANAGED_USERS)
-  is_managed = ManagedUserService::ProfileIsManaged(profile_);
-#endif
-  if (is_managed) {
+  if (profile_->IsManaged()) {
     oauth2_scopes.insert(GaiaConstants::kChromeSyncManagedOAuth2Scope);
   } else {
     oauth2_scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
@@ -2021,7 +2039,7 @@
   if (backend_) {
     backend_->UnregisterInvalidationIds();
   }
-  ShutdownImpl(false);
+  ShutdownImpl(browser_sync::SyncBackendHost::STOP_AND_CLAIM_THREAD);
 }
 
 bool ProfileSyncService::IsStartSuppressed() const {
@@ -2077,12 +2095,18 @@
 }
 
 std::string ProfileSyncService::GetEffectiveUsername() {
+  if (profile_->IsManaged()) {
 #if defined(ENABLE_MANAGED_USERS)
-  if (ManagedUserService::ProfileIsManaged(profile_)) {
     DCHECK_EQ(std::string(), signin_->GetAuthenticatedUsername());
     return ManagedUserService::GetManagedUserPseudoEmail();
-  }
+#else
+    NOTREACHED();
 #endif
+  }
 
   return signin_->GetAuthenticatedUsername();
 }
+
+WeakHandle<syncer::JsEventHandler> ProfileSyncService::GetJsEventHandler() {
+  return MakeWeakHandle(sync_js_controller_.AsWeakPtr());
+}
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index 89edb3b..ff1721b 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -14,6 +14,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/location.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/strings/string16.h"
@@ -280,6 +281,10 @@
   virtual scoped_ptr<browser_sync::DeviceInfo> GetDeviceInfo(
       const std::string& client_id) const;
 
+  // Gets the device info for all devices signed into the account associated
+  // with this profile.
+  virtual ScopedVector<browser_sync::DeviceInfo> GetAllSignedInDevices() const;
+
   // Fills state_map with a map of current data types that are possible to
   // sync, as well as their states.
   void GetDataTypeControllerStates(
@@ -632,12 +637,15 @@
   void ConfigureDataTypeManager();
 
   // Shuts down the backend sync components.
-  // |sync_disabled| indicates if syncing is being disabled or not.
-  void ShutdownImpl(bool sync_disabled);
+  // |option| indicates if syncing is being disabled or not, and whether
+  // to claim ownership of sync thread from backend.
+  void ShutdownImpl(browser_sync::SyncBackendHost::ShutdownOption option);
 
   // Return SyncCredentials from the TokenService.
   syncer::SyncCredentials GetCredentials();
 
+  virtual syncer::WeakHandle<syncer::JsEventHandler> GetJsEventHandler();
+
   // Test need to override this to create backends that allow setting up
   // initial conditions, such as populating sync nodes.
   //
@@ -900,9 +908,6 @@
   // or must delay loading for some reason).
   browser_sync::FailedDataTypesHandler failed_data_types_handler_;
 
-  scoped_ptr<browser_sync::BackendUnrecoverableErrorHandler>
-      backend_unrecoverable_error_handler_;
-
   browser_sync::DataTypeManager::ConfigureStatus configure_status_;
 
   // If |true|, there is setup UI visible so we should not start downloading
@@ -912,13 +917,17 @@
   // The set of currently enabled sync experiments.
   syncer::Experiments current_experiments_;
 
-  // Factory the backend will use to build the SyncManager.
-  syncer::SyncManagerFactory sync_manager_factory_;
-
   // Sync's internal debug info listener. Used to record datatype configuration
   // and association information.
   syncer::WeakHandle<syncer::DataTypeDebugInfoListener> debug_info_listener_;
 
+  // A thread where all the sync operations happen.
+  // OWNERSHIP Notes:
+  //     * Created when backend starts for the first time.
+  //     * If sync is disabled, PSS claims ownership from backend.
+  //     * If sync is reenabled, PSS passes ownership to new backend.
+  scoped_ptr<base::Thread> sync_thread_;
+
   // Specifies whenever to use oauth2 access token or ClientLogin token in
   // communications with sync and xmpp servers.
   // TODO(pavely): Remove once android is converted to oauth2 tokens.
diff --git a/chrome/browser/sync/profile_sync_service_harness.cc b/chrome/browser/sync/profile_sync_service_harness.cc
index c89df6d..67aea31 100644
--- a/chrome/browser/sync/profile_sync_service_harness.cc
+++ b/chrome/browser/sync/profile_sync_service_harness.cc
@@ -501,9 +501,14 @@
   bool is_notifiable_commit =
       (snap.model_neutral_state().num_successful_commits > 0);
   if (is_notifiable_commit && p2p_invalidation_service_) {
-    const syncer::ObjectIdInvalidationMap& invalidation_map =
-        ModelTypeInvalidationMapToObjectIdInvalidationMap(
-            snap.source().types);
+    syncer::ModelTypeSet model_types =
+        snap.model_neutral_state().commit_request_types;
+    syncer::ObjectIdSet ids = ModelTypeSetToObjectIdSet(model_types);
+    syncer::ObjectIdInvalidationMap invalidation_map =
+        syncer::ObjectIdSetToInvalidationMap(
+            ids,
+            syncer::Invalidation::kUnknownVersion,
+            "");
     p2p_invalidation_service_->SendInvalidation(invalidation_map);
   }
 
diff --git a/chrome/browser/sync/profile_sync_service_mock.cc b/chrome/browser/sync/profile_sync_service_mock.cc
index b53da40..150273b 100644
--- a/chrome/browser/sync/profile_sync_service_mock.cc
+++ b/chrome/browser/sync/profile_sync_service_mock.cc
@@ -39,3 +39,13 @@
     content::BrowserContext* profile) {
   return new ProfileSyncServiceMock(static_cast<Profile*>(profile));
 }
+
+ScopedVector<browser_sync::DeviceInfo>
+      ProfileSyncServiceMock::GetAllSignedInDevices() const {
+    ScopedVector<browser_sync::DeviceInfo> devices;
+    std::vector<browser_sync::DeviceInfo*>* device_vector =
+        GetAllSignedInDevicesMock();
+    devices.get() = *device_vector;
+    return devices.Pass();
+}
+
diff --git a/chrome/browser/sync/profile_sync_service_mock.h b/chrome/browser/sync/profile_sync_service_mock.h
index ce3aec2..116185a 100644
--- a/chrome/browser/sync/profile_sync_service_mock.h
+++ b/chrome/browser/sync/profile_sync_service_mock.h
@@ -6,11 +6,13 @@
 #define CHROME_BROWSER_SYNC_PROFILE_SYNC_SERVICE_MOCK_H_
 
 #include <string>
+#include <vector>
 
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/sync/glue/change_processor.h"
 #include "chrome/browser/sync/glue/data_type_controller.h"
+#include "chrome/browser/sync/glue/device_info.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/test/base/testing_profile.h"
 #include "google_apis/gaia/google_service_auth_error.h"
@@ -94,6 +96,12 @@
       const syncer::SyncProtocolError&));
   MOCK_METHOD1(SetSetupInProgress, void(bool));
 
+  MOCK_CONST_METHOD0(GetAllSignedInDevicesMock,
+                     std::vector<browser_sync::DeviceInfo*>* ());
+  // This is to get around the fact that GMOCK does not handle Scoped*.
+  virtual ScopedVector<browser_sync::DeviceInfo>
+      GetAllSignedInDevices() const OVERRIDE;
+
   // DataTypeManagerObserver mocks.
   MOCK_METHOD0(OnConfigureBlocked, void());
   MOCK_METHOD1(OnConfigureDone,
diff --git a/chrome/browser/sync/profile_sync_service_session_unittest.cc b/chrome/browser/sync/profile_sync_service_session_unittest.cc
index aec8539..c7aa28f 100644
--- a/chrome/browser/sync/profile_sync_service_session_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_session_unittest.cc
@@ -123,22 +123,6 @@
   }
 }
 
-void BuildTabSpecifics(const std::string& tag, int window_id, int tab_id,
-                       sync_pb::SessionSpecifics* tab_base) {
-  tab_base->set_session_tag(tag);
-  sync_pb::SessionTab* tab = tab_base->mutable_tab();
-  tab->set_tab_id(tab_id);
-  tab->set_tab_visual_index(1);
-  tab->set_current_navigation_index(0);
-  tab->set_pinned(true);
-  tab->set_extension_app_id("app_id");
-  sync_pb::TabNavigation* navigation = tab->add_navigation();
-  navigation->set_virtual_url("http://foo/1");
-  navigation->set_referrer("referrer");
-  navigation->set_title("title");
-  navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED);
-}
-
 // Verifies number of windows, number of tabs, and basic fields.
 void VerifySyncedSession(
     const std::string& tag,
@@ -205,9 +189,27 @@
   ProfileSyncServiceSessionTest()
       : window_bounds_(0, 1, 2, 3),
         notified_of_update_(false),
-        notified_of_refresh_(false) {}
+        notified_of_refresh_(false),
+        max_tab_node_id_(0) {}
   ProfileSyncService* sync_service() { return sync_service_.get(); }
 
+  void BuildTabSpecifics(const std::string& tag, int window_id, int tab_id,
+                         sync_pb::SessionSpecifics* tab_base) {
+    tab_base->set_session_tag(tag);
+    tab_base->set_tab_node_id(++max_tab_node_id_);
+    sync_pb::SessionTab* tab = tab_base->mutable_tab();
+    tab->set_tab_id(tab_id);
+    tab->set_tab_visual_index(1);
+    tab->set_current_navigation_index(0);
+    tab->set_pinned(true);
+    tab->set_extension_app_id("app_id");
+    sync_pb::TabNavigation* navigation = tab->add_navigation();
+    navigation->set_virtual_url("http://foo/1");
+    navigation->set_referrer("referrer");
+    navigation->set_title("title");
+    navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED);
+  }
+
  protected:
   virtual TestingProfile* CreateProfile() OVERRIDE {
     TestingProfile* profile = new TestingProfile();
@@ -246,6 +248,7 @@
   }
 
   virtual void TearDown() {
+    max_tab_node_id_ = 0;
     sync_service_->Shutdown();
     sync_service_.reset();
 
@@ -318,6 +321,7 @@
   bool notified_of_refresh_;
   content::NotificationRegistrar registrar_;
   net::TestURLFetcherFactory fetcher_factory_;
+  int max_tab_node_id_;
 };
 
 class CreateRootHelper {
@@ -408,7 +412,8 @@
 
   // Get the tabs for this machine from the node and check that they were
   // filled.
-  SessionModelAssociator::TabLinksMap tab_map = model_associator_->tab_map_;
+  SessionModelAssociator::TabLinksMap tab_map =
+      model_associator_->local_tab_map_;
   ASSERT_EQ(2U, tab_map.size());
   // Tabs are ordered by sessionid in tab_map, so should be able to traverse
   // the tree based on order of tabs created
@@ -757,27 +762,27 @@
   ASSERT_TRUE(StartSyncService(create_root.callback(), false));
   ASSERT_TRUE(create_root.success());
 
-  std::vector<int64> node_ids;
-  ASSERT_EQ(0U, model_associator_->tab_pool_.Capacity());
-  ASSERT_TRUE(model_associator_->tab_pool_.Empty());
-  ASSERT_TRUE(model_associator_->tab_pool_.Full());
+  std::vector<int> node_ids;
+  ASSERT_EQ(0U, model_associator_->local_tab_pool_.Capacity());
+  ASSERT_TRUE(model_associator_->local_tab_pool_.Empty());
+  ASSERT_TRUE(model_associator_->local_tab_pool_.Full());
   const size_t num_ids = 10;
   for (size_t i = 0; i < num_ids; ++i) {
-    int64 id = model_associator_->tab_pool_.GetFreeTabNode();
-    ASSERT_GT(id, -1);
+    int id = model_associator_->local_tab_pool_.GetFreeTabNode();
+    ASSERT_GT(id, TabNodePool::kInvalidTabNodeID);
     node_ids.push_back(id);
     // Associate with a tab node.
-    model_associator_->tab_pool_.AssociateTabNode(id, i + 1);
+    model_associator_->local_tab_pool_.AssociateTabNode(id, i + 1);
   }
-  ASSERT_EQ(num_ids, model_associator_->tab_pool_.Capacity());
-  ASSERT_TRUE(model_associator_->tab_pool_.Empty());
-  ASSERT_FALSE(model_associator_->tab_pool_.Full());
+  ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity());
+  ASSERT_TRUE(model_associator_->local_tab_pool_.Empty());
+  ASSERT_FALSE(model_associator_->local_tab_pool_.Full());
   for (size_t i = 0; i < num_ids; ++i) {
-    model_associator_->tab_pool_.FreeTabNode(node_ids[i]);
+    model_associator_->local_tab_pool_.FreeTabNode(node_ids[i]);
   }
-  ASSERT_EQ(num_ids, model_associator_->tab_pool_.Capacity());
-  ASSERT_FALSE(model_associator_->tab_pool_.Empty());
-  ASSERT_TRUE(model_associator_->tab_pool_.Full());
+  ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity());
+  ASSERT_FALSE(model_associator_->local_tab_pool_.Empty());
+  ASSERT_TRUE(model_associator_->local_tab_pool_.Full());
 }
 
 // TODO(jhorwich): Re-enable when crbug.com/121487 addressed
@@ -790,31 +795,31 @@
   SessionID session_id;
   for (size_t i = 0; i < num_starting_nodes; ++i) {
     session_id.set_id(i + 1);
-    model_associator_->tab_pool_.AddTabNode(i + 1, session_id, i);
+    model_associator_->local_tab_pool_.AddTabNode(i + 1, session_id);
   }
 
-  model_associator_->tab_pool_.FreeUnassociatedTabNodes();
-  std::vector<int64> node_ids;
-  ASSERT_EQ(num_starting_nodes, model_associator_->tab_pool_.Capacity());
-  ASSERT_FALSE(model_associator_->tab_pool_.Empty());
-  ASSERT_TRUE(model_associator_->tab_pool_.Full());
+  model_associator_->local_tab_pool_.FreeUnassociatedTabNodes();
+  std::vector<int> node_ids;
+  ASSERT_EQ(num_starting_nodes, model_associator_->local_tab_pool_.Capacity());
+  ASSERT_FALSE(model_associator_->local_tab_pool_.Empty());
+  ASSERT_TRUE(model_associator_->local_tab_pool_.Full());
   const size_t num_ids = 10;
   for (size_t i = 0; i < num_ids; ++i) {
-    int64 id = model_associator_->tab_pool_.GetFreeTabNode();
-    ASSERT_GT(id, -1);
+    int id = model_associator_->local_tab_pool_.GetFreeTabNode();
+    ASSERT_GT(id, TabNodePool::kInvalidTabNodeID);
     node_ids.push_back(id);
     // Associate with a tab node.
-    model_associator_->tab_pool_.AssociateTabNode(id, i + 1);
+    model_associator_->local_tab_pool_.AssociateTabNode(id, i + 1);
   }
-  ASSERT_EQ(num_ids, model_associator_->tab_pool_.Capacity());
-  ASSERT_TRUE(model_associator_->tab_pool_.Empty());
-  ASSERT_FALSE(model_associator_->tab_pool_.Full());
+  ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity());
+  ASSERT_TRUE(model_associator_->local_tab_pool_.Empty());
+  ASSERT_FALSE(model_associator_->local_tab_pool_.Full());
   for (size_t i = 0; i < num_ids; ++i) {
-    model_associator_->tab_pool_.FreeTabNode(node_ids[i]);
+    model_associator_->local_tab_pool_.FreeTabNode(node_ids[i]);
   }
-  ASSERT_EQ(num_ids, model_associator_->tab_pool_.Capacity());
-  ASSERT_FALSE(model_associator_->tab_pool_.Empty());
-  ASSERT_TRUE(model_associator_->tab_pool_.Full());
+  ASSERT_EQ(num_ids, model_associator_->local_tab_pool_.Capacity());
+  ASSERT_FALSE(model_associator_->local_tab_pool_.Empty());
+  ASSERT_TRUE(model_associator_->local_tab_pool_.Full());
 }
 
 // Write a foreign session to a node, and then delete it.
@@ -1000,7 +1005,8 @@
   // Note: chrome://newtab has special handling which crashes in unit tests.
 
   // Get the tabs for this machine. Only the bla:// url should be synced.
-  SessionModelAssociator::TabLinksMap tab_map = model_associator_->tab_map_;
+  SessionModelAssociator::TabLinksMap tab_map =
+      model_associator_->local_tab_map_;
   ASSERT_EQ(1U, tab_map.size());
   SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin();
   ASSERT_EQ(1, iter->second->tab()->GetEntryCount());
@@ -1054,7 +1060,8 @@
 
   // Get the tabs for this machine from the node and check that they were
   // filled.
-  SessionModelAssociator::TabLinksMap tab_map = model_associator_->tab_map_;
+  SessionModelAssociator::TabLinksMap tab_map =
+      model_associator_->local_tab_map_;
   ASSERT_EQ(2U, tab_map.size());
   // Tabs are ordered by sessionid in tab_map, so should be able to traverse
   // the tree based on order of tabs created
@@ -1333,36 +1340,36 @@
   // kFreeNodesLowWatermark.
 
   SessionID session_id;
-  std::vector<int64> used_sync_ids;
+  std::vector<int> used_sync_ids;
   for (size_t i = 1; i <= TabNodePool::kFreeNodesHighWatermark + 1; ++i) {
     session_id.set_id(i);
-    int64 sync_id = model_associator_->tab_pool_.GetFreeTabNode();
-    model_associator_->tab_pool_.AssociateTabNode(sync_id, i);
+    int sync_id = model_associator_->local_tab_pool_.GetFreeTabNode();
+    model_associator_->local_tab_pool_.AssociateTabNode(sync_id, i);
     used_sync_ids.push_back(sync_id);
   }
 
   // Free all except one node.
-  int64 last_sync_id = used_sync_ids.back();
+  int last_sync_id = used_sync_ids.back();
   used_sync_ids.pop_back();
 
   for (size_t i = 0; i < used_sync_ids.size(); ++i) {
-    model_associator_->tab_pool_.FreeTabNode(used_sync_ids[i]);
+    model_associator_->local_tab_pool_.FreeTabNode(used_sync_ids[i]);
   }
 
   // Except one node all nodes should be in FreeNode pool.
-  EXPECT_FALSE(model_associator_->tab_pool_.Full());
-  EXPECT_FALSE(model_associator_->tab_pool_.Empty());
+  EXPECT_FALSE(model_associator_->local_tab_pool_.Full());
+  EXPECT_FALSE(model_associator_->local_tab_pool_.Empty());
   // Total capacity = 1 Associated Node + kFreeNodesHighWatermark free node.
   EXPECT_EQ(TabNodePool::kFreeNodesHighWatermark + 1,
-            model_associator_->tab_pool_.Capacity());
+            model_associator_->local_tab_pool_.Capacity());
 
   // Freeing the last sync node should drop the free nodes to
   // kFreeNodesLowWatermark.
-  model_associator_->tab_pool_.FreeTabNode(last_sync_id);
-  EXPECT_FALSE(model_associator_->tab_pool_.Empty());
-  EXPECT_TRUE(model_associator_->tab_pool_.Full());
+  model_associator_->local_tab_pool_.FreeTabNode(last_sync_id);
+  EXPECT_FALSE(model_associator_->local_tab_pool_.Empty());
+  EXPECT_TRUE(model_associator_->local_tab_pool_.Full());
   EXPECT_EQ(TabNodePool::kFreeNodesLowWatermark,
-            model_associator_->tab_pool_.Capacity());
+            model_associator_->local_tab_pool_.Capacity());
 }
 
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
index 2738c10..23c6edd 100644
--- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
@@ -97,6 +97,11 @@
   explicit HistoryServiceMock(Profile* profile) : HistoryService(profile) {}
   MOCK_METHOD2(ScheduleDBTask, void(history::HistoryDBTask*,
                                     CancelableRequestConsumerBase*));
+  MOCK_METHOD0(Shutdown, void());
+
+  void ShutdownBaseService() {
+    HistoryService::Shutdown();
+  }
 
  private:
   virtual ~HistoryServiceMock() {}
@@ -135,6 +140,11 @@
                             task));
 }
 
+ACTION_P2(ShutdownHistoryService, thread, service) {
+  service->ShutdownBaseService();
+  delete thread;
+}
+
 ACTION_P6(MakeTypedUrlSyncComponents,
               profile,
               service,
@@ -168,8 +178,8 @@
   }
 
  protected:
-  ProfileSyncServiceTypedUrlTest()
-      : history_thread_("history") {
+  ProfileSyncServiceTypedUrlTest() {
+    history_thread_.reset(new Thread("history"));
   }
 
   virtual void SetUp() {
@@ -182,17 +192,15 @@
         HistoryServiceFactory::GetInstance()->SetTestingFactoryAndUse(
             profile_.get(), BuildHistoryService));
     EXPECT_CALL((*history_service_), ScheduleDBTask(_, _))
-        .WillRepeatedly(RunTaskOnDBThread(&history_thread_,
+        .WillRepeatedly(RunTaskOnDBThread(history_thread_.get(),
                                           history_backend_.get()));
-    history_thread_.Start();
+    history_thread_->Start();
   }
 
   virtual void TearDown() {
-    history_backend_ = NULL;
-    history_service_ = NULL;
-    ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(
-        profile_.get(), NULL);
-    history_thread_.Stop();
+    EXPECT_CALL((*history_service_), Shutdown())
+        .WillOnce(ShutdownHistoryService(history_thread_.release(),
+                                         history_service_));
     profile_.reset();
     AbstractProfileSyncServiceTest::TearDown();
   }
@@ -312,7 +320,7 @@
     return history_url;
   }
 
-  Thread history_thread_;
+  scoped_ptr<Thread> history_thread_;
 
   scoped_ptr<ProfileMock> profile_;
   scoped_refptr<HistoryBackendMock> history_backend_;
@@ -562,7 +570,8 @@
 
   history::URLsModifiedDetails details;
   details.changed_urls.push_back(added_entry);
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLsModifiedDetails>(&details));
@@ -592,7 +601,8 @@
   history::URLsModifiedDetails details;
   details.changed_urls.push_back(empty_entry);
   details.changed_urls.push_back(added_entry);
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLsModifiedDetails>(&details));
@@ -629,7 +639,8 @@
 
   history::URLsModifiedDetails details;
   details.changed_urls.push_back(updated_entry);
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLsModifiedDetails>(&details));
@@ -657,7 +668,8 @@
   history::URLVisitedDetails details;
   details.row = added_entry;
   details.transition = content::PAGE_TRANSITION_TYPED;
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLVisitedDetails>(&details));
@@ -695,7 +707,8 @@
   history::URLVisitedDetails details;
   details.row = updated_entry;
   details.transition = content::PAGE_TRANSITION_TYPED;
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLVisitedDetails>(&details));
@@ -735,7 +748,8 @@
 
   // Should ignore this change because it's not TYPED.
   details.transition = content::PAGE_TRANSITION_RELOAD;
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URL_VISITED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLVisitedDetails>(&details));
@@ -801,7 +815,8 @@
   history::URLsDeletedDetails changes;
   changes.all_history = false;
   changes.rows.push_back(history::URLRow(GURL("http://mine.com")));
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLsDeletedDetails>(&changes));
@@ -839,7 +854,8 @@
   // Setting archived=true should cause the sync code to ignore this deletion.
   changes.archived = true;
   changes.rows.push_back(history::URLRow(GURL("http://mine.com")));
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLsDeletedDetails>(&changes));
@@ -878,7 +894,8 @@
 
   history::URLsDeletedDetails changes;
   changes.all_history = true;
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLsDeletedDetails>(&changes));
@@ -957,6 +974,7 @@
   // Can't check GetErrorPercentage(), because generating an unrecoverable
   // error will free the model associator.
 }
+
 TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalFileURL) {
   history::VisitVector original_visits;
   // Create http and file url.
@@ -995,7 +1013,8 @@
   details.changed_urls.push_back(updated_url_entry);
   details.changed_urls.push_back(updated_file_entry);
   details.changed_urls.push_back(new_file_entry);
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLsModifiedDetails>(&details));
@@ -1048,7 +1067,8 @@
   details.changed_urls.push_back(updated_url_entry);
   details.changed_urls.push_back(updated_localhost_entry);
   details.changed_urls.push_back(localhost_ip_entry);
-  scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&history_thread_));
+  scoped_refptr<ThreadNotifier> notifier(
+      new ThreadNotifier(history_thread_.get()));
   notifier->Notify(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
                    content::Source<Profile>(profile_.get()),
                    content::Details<history::URLsModifiedDetails>(&details));
diff --git a/chrome/browser/sync/profile_sync_service_unittest.cc b/chrome/browser/sync/profile_sync_service_unittest.cc
index 9b22da9..ff213c2 100644
--- a/chrome/browser/sync/profile_sync_service_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_unittest.cc
@@ -46,6 +46,10 @@
 using testing::Return;
 using testing::StrictMock;
 
+void SignalDone(base::WaitableEvent* done) {
+  done->Signal();
+}
+
 class ProfileSyncServiceTestHarness {
  public:
   ProfileSyncServiceTestHarness()
@@ -120,6 +124,21 @@
     }
   }
 
+  void WaitForBackendInitDone() {
+    for (int i = 0; i < 5; ++i) {
+      base::WaitableEvent done(false, false);
+      service->GetBackendForTest()->GetSyncLoopForTesting()
+          ->PostTask(FROM_HERE,
+                     base::Bind(&SignalDone, &done));
+      done.Wait();
+      base::RunLoop().RunUntilIdle();
+      if (service->sync_initialized()) {
+        return;
+      }
+    }
+    LOG(ERROR) << "Backend not initialized.";
+  }
+
   void IssueTestTokens() {
     TokenService* token_service =
         TokenServiceFactory::GetForProfile(profile.get());
@@ -338,6 +357,7 @@
 
 TEST_F(ProfileSyncServiceTest, JsControllerProcessJsMessageBasic) {
   harness_.StartSyncService();
+  harness_.WaitForBackendInitDone();
 
   StrictMock<syncer::MockJsReplyHandler> reply_handler;
 
@@ -355,6 +375,11 @@
   }
 
   // This forces the sync thread to process the message and reply.
+  base::WaitableEvent done(false, false);
+  harness_.service->GetBackendForTest()->GetSyncLoopForTesting()
+      ->PostTask(FROM_HERE,
+                 base::Bind(&SignalDone, &done));
+  done.Wait();
   harness_.TearDown();
 }
 
@@ -379,9 +404,14 @@
   }
 
   harness_.IssueTestTokens();
+  harness_.WaitForBackendInitDone();
 
   // This forces the sync thread to process the message and reply.
-  harness_.TearDown();
+  base::WaitableEvent done(false, false);
+  harness_.service->GetBackendForTest()->GetSyncLoopForTesting()
+      ->PostTask(FROM_HERE,
+                 base::Bind(&SignalDone, &done));
+  done.Wait();  harness_.TearDown();
 }
 
 // Make sure that things still work if sync is not enabled, but some old sync
diff --git a/chrome/browser/sync/test/integration/enable_disable_test.cc b/chrome/browser/sync/test/integration/enable_disable_test.cc
index 45a2cea..7fecc87 100644
--- a/chrome/browser/sync/test/integration/enable_disable_test.cc
+++ b/chrome/browser/sync/test/integration/enable_disable_test.cc
@@ -111,7 +111,8 @@
   for (syncer::ModelTypeSet::Iterator it = registered_types.First();
        it.Good(); it.Inc()) {
     // MANAGED_USERS is always synced.
-    if (it.Get() == syncer::MANAGED_USERS)
+    if (it.Get() == syncer::MANAGED_USERS ||
+        it.Get() == syncer::SYNCED_NOTIFICATIONS)
       continue;
 
     ASSERT_TRUE(GetClient(0)->DisableSyncForDatatype(it.Get()));
diff --git a/chrome/browser/sync/test/integration/sync_extension_helper.cc b/chrome/browser/sync/test/integration/sync_extension_helper.cc
index 62204bf..fba06cd 100644
--- a/chrome/browser/sync/test/integration/sync_extension_helper.cc
+++ b/chrome/browser/sync/test/integration/sync_extension_helper.cc
@@ -69,6 +69,7 @@
       ->OnExtensionInstalled(extension.get(),
                              syncer::StringOrdinal(),
                              false /* no requirement errors */,
+                             extensions::Blacklist::NOT_BLACKLISTED,
                              false /* don't wait for idle to install */);
   return extension->id();
 }
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 061fcad..ca447bc 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -363,6 +363,10 @@
 }
 
 void SyncTest::CleanUpOnMainThread() {
+  for (size_t i = 0; i < clients_.size(); ++i) {
+    clients_[i]->service()->DisableForUser();
+  }
+
   // Some of the pending messages might rely on browser windows still being
   // around, so run messages both before and after closing all browsers.
   content::RunAllPendingInMessageLoop();
diff --git a/chrome/browser/sync/test_profile_sync_service.cc b/chrome/browser/sync/test_profile_sync_service.cc
index 499d2be..bb54416 100644
--- a/chrome/browser/sync/test_profile_sync_service.cc
+++ b/chrome/browser/sync/test_profile_sync_service.cc
@@ -23,7 +23,6 @@
 using syncer::TestInternalComponentsFactory;
 using syncer::sessions::ModelNeutralState;
 using syncer::sessions::SyncSessionSnapshot;
-using syncer::sessions::SyncSourceInfo;
 using syncer::UserShare;
 using syncer::syncable::Directory;
 using syncer::DEVICE_INFO;
@@ -64,26 +63,23 @@
 }  // namespace
 
 void SyncBackendHostForProfileSyncTest::InitCore(
-    const DoInitializeOptions& options) {
-  DoInitializeOptions test_options = options;
-  test_options.make_http_bridge_factory_fn =
+    scoped_ptr<DoInitializeOptions> options) {
+  options->make_http_bridge_factory_fn =
       base::Bind(&MakeTestHttpBridgeFactory);
-  test_options.credentials.email = "testuser@gmail.com";
-  test_options.credentials.sync_token = "token";
-  test_options.restored_key_for_bootstrapping = "";
+  options->credentials.email = "testuser@gmail.com";
+  options->credentials.sync_token = "token";
+  options->restored_key_for_bootstrapping = "";
   syncer::StorageOption storage = storage_option_;
 
   // It'd be nice if we avoided creating the InternalComponentsFactory in the
   // first place, but SyncBackendHost will have created one by now so we must
   // free it. Grab the switches to pass on first.
   InternalComponentsFactory::Switches factory_switches =
-      test_options.internal_components_factory->GetSwitches();
-  delete test_options.internal_components_factory;
+      options->internal_components_factory->GetSwitches();
+  options->internal_components_factory.reset(
+      new TestInternalComponentsFactory(factory_switches, storage));
 
-  test_options.internal_components_factory =
-      new TestInternalComponentsFactory(factory_switches, storage);
-
-  SyncBackendHost::InitCore(test_options);
+  SyncBackendHost::InitCore(options.Pass());
   if (synchronous_init_ && !base::MessageLoop::current()->is_running()) {
     // The SyncBackend posts a task to the current loop when
     // initialization completes.
@@ -204,6 +200,11 @@
       ProfileSyncService::GetBackendForTest());
 }
 
+syncer::WeakHandle<syncer::JsEventHandler>
+TestProfileSyncService::GetJsEventHandler() {
+  return syncer::WeakHandle<syncer::JsEventHandler>();
+}
+
 TestProfileSyncService::TestProfileSyncService(
     ProfileSyncComponentsFactory* factory,
     Profile* profile,
diff --git a/chrome/browser/sync/test_profile_sync_service.h b/chrome/browser/sync/test_profile_sync_service.h
index 3322258..0034429 100644
--- a/chrome/browser/sync/test_profile_sync_service.h
+++ b/chrome/browser/sync/test_profile_sync_service.h
@@ -80,7 +80,7 @@
   static void SetHistoryServiceExpectations(ProfileMock* profile);
 
  protected:
-  virtual void InitCore(const DoInitializeOptions& options) OVERRIDE;
+  virtual void InitCore(scoped_ptr<DoInitializeOptions> options) OVERRIDE;
 
  private:
   void ContinueInitialization(
@@ -174,6 +174,12 @@
  protected:
   virtual void CreateBackend() OVERRIDE;
 
+  // Return NULL handle to use in backend initialization to avoid receiving
+  // js messages on UI loop when it's being destroyed, which are not deleted
+  // and cause memory leak in test.
+  virtual syncer::WeakHandle<syncer::JsEventHandler> GetJsEventHandler()
+      OVERRIDE;
+
  private:
   syncer::TestIdFactory id_factory_;
 
diff --git a/chrome/browser/sync_file_system/drive_backend/api_util.cc b/chrome/browser/sync_file_system/drive_backend/api_util.cc
index f63a86b..5c27aeb 100644
--- a/chrome/browser/sync_file_system/drive_backend/api_util.cc
+++ b/chrome/browser/sync_file_system/drive_backend/api_util.cc
@@ -185,6 +185,7 @@
         content::BrowserThread::GetBlockingPool(),
         GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction),
         GURL(google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction),
+        GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
         std::string() /* custom_user_agent */));
   }
 
diff --git a/chrome/browser/sync_file_system/drive_backend/drive_file_sync_service_sync_unittest.cc b/chrome/browser/sync_file_system/drive_backend/drive_file_sync_service_sync_unittest.cc
index 20c7343..06cf1f1 100644
--- a/chrome/browser/sync_file_system/drive_backend/drive_file_sync_service_sync_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/drive_file_sync_service_sync_unittest.cc
@@ -27,6 +27,7 @@
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/syncable/canned_syncable_file_system.h"
 #include "webkit/browser/fileapi/syncable/local_file_sync_context.h"
+#include "webkit/browser/fileapi/syncable/sync_file_system_backend.h"
 #include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
 
 #define FPL(path) FILE_PATH_LITERAL(path)
@@ -169,7 +170,7 @@
       EXPECT_TRUE(done);
       EXPECT_EQ(SYNC_STATUS_OK, status);
 
-      file_system->file_system_context()->sync_context()->
+      file_system->backend()->sync_context()->
           set_mock_notify_changes_duration_in_sec(0);
 
       EXPECT_EQ(base::PLATFORM_FILE_OK, file_system->OpenFileSystem());
diff --git a/chrome/browser/sync_file_system/local_file_sync_service.cc b/chrome/browser/sync_file_system/local_file_sync_service.cc
index 13cbefb..5754a04 100644
--- a/chrome/browser/sync_file_system/local_file_sync_service.cc
+++ b/chrome/browser/sync_file_system/local_file_sync_service.cc
@@ -21,6 +21,7 @@
 #include "webkit/browser/fileapi/syncable/local_file_change_tracker.h"
 #include "webkit/browser/fileapi/syncable/local_file_sync_context.h"
 #include "webkit/browser/fileapi/syncable/sync_file_metadata.h"
+#include "webkit/browser/fileapi/syncable/sync_file_system_backend.h"
 
 using content::BrowserThread;
 using fileapi::FileSystemURL;
@@ -282,10 +283,12 @@
       continue;
     }
     need_notification = true;
-    fileapi::FileSystemContext* context = origin_to_contexts_[origin];
-    DCHECK(context->change_tracker());
+    SyncFileSystemBackend* backend =
+        SyncFileSystemBackend::GetBackend(origin_to_contexts_[origin]);
+    DCHECK(backend);
+    DCHECK(backend->change_tracker());
     origin_change_map_.SetOriginChangeCount(
-        origin, context->change_tracker()->num_changes());
+        origin, backend->change_tracker()->num_changes());
   }
   if (!need_notification)
     return;
@@ -316,9 +319,12 @@
       pending_origins_with_changes_.end()) {
     // We have remaining changes for the origin.
     pending_origins_with_changes_.erase(app_origin);
-    DCHECK(file_system_context->change_tracker());
+    SyncFileSystemBackend* backend =
+        SyncFileSystemBackend::GetBackend(file_system_context);
+    DCHECK(backend);
+    DCHECK(backend->change_tracker());
     origin_change_map_.SetOriginChangeCount(
-        app_origin, file_system_context->change_tracker()->num_changes());
+        app_origin, backend->change_tracker()->num_changes());
     int64 num_changes = origin_change_map_.GetTotalChangeCount();
     FOR_EACH_OBSERVER(Observer, change_observers_,
                       OnLocalChangeAvailable(num_changes));
diff --git a/chrome/browser/sync_file_system/local_file_sync_service_unittest.cc b/chrome/browser/sync_file_system/local_file_sync_service_unittest.cc
index 9f19923..7ab0ccf 100644
--- a/chrome/browser/sync_file_system/local_file_sync_service_unittest.cc
+++ b/chrome/browser/sync_file_system/local_file_sync_service_unittest.cc
@@ -27,6 +27,7 @@
 #include "webkit/browser/fileapi/syncable/local_file_sync_status.h"
 #include "webkit/browser/fileapi/syncable/mock_sync_status_observer.h"
 #include "webkit/browser/fileapi/syncable/sync_file_metadata.h"
+#include "webkit/browser/fileapi/syncable/sync_file_system_backend.h"
 #include "webkit/browser/fileapi/syncable/sync_status_code.h"
 #include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
 
@@ -127,7 +128,7 @@
 
     EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->OpenFileSystem());
 
-    file_system_->file_system_context()->sync_context()->
+    file_system_->backend()->sync_context()->
         set_mock_notify_changes_duration_in_sec(0);
   }
 
@@ -172,7 +173,7 @@
   }
 
   int64 GetNumChangesInTracker() const {
-    return file_system_->file_system_context()->change_tracker()->num_changes();
+    return file_system_->backend()->change_tracker()->num_changes();
   }
 
   content::TestBrowserThreadBundle thread_bundle_;
@@ -293,7 +294,7 @@
   run_loop.Run();
 
   EXPECT_EQ(base::PLATFORM_FILE_OK, file_system2.OpenFileSystem());
-  file_system2.file_system_context()->sync_context()->
+  file_system2.backend()->sync_context()->
       set_mock_notify_changes_duration_in_sec(0);
 
   const FileSystemURL kFile1(file_system_->URL("file1"));
diff --git a/chrome/browser/sync_file_system/logger.cc b/chrome/browser/sync_file_system/logger.cc
index 7e5e628..4f4bede 100644
--- a/chrome/browser/sync_file_system/logger.cc
+++ b/chrome/browser/sync_file_system/logger.cc
@@ -54,7 +54,9 @@
   // On thread-safety: LazyInstance guarantees thread-safety for the object
   // creation. EventLogger::Log() internally maintains the lock.
   drive::EventLogger* ptr = g_logger.Pointer();
-  ptr->Log("[%s] %s", LogSeverityToString(severity), what.c_str());
+  ptr->Log(severity, base::StringPrintf("[%s] %s",
+                                        LogSeverityToString(severity),
+                                        what.c_str()));
 
   // Log to console if the severity is at or above the min level.
   // LOG_VERBOSE logs are also output if the verbosity of this module
diff --git a/chrome/browser/sync_file_system/sync_file_system_service.cc b/chrome/browser/sync_file_system/sync_file_system_service.cc
index 4d24660..9ae22d8 100644
--- a/chrome/browser/sync_file_system/sync_file_system_service.cc
+++ b/chrome/browser/sync_file_system/sync_file_system_service.cc
@@ -62,25 +62,23 @@
 
 void DidHandleOriginForExtensionUnloadedEvent(
     int type,
-    extension_misc::UnloadedExtensionReason reason,
     const GURL& origin,
     SyncStatusCode code) {
-  DCHECK(chrome::NOTIFICATION_EXTENSION_UNLOADED == type);
-  DCHECK(extension_misc::UNLOAD_REASON_DISABLE == reason ||
-         extension_misc::UNLOAD_REASON_UNINSTALL == reason);
+  DCHECK(chrome::NOTIFICATION_EXTENSION_UNLOADED == type ||
+         chrome::NOTIFICATION_EXTENSION_UNINSTALLED == type);
   if (code != SYNC_STATUS_OK &&
       code != SYNC_STATUS_UNKNOWN_ORIGIN) {
-    switch (reason) {
-      case extension_misc::UNLOAD_REASON_DISABLE:
+    switch (type) {
+      case chrome::NOTIFICATION_EXTENSION_UNLOADED:
         util::Log(logging::LOG_WARNING,
                   FROM_HERE,
-                  "Disabling origin for UNLOAD(DISABLE) failed: %s",
+                  "Disabling origin for UNLOADED(DISABLE) failed: %s",
                   origin.spec().c_str());
         break;
-      case extension_misc::UNLOAD_REASON_UNINSTALL:
+      case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
         util::Log(logging::LOG_WARNING,
                   FROM_HERE,
-                  "Uninstall origin for UNLOAD(UNINSTALL) failed: %s",
+                  "Uninstall origin for UNINSTALLED failed: %s",
                   origin.spec().c_str());
         break;
       default:
@@ -281,6 +279,8 @@
                  content::Source<Profile>(profile_));
   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
                  content::Source<Profile>(profile_));
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
+                 content::Source<Profile>(profile_));
   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED,
                  content::Source<Profile>(profile_));
 }
@@ -561,7 +561,7 @@
   // (User action)    (Notification type)
   // Install:         INSTALLED.
   // Update:          INSTALLED.
-  // Uninstall:       UNLOADED(UNINSTALL).
+  // Uninstall:       UNINSTALLED.
   // Launch, Close:   No notification.
   // Enable:          ENABLED.
   // Disable:         UNLOADED(DISABLE).
@@ -574,6 +574,9 @@
     case chrome::NOTIFICATION_EXTENSION_UNLOADED:
       HandleExtensionUnloaded(type, details);
       break;
+    case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
+      HandleExtensionUninstalled(type, details);
+      break;
     case chrome::NOTIFICATION_EXTENSION_ENABLED:
       HandleExtensionEnabled(type, details);
       break;
@@ -603,30 +606,31 @@
   std::string extension_id = info->extension->id();
   GURL app_origin =
       extensions::Extension::GetBaseURLFromExtensionId(extension_id);
+  if (info->reason != extension_misc::UNLOAD_REASON_DISABLE)
+    return;
+  DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE): "
+           << app_origin;
+  remote_file_service_->DisableOriginForTrackingChanges(
+      app_origin,
+      base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
+                 type, app_origin));
+  local_file_service_->SetOriginEnabled(app_origin, false);
+}
 
-  switch (info->reason) {
-    case extension_misc::UNLOAD_REASON_DISABLE:
-      DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE): "
-               << app_origin;
-      remote_file_service_->DisableOriginForTrackingChanges(
-          app_origin,
-          base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
-                     type, info->reason, app_origin));
-      local_file_service_->SetOriginEnabled(app_origin, false);
-      break;
-    case extension_misc::UNLOAD_REASON_UNINSTALL:
-      DVLOG(1) << "Handle extension notification for UNLOAD(UNINSTALL): "
-               << app_origin;
-      remote_file_service_->UninstallOrigin(
-          app_origin,
-          base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
-                     type, info->reason, app_origin));
-      local_file_service_->SetOriginEnabled(app_origin, false);
-      break;
-    default:
-      // Nothing to do.
-      break;
-  }
+void SyncFileSystemService::HandleExtensionUninstalled(
+    int type,
+    const content::NotificationDetails& details) {
+  std::string extension_id =
+      content::Details<const extensions::Extension>(details)->id();
+  GURL app_origin =
+      extensions::Extension::GetBaseURLFromExtensionId(extension_id);
+  DVLOG(1) << "Handle extension notification for UNINSTALLED: "
+           << app_origin;
+  remote_file_service_->UninstallOrigin(
+      app_origin,
+      base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
+                 type, app_origin));
+  local_file_service_->SetOriginEnabled(app_origin, false);
 }
 
 void SyncFileSystemService::HandleExtensionEnabled(
diff --git a/chrome/browser/sync_file_system/sync_file_system_service.h b/chrome/browser/sync_file_system/sync_file_system_service.h
index 0e015c9..5727dce 100644
--- a/chrome/browser/sync_file_system/sync_file_system_service.h
+++ b/chrome/browser/sync_file_system/sync_file_system_service.h
@@ -135,6 +135,8 @@
   void HandleExtensionInstalled(const content::NotificationDetails& details);
   void HandleExtensionUnloaded(int type,
                                const content::NotificationDetails& details);
+  void HandleExtensionUninstalled(int type,
+                                  const content::NotificationDetails& details);
   void HandleExtensionEnabled(int type,
                               const content::NotificationDetails& details);
 
diff --git a/chrome/browser/sync_file_system/sync_file_system_service_unittest.cc b/chrome/browser/sync_file_system/sync_file_system_service_unittest.cc
index 7e3c543..cd64fea 100644
--- a/chrome/browser/sync_file_system/sync_file_system_service_unittest.cc
+++ b/chrome/browser/sync_file_system/sync_file_system_service_unittest.cc
@@ -25,6 +25,7 @@
 #include "webkit/browser/fileapi/syncable/mock_sync_status_observer.h"
 #include "webkit/browser/fileapi/syncable/sync_callbacks.h"
 #include "webkit/browser/fileapi/syncable/sync_file_metadata.h"
+#include "webkit/browser/fileapi/syncable/sync_file_system_backend.h"
 #include "webkit/browser/fileapi/syncable/sync_status_code.h"
 #include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
 
@@ -300,7 +301,7 @@
   StrictMock<MockSyncStatusObserver> status_observer;
 
   EnableSync();
-  file_system_->file_system_context()->sync_context()->
+  file_system_->backend()->sync_context()->
       set_mock_notify_changes_duration_in_sec(0);
   file_system_->AddSyncStatusObserver(&status_observer);
 
@@ -358,7 +359,7 @@
   InitializeApp();
 
   EnableSync();
-  file_system_->file_system_context()->sync_context()->
+  file_system_->backend()->sync_context()->
       set_mock_notify_changes_duration_in_sec(0);
 
   const FileSystemURL kFile(file_system_->URL("foo"));
diff --git a/chrome/browser/tab_contents/render_view_context_menu.cc b/chrome/browser/tab_contents/render_view_context_menu.cc
index 17bcca6..a788771 100644
--- a/chrome/browser/tab_contents/render_view_context_menu.cc
+++ b/chrome/browser/tab_contents/render_view_context_menu.cc
@@ -16,6 +16,7 @@
 #include "base/prefs/pref_service.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/app/chrome_command_ids.h"
@@ -42,6 +43,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/browser/search/search.h"
+#include "chrome/browser/search_engines/search_terms_data.h"
 #include "chrome/browser/search_engines/template_url.h"
 #include "chrome/browser/search_engines/template_url_service.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -65,6 +67,7 @@
 #include "chrome/common/net/url_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/print_messages.h"
+#include "chrome/common/render_messages.h"
 #include "chrome/common/spellcheck_messages.h"
 #include "chrome/common/url_constants.h"
 #include "content/public/browser/child_process_security_policy.h"
@@ -92,6 +95,8 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/text/text_elider.h"
 #include "ui/gfx/favicon_size.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/size.h"
 
 using WebKit::WebContextMenuData;
 using WebKit::WebMediaPlayerAction;
@@ -115,6 +120,10 @@
 
 namespace {
 
+const int kImageSearchThumbnailMinSize = 300 * 300;
+const int kImageSearchThumbnailMaxWidth = 600;
+const int kImageSearchThumbnailMaxHeight = 600;
+
 // Maps UMA enumeration to IDC. IDC could be changed so we can't use
 // just them and |UMA_HISTOGRAM_CUSTOM_ENUMERATION|.
 // Never change mapping or reuse |enum_id|. Always push back new items.
@@ -181,8 +190,9 @@
   { 54, IDC_SPELLCHECK_MENU },
   { 55, IDC_CONTENT_CONTEXT_SPELLING_TOGGLE },
   { 56, IDC_SPELLCHECK_LANGUAGES_FIRST },
+  { 57, IDC_CONTENT_CONTEXT_SEARCHIMAGENEWTAB },
   // Add new items here and use |enum_id| from the next line.
-  { 57, 0 },  // Must be the last. Increment |enum_id| when new IDC was added.
+  { 58, 0 },  // Must be the last. Increment |enum_id| when new IDC was added.
 };
 
 // Collapses large ranges of ids before looking for UMA enum.
@@ -842,6 +852,20 @@
                                   IDS_CONTENT_CONTEXT_COPYIMAGE);
   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB,
                                   IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB);
+  AppendPrintItem();
+  const TemplateURL* const default_provider =
+      TemplateURLServiceFactory::GetForProfile(profile_)->
+          GetDefaultSearchProvider();
+  if (!default_provider)
+    return;
+  SearchTermsData search_terms;
+  if (!default_provider->image_url().empty() &&
+      default_provider->image_url_ref().IsValidUsingTermsData(search_terms)) {
+    menu_model_.AddItem(
+        IDC_CONTENT_CONTEXT_SEARCHIMAGENEWTAB,
+        l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
+                                   default_provider->short_name()));
+  }
 }
 
 void RenderViewContextMenu::AppendAudioItems() {
@@ -1252,6 +1276,7 @@
     }
 
     case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
+    case IDC_CONTENT_CONTEXT_SEARCHIMAGENEWTAB:
       // The images shown in the most visited thumbnails do not currently open
       // in a new tab as they should. Disabling this context menu option for
       // now, as a quick hack, before we resolve this issue (Issue = 2608).
@@ -1605,6 +1630,10 @@
       CopyImageAt(params_.x, params_.y);
       break;
 
+    case IDC_CONTENT_CONTEXT_SEARCHIMAGENEWTAB:
+      GetImageThumbnailForSearch();
+      break;
+
     case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
     case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
       OpenURL(
@@ -2033,6 +2062,15 @@
   source_web_contents_->GetRenderViewHost()->CopyImageAt(x, y);
 }
 
+void RenderViewContextMenu::GetImageThumbnailForSearch() {
+  source_web_contents_->GetRenderViewHost()->Send(
+       new ChromeViewMsg_RequestThumbnailForContextNode(
+           source_web_contents_->GetRenderViewHost()->GetRoutingID(),
+           kImageSearchThumbnailMinSize,
+           gfx::Size(kImageSearchThumbnailMaxWidth,
+                     kImageSearchThumbnailMaxHeight)));
+}
+
 void RenderViewContextMenu::Inspect(int x, int y) {
   content::RecordAction(UserMetricsAction("DevTools_InspectElement"));
   source_web_contents_->GetRenderViewHostAtPosition(
diff --git a/chrome/browser/tab_contents/render_view_context_menu.h b/chrome/browser/tab_contents/render_view_context_menu.h
index 6f2d0b4..db0bb7e 100644
--- a/chrome/browser/tab_contents/render_view_context_menu.h
+++ b/chrome/browser/tab_contents/render_view_context_menu.h
@@ -237,6 +237,9 @@
   // Copy to the clipboard an image located at a point in the RenderView
   void CopyImageAt(int x, int y);
 
+  // Get an image located at a point in the RenderView for search.
+  void GetImageThumbnailForSearch();
+
   // Launch the inspector targeting a point in the RenderView
   void Inspect(int x, int y);
 
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 22e9e28..08595e7 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/message_loop/message_loop.h"
 #include "base/prefs/pref_service.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_util.h"
@@ -34,7 +35,6 @@
 #endif
 
 #if defined(ENABLE_MANAGED_USERS)
-#include "chrome/browser/managed_mode/managed_user_service.h"
 #include "chrome/browser/managed_mode/managed_user_theme.h"
 #endif
 
@@ -57,6 +57,12 @@
 // unpacked on the filesystem.)
 const char* kDefaultThemeGalleryID = "hkacjpbfdknhflllbcmjibkdeoafencn";
 
+// Wait this many seconds after startup to garbage collect unused themes.
+// Removing unused themes is done after a delay because there is no
+// reason to do it at startup.
+// ExtensionService::GarbageCollectExtensions() does something similar.
+const int kRemoveUnusedThemesStartupDelay = 30;
+
 SkColor TintForUnderline(SkColor input) {
   return SkColorSetA(input, SkColorGetA(input) / 3);
 }
@@ -81,7 +87,9 @@
     : rb_(ResourceBundle::GetSharedInstance()),
       profile_(NULL),
       ready_(false),
-      number_of_infobars_(0) {
+      installed_pending_load_id_(kDefaultThemeID),
+      number_of_infobars_(0),
+      weak_ptr_factory_(this) {
 }
 
 ThemeService::~ThemeService() {
@@ -94,11 +102,9 @@
 
   LoadThemePrefs();
 
-  if (!ready_) {
-    registrar_.Add(this,
-                   chrome::NOTIFICATION_EXTENSIONS_READY,
-                   content::Source<Profile>(profile_));
-  }
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSIONS_READY,
+                 content::Source<Profile>(profile_));
 
   theme_syncable_service_.reset(new ThemeSyncableService(profile_, this));
 }
@@ -204,36 +210,83 @@
 void ThemeService::Observe(int type,
                            const content::NotificationSource& source,
                            const content::NotificationDetails& details) {
-  DCHECK(type == chrome::NOTIFICATION_EXTENSIONS_READY);
-  registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY,
-      content::Source<Profile>(profile_));
-
-  MigrateTheme();
-  set_ready();
-
-  // Send notification in case anyone requested data and cached it when the
-  // theme service was not ready yet.
-  NotifyThemeChanged();
+  using content::Details;
+  switch (type) {
+    case chrome::NOTIFICATION_EXTENSIONS_READY:
+      registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY,
+          content::Source<Profile>(profile_));
+      OnExtensionServiceReady();
+      break;
+    case chrome::NOTIFICATION_EXTENSION_INSTALLED:
+    {
+      // The theme may be initially disabled. Wait till it is loaded (if ever).
+      Details<const extensions::InstalledExtensionInfo> installed_details(
+          details);
+      if (installed_details->extension->is_theme())
+        installed_pending_load_id_ = installed_details->extension->id();
+      break;
+    }
+    case chrome::NOTIFICATION_EXTENSION_LOADED:
+    {
+      const Extension* extension = Details<const Extension>(details).ptr();
+      if (extension->is_theme() &&
+          installed_pending_load_id_ != kDefaultThemeID &&
+          installed_pending_load_id_ == extension->id()) {
+        SetTheme(extension);
+      }
+      installed_pending_load_id_ = kDefaultThemeID;
+      break;
+    }
+    case chrome::NOTIFICATION_EXTENSION_ENABLED:
+    {
+      const Extension* extension = Details<const Extension>(details).ptr();
+      if (extension->is_theme())
+        SetTheme(extension);
+      break;
+    }
+    case chrome::NOTIFICATION_EXTENSION_UNLOADED:
+    {
+      Details<const extensions::UnloadedExtensionInfo> unloaded_details(
+          details);
+      if (unloaded_details->reason != extension_misc::UNLOAD_REASON_UPDATE &&
+          unloaded_details->extension->is_theme() &&
+          unloaded_details->extension->id() == GetThemeID()) {
+        UseDefaultTheme();
+      }
+      break;
+    }
+  }
 }
 
 void ThemeService::SetTheme(const Extension* extension) {
+  DCHECK(extension->is_theme());
+  ExtensionService* service =
+      extensions::ExtensionSystem::Get(profile_)->extension_service();
+  if (!service->IsExtensionEnabled(extension->id())) {
+    // |extension| is disabled when reverting to the previous theme via an
+    // infobar.
+    service->EnableExtension(extension->id());
+    // Enabling the extension will call back to SetTheme().
+    return;
+  }
+
+  std::string previous_theme_id = GetThemeID();
+
   // Clear our image cache.
   FreePlatformCaches();
 
-  DCHECK(extension);
-  DCHECK(extension->is_theme());
-  if (DCHECK_IS_ON()) {
-    ExtensionService* service =
-        extensions::ExtensionSystem::Get(profile_)->extension_service();
-    DCHECK(service);
-    DCHECK(service->GetExtensionById(extension->id(), false));
-  }
-
   BuildFromExtension(extension);
   SaveThemeID(extension->id());
 
   NotifyThemeChanged();
   content::RecordAction(UserMetricsAction("Themes_Installed"));
+
+  if (previous_theme_id != kDefaultThemeID &&
+      previous_theme_id != extension->id()) {
+    // Disable the old theme.
+    service->DisableExtension(previous_theme_id,
+                              extensions::Extension::DISABLE_USER_ACTION);
+  }
 }
 
 void ThemeService::SetCustomDefaultTheme(
@@ -247,25 +300,41 @@
   return false;
 }
 
-void ThemeService::RemoveUnusedThemes() {
+void ThemeService::RemoveUnusedThemes(bool ignore_infobars) {
   // We do not want to garbage collect themes on startup (|ready_| is false).
-  // Themes will get garbage collected once
-  // ExtensionService::GarbageCollectExtensions() runs.
+  // Themes will get garbage collected after |kRemoveUnusedThemesStartupDelay|.
   if (!profile_ || !ready_)
     return;
+  if (!ignore_infobars && number_of_infobars_ != 0)
+    return;
 
   ExtensionService* service = profile_->GetExtensionService();
   if (!service)
     return;
   std::string current_theme = GetThemeID();
   std::vector<std::string> remove_list;
-  const ExtensionSet* extensions = service->extensions();
+  scoped_ptr<const ExtensionSet> extensions(
+      service->GenerateInstalledExtensionsSet());
+  extensions::ExtensionPrefs* prefs = service->extension_prefs();
   for (ExtensionSet::const_iterator it = extensions->begin();
        it != extensions->end(); ++it) {
-    if ((*it)->is_theme() && (*it)->id() != current_theme) {
-      remove_list.push_back((*it)->id());
+    const extensions::Extension* extension = *it;
+    if (extension->is_theme() &&
+        extension->id() != current_theme) {
+      // Only uninstall themes which are not disabled or are disabled with
+      // reason DISABLE_USER_ACTION. We cannot blanket uninstall all disabled
+      // themes because externally installed themes are initially disabled.
+      int disable_reason = prefs->GetDisableReasons(extension->id());
+      if (!prefs->IsExtensionDisabled(extension->id()) ||
+          disable_reason == Extension::DISABLE_USER_ACTION) {
+        remove_list.push_back((*it)->id());
+      }
     }
   }
+  // TODO: Garbage collect all unused themes. This method misses themes which
+  // are installed but not loaded because they are blacklisted by a management
+  // policy provider.
+
   for (size_t i = 0; i < remove_list.size(); ++i)
     service->UninstallExtension(remove_list[i], false, NULL);
 }
@@ -320,7 +389,14 @@
   profile_->GetPrefs()->ClearPref(prefs::kCurrentThemePackFilename);
   SaveThemeID(kDefaultThemeID);
 
-  RemoveUnusedThemes();
+  // There should be no more infobars. This may not be the case because of
+  // http://crbug.com/62154
+  // RemoveUnusedThemes is called on a task because ClearAllThemeData() may
+  // be called as a result of NOTIFICATION_EXTENSION_UNLOADED.
+  base::MessageLoop::current()->PostTask(FROM_HERE,
+      base::Bind(&ThemeService::RemoveUnusedThemes,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 true));
 }
 
 void ThemeService::LoadThemePrefs() {
@@ -351,16 +427,9 @@
   if (loaded_pack) {
     content::RecordAction(UserMetricsAction("Themes.Loaded"));
     set_ready();
-  } else {
-    // TODO(erg): We need to pop up a dialog informing the user that their
-    // theme is being migrated.
-    ExtensionService* service =
-        extensions::ExtensionSystem::Get(profile_)->extension_service();
-    if (service && service->is_ready()) {
-      MigrateTheme();
-      set_ready();
-    }
   }
+  // Else: wait for the extension service to be ready so that the theme pack
+  // can be recreated from the extension.
 }
 
 void ThemeService::NotifyThemeChanged() {
@@ -390,16 +459,41 @@
 }
 #endif
 
-void ThemeService::SwapThemeSupplier(
-    scoped_refptr<CustomThemeSupplier> theme_supplier) {
-  if (theme_supplier_.get())
-    theme_supplier_->StopUsingTheme();
-  theme_supplier_ = theme_supplier;
-  if (theme_supplier_.get())
-    theme_supplier_->StartUsingTheme();
+void ThemeService::OnExtensionServiceReady() {
+  if (!ready_) {
+    // If the ThemeService is not ready yet, the custom theme data pack needs to
+    // be recreated from the extension.
+    MigrateTheme();
+    set_ready();
+
+    // Send notification in case anyone requested data and cached it when the
+    // theme service was not ready yet.
+    NotifyThemeChanged();
+  }
+
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSION_INSTALLED,
+                 content::Source<Profile>(profile_));
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSION_LOADED,
+                 content::Source<Profile>(profile_));
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSION_ENABLED,
+                 content::Source<Profile>(profile_));
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSION_UNLOADED,
+                 content::Source<Profile>(profile_));
+
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&ThemeService::RemoveUnusedThemes,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 false),
+      base::TimeDelta::FromSeconds(kRemoveUnusedThemesStartupDelay));
 }
 
 void ThemeService::MigrateTheme() {
+  // TODO(erg): We need to pop up a dialog informing the user that their
+  // theme is being migrated.
   ExtensionService* service =
       extensions::ExtensionSystem::Get(profile_)->extension_service();
   const Extension* extension = service ?
@@ -415,6 +509,15 @@
   }
 }
 
+void ThemeService::SwapThemeSupplier(
+    scoped_refptr<CustomThemeSupplier> theme_supplier) {
+  if (theme_supplier_.get())
+    theme_supplier_->StopUsingTheme();
+  theme_supplier_ = theme_supplier;
+  if (theme_supplier_.get())
+    theme_supplier_->StartUsingTheme();
+}
+
 void ThemeService::SavePackName(const base::FilePath& pack_path) {
   profile_->GetPrefs()->SetFilePath(
       prefs::kCurrentThemePackFilename, pack_path);
@@ -451,15 +554,14 @@
 }
 
 bool ThemeService::IsManagedUser() const {
-#if defined(ENABLE_MANAGED_USERS)
-  return ManagedUserService::ProfileIsManaged(profile_);
-#endif
-  return false;
+  return profile_->IsManaged();
 }
 
 void ThemeService::SetManagedUserTheme() {
 #if defined(ENABLE_MANAGED_USERS)
   SetCustomDefaultTheme(new ManagedUserTheme);
+#else
+  NOTREACHED();
 #endif
 }
 
@@ -471,7 +573,7 @@
   number_of_infobars_--;
 
   if (number_of_infobars_ == 0)
-    RemoveUnusedThemes();
+    RemoveUnusedThemes(false);
 }
 
 ThemeSyncableService* ThemeService::GetThemeSyncableService() const {
diff --git a/chrome/browser/themes/theme_service.h b/chrome/browser/themes/theme_service.h
index 1452a9a..37e951f 100644
--- a/chrome/browser/themes/theme_service.h
+++ b/chrome/browser/themes/theme_service.h
@@ -13,6 +13,7 @@
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/threading/non_thread_safe.h"
 #include "components/browser_context_keyed_service/browser_context_keyed_service.h"
 #include "content/public/browser/notification_observer.h"
@@ -135,8 +136,10 @@
   // destroyed, uninstalls all themes that aren't the currently selected.
   void OnInfobarDestroyed();
 
-  // Remove preference values for themes that are no longer in use.
-  void RemoveUnusedThemes();
+  // Uninstall theme extensions which are no longer in use. |ignore_infobars| is
+  // whether unused themes should be removed despite a theme infobar being
+  // visible.
+  void RemoveUnusedThemes(bool ignore_infobars);
 
   // Returns the syncable service for syncing theme. The returned service is
   // owned by |this| object.
@@ -185,14 +188,17 @@
  private:
   friend class theme_service_internal::ThemeServiceTest;
 
-  // Replaces the current theme supplier with a new one and calls
-  // StopUsingTheme() or StartUsingTheme() as appropriate.
-  void SwapThemeSupplier(scoped_refptr<CustomThemeSupplier> theme_supplier);
+  // Called when the extension service is ready.
+  void OnExtensionServiceReady();
 
   // Migrate the theme to the new theme pack schema by recreating the data pack
   // from the extension.
   void MigrateTheme();
 
+  // Replaces the current theme supplier with a new one and calls
+  // StopUsingTheme() or StartUsingTheme() as appropriate.
+  void SwapThemeSupplier(scoped_refptr<CustomThemeSupplier> theme_supplier);
+
   // Saves the filename of the cached theme pack.
   void SavePackName(const base::FilePath& pack_path);
 
@@ -240,6 +246,13 @@
 
   scoped_refptr<CustomThemeSupplier> theme_supplier_;
 
+  // The id of the theme extension which has just been installed but has not
+  // been loaded yet. The theme extension with |installed_pending_load_id_| may
+  // never be loaded if the install is due to updating a disabled theme.
+  // |pending_install_id_| should be set to |kDefaultThemeID| if there are no
+  // recently installed theme extensions
+  std::string installed_pending_load_id_;
+
   // The number of infobars currently displayed.
   int number_of_infobars_;
 
@@ -247,6 +260,8 @@
 
   scoped_ptr<ThemeSyncableService> theme_syncable_service_;
 
+  base::WeakPtrFactory<ThemeService> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ThemeService);
 };
 
diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc
index 4168f96..499b2ee 100644
--- a/chrome/browser/themes/theme_service_unittest.cc
+++ b/chrome/browser/themes/theme_service_unittest.cc
@@ -4,14 +4,19 @@
 
 #include "chrome/browser/themes/theme_service.h"
 
-#include "base/json/json_reader.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_service_unittest.h"
+#include "chrome/browser/extensions/unpacked_installer.h"
 #include "chrome/browser/themes/custom_theme_supplier.h"
 #include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace theme_service_internal {
@@ -21,25 +26,57 @@
   ThemeServiceTest() {}
   virtual ~ThemeServiceTest() {}
 
-  scoped_refptr<extensions::Extension> MakeThemeExtension(base::FilePath path) {
-    DictionaryValue source;
-    source.SetString(extension_manifest_keys::kName, "theme");
-    source.Set(extension_manifest_keys::kTheme, new DictionaryValue());
-    source.SetString(extension_manifest_keys::kUpdateURL, "http://foo.com");
-    source.SetString(extension_manifest_keys::kVersion, "0.0.0.0");
-    std::string error;
-    scoped_refptr<extensions::Extension> extension =
-        extensions::Extension::Create(
-            path, extensions::Manifest::EXTERNAL_PREF_DOWNLOAD,
-            source, extensions::Extension::NO_FLAGS, &error);
-    EXPECT_TRUE(extension.get());
-    EXPECT_EQ("", error);
-    return extension;
+  // Moves a minimal theme to |temp_dir_path| and unpacks it from that
+  // directory.
+  std::string LoadUnpackedThemeAt(const base::FilePath& temp_dir) {
+    base::FilePath dst_manifest_path = temp_dir.AppendASCII("manifest.json");
+    base::FilePath test_data_dir;
+    EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
+    base::FilePath src_manifest_path =
+        test_data_dir.AppendASCII("extensions/theme_minimal/manifest.json");
+    EXPECT_TRUE(base::CopyFile(src_manifest_path, dst_manifest_path));
+
+    scoped_refptr<extensions::UnpackedInstaller> installer(
+        extensions::UnpackedInstaller::Create(service_));
+    content::WindowedNotificationObserver observer(
+        chrome::NOTIFICATION_EXTENSION_LOADED,
+        content::Source<Profile>(profile_.get()));
+    installer->Load(temp_dir);
+    observer.Wait();
+
+    std::string extension_id =
+        content::Details<extensions::Extension>(observer.details())->id();
+
+    // Let the ThemeService finish creating the theme pack.
+    base::MessageLoop::current()->RunUntilIdle();
+
+    return extension_id;
+  }
+
+  // Update the theme with |extension_id|.
+  void UpdateUnpackedTheme(const std::string& extension_id) {
+    int updated_notification = service_->IsExtensionEnabled(extension_id) ?
+        chrome::NOTIFICATION_EXTENSION_LOADED :
+        chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED;
+
+    const base::FilePath& path =
+        service_->GetInstalledExtension(extension_id)->path();
+
+    scoped_refptr<extensions::UnpackedInstaller> installer(
+        extensions::UnpackedInstaller::Create(service_));
+    content::WindowedNotificationObserver observer(updated_notification,
+        content::Source<Profile>(profile_.get()));
+    installer->Load(path);
+    observer.Wait();
+
+    // Let the ThemeService finish creating the theme pack.
+    base::MessageLoop::current()->RunUntilIdle();
   }
 
   virtual void SetUp() {
     ExtensionServiceTestBase::SetUp();
     InitializeEmptyExtensionService();
+    service_->Init();
   }
 
   const CustomThemeSupplier* get_theme_supplier(ThemeService* theme_service) {
@@ -50,45 +87,128 @@
 // Installs then uninstalls a theme and makes sure that the ThemeService
 // reverts to the default theme after the uninstall.
 TEST_F(ThemeServiceTest, ThemeInstallUninstall) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
   ThemeService* theme_service =
       ThemeServiceFactory::GetForProfile(profile_.get());
   theme_service->UseDefaultTheme();
-  scoped_refptr<extensions::Extension> extension =
-      MakeThemeExtension(temp_dir.path());
-  service_->FinishInstallationForTest(extension.get());
-  // Let ThemeService finish creating the theme pack.
+  // Let the ThemeService uninstall unused themes.
   base::MessageLoop::current()->RunUntilIdle();
-  EXPECT_FALSE(theme_service->UsingDefaultTheme());
-  EXPECT_EQ(extension->id(), theme_service->GetThemeID());
 
-  // Now unload the extension, should revert to the default theme.
-  service_->UnloadExtension(extension->id(),
-                            extension_misc::UNLOAD_REASON_UNINSTALL);
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  const std::string& extension_id = LoadUnpackedThemeAt(temp_dir.path());
+  EXPECT_FALSE(theme_service->UsingDefaultTheme());
+  EXPECT_EQ(extension_id, theme_service->GetThemeID());
+
+  // Now uninstall the extension, should revert to the default theme.
+  service_->UninstallExtension(extension_id, false, NULL);
   EXPECT_TRUE(theme_service->UsingDefaultTheme());
 }
 
-// Upgrades a theme and ensures that the ThemeService does not revert to the
-// default theme.
-TEST_F(ThemeServiceTest, ThemeUpgrade) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+// Test that a theme extension is disabled when not in use. A theme may be
+// installed but not in use if it there is an infobar to revert to the previous
+// theme.
+TEST_F(ThemeServiceTest, DisableUnusedTheme) {
   ThemeService* theme_service =
       ThemeServiceFactory::GetForProfile(profile_.get());
   theme_service->UseDefaultTheme();
-  scoped_refptr<extensions::Extension> extension =
-      MakeThemeExtension(temp_dir.path());
-  service_->FinishInstallationForTest(extension.get());
-  // Let ThemeService finish creating the theme pack.
+  // Let the ThemeService uninstall unused themes.
   base::MessageLoop::current()->RunUntilIdle();
-  EXPECT_FALSE(theme_service->UsingDefaultTheme());
-  EXPECT_EQ(extension->id(), theme_service->GetThemeID());
 
-  // Now unload the extension, should revert to the default theme.
-  service_->UnloadExtension(extension->id(),
-                            extension_misc::UNLOAD_REASON_UPDATE);
+  base::ScopedTempDir temp_dir1;
+  ASSERT_TRUE(temp_dir1.CreateUniqueTempDir());
+  base::ScopedTempDir temp_dir2;
+  ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
+
+  // 1) Installing a theme should disable the previously active theme.
+  const std::string& extension1_id = LoadUnpackedThemeAt(temp_dir1.path());
   EXPECT_FALSE(theme_service->UsingDefaultTheme());
+  EXPECT_EQ(extension1_id, theme_service->GetThemeID());
+  EXPECT_TRUE(service_->IsExtensionEnabled(extension1_id));
+
+  // Show an infobar to prevent the current theme from being uninstalled.
+  theme_service->OnInfobarDisplayed();
+
+  const std::string& extension2_id = LoadUnpackedThemeAt(temp_dir2.path());
+  EXPECT_EQ(extension2_id, theme_service->GetThemeID());
+  EXPECT_TRUE(service_->IsExtensionEnabled(extension2_id));
+  EXPECT_TRUE(service_->GetExtensionById(extension1_id,
+      ExtensionService::INCLUDE_DISABLED));
+
+  // 2) Enabling a disabled theme extension should swap the current theme.
+  service_->EnableExtension(extension1_id);
+  base::MessageLoop::current()->RunUntilIdle();
+  EXPECT_EQ(extension1_id, theme_service->GetThemeID());
+  EXPECT_TRUE(service_->IsExtensionEnabled(extension1_id));
+  EXPECT_TRUE(service_->GetExtensionById(extension2_id,
+      ExtensionService::INCLUDE_DISABLED));
+
+  // 3) Using SetTheme() with a disabled theme should enable and set the
+  // theme. This is the case when the user reverts to the previous theme
+  // via an infobar.
+  const extensions::Extension* extension2 =
+      service_->GetInstalledExtension(extension2_id);
+  theme_service->SetTheme(extension2);
+  base::MessageLoop::current()->RunUntilIdle();
+  EXPECT_EQ(extension2_id, theme_service->GetThemeID());
+  EXPECT_TRUE(service_->IsExtensionEnabled(extension2_id));
+  EXPECT_TRUE(service_->GetExtensionById(extension1_id,
+      ExtensionService::INCLUDE_DISABLED));
+
+  // 4) Disabling the current theme extension should revert to the default theme
+  // and uninstall any installed theme extensions.
+  theme_service->OnInfobarDestroyed();
+  EXPECT_FALSE(theme_service->UsingDefaultTheme());
+  service_->DisableExtension(extension2_id,
+      extensions::Extension::DISABLE_USER_ACTION);
+  base::MessageLoop::current()->RunUntilIdle();
+  EXPECT_TRUE(theme_service->UsingDefaultTheme());
+  EXPECT_FALSE(service_->GetInstalledExtension(extension1_id));
+  EXPECT_FALSE(service_->GetInstalledExtension(extension2_id));
+}
+
+// Test the ThemeService's behavior when a theme is upgraded.
+TEST_F(ThemeServiceTest, ThemeUpgrade) {
+  // Setup.
+  ThemeService* theme_service =
+      ThemeServiceFactory::GetForProfile(profile_.get());
+  theme_service->UseDefaultTheme();
+  // Let the ThemeService uninstall unused themes.
+  base::MessageLoop::current()->RunUntilIdle();
+
+  theme_service->OnInfobarDisplayed();
+
+  base::ScopedTempDir temp_dir1;
+  ASSERT_TRUE(temp_dir1.CreateUniqueTempDir());
+  base::ScopedTempDir temp_dir2;
+  ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
+
+  const std::string& extension1_id = LoadUnpackedThemeAt(temp_dir1.path());
+  const std::string& extension2_id = LoadUnpackedThemeAt(temp_dir2.path());
+
+  // Test the initial state.
+  EXPECT_TRUE(service_->GetExtensionById(extension1_id,
+      ExtensionService::INCLUDE_DISABLED));
+  EXPECT_EQ(extension2_id, theme_service->GetThemeID());
+
+  // 1) Upgrading the current theme should not revert to the default theme.
+  content::WindowedNotificationObserver theme_change_observer(
+      chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+      content::Source<ThemeService>(theme_service));
+  UpdateUnpackedTheme(extension2_id);
+
+  // The ThemeService should have sent an theme change notification even though
+  // the id of the current theme did not change.
+  theme_change_observer.Wait();
+
+  EXPECT_EQ(extension2_id, theme_service->GetThemeID());
+  EXPECT_TRUE(service_->GetExtensionById(extension1_id,
+      ExtensionService::INCLUDE_DISABLED));
+
+  // 2) Upgrading a disabled theme should not change the current theme.
+  UpdateUnpackedTheme(extension1_id);
+  EXPECT_EQ(extension2_id, theme_service->GetThemeID());
+  EXPECT_TRUE(service_->GetExtensionById(extension1_id,
+      ExtensionService::INCLUDE_DISABLED));
 }
 
 // Checks that managed users have their own default theme.
diff --git a/chrome/browser/themes/theme_syncable_service.cc b/chrome/browser/themes/theme_syncable_service.cc
index 3389443..55d3f74 100644
--- a/chrome/browser/themes/theme_syncable_service.cc
+++ b/chrome/browser/themes/theme_syncable_service.cc
@@ -208,7 +208,7 @@
     string id(theme_specifics.custom_theme_id());
     GURL update_url(theme_specifics.custom_theme_update_url());
     DVLOG(1) << "Applying theme " << id << " with update_url " << update_url;
-    ExtensionServiceInterface* extensions_service =
+    ExtensionService* extensions_service =
         extensions::ExtensionSystem::Get(profile_)->extension_service();
     CHECK(extensions_service);
     const extensions::Extension* extension =
@@ -218,8 +218,12 @@
         DVLOG(1) << "Extension " << id << " is not a theme; aborting";
         return;
       }
-      if (!extensions_service->IsExtensionEnabled(id)) {
-        DVLOG(1) << "Theme " << id << " is not enabled; aborting";
+      int disabled_reasons =
+          extensions_service->extension_prefs()->GetDisableReasons(id);
+      if (!extensions_service->IsExtensionEnabled(id) &&
+          disabled_reasons != extensions::Extension::DISABLE_USER_ACTION) {
+        DVLOG(1) << "Theme " << id << " is disabled with reason "
+                 << disabled_reasons << "; aborting";
         return;
       }
       // An enabled theme extension with the given id was found, so
diff --git a/chrome/browser/translate/options_menu_model.cc b/chrome/browser/translate/options_menu_model.cc
index a6cd71b..86a1cfc 100644
--- a/chrome/browser/translate/options_menu_model.cc
+++ b/chrome/browser/translate/options_menu_model.cc
@@ -106,17 +106,14 @@
 void OptionsMenuModel::ExecuteCommand(int command_id, int event_flags) {
   switch (command_id) {
     case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_LANG:
-      UMA_HISTOGRAM_BOOLEAN("Translate.NeverTranslateLang", true);
       translate_infobar_delegate_->ToggleTranslatableLanguageByPrefs();
       break;
 
     case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_SITE:
-      UMA_HISTOGRAM_BOOLEAN("Translate.NeverTranslateSite", true);
       translate_infobar_delegate_->ToggleSiteBlacklist();
       break;
 
     case IDC_TRANSLATE_OPTIONS_ALWAYS:
-      UMA_HISTOGRAM_BOOLEAN("Translate.AlwaysTranslateLang", true);
       translate_infobar_delegate_->ToggleAlwaysTranslate();
       break;
 
diff --git a/chrome/browser/translate/translate_accept_languages.h b/chrome/browser/translate/translate_accept_languages.h
index 53af9ba..bce859e 100644
--- a/chrome/browser/translate/translate_accept_languages.h
+++ b/chrome/browser/translate/translate_accept_languages.h
@@ -33,8 +33,6 @@
                         const std::string& language);
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(TranslateAcceptLanguages);
-
   // content::NotificationObserver implementation:
   virtual void Observe(int type,
                        const content::NotificationSource& source,
@@ -55,6 +53,8 @@
   PrefServiceRegistrarMap pref_change_registrars_;
 
   content::NotificationRegistrar notification_registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(TranslateAcceptLanguages);
 };
 
 #endif  // CHROME_BROWSER_TRANSLATE_TRANSLATE_ACCEPT_LANGUAGES_H_
diff --git a/chrome/browser/translate/translate_browsertest.cc b/chrome/browser/translate/translate_browsertest.cc
index 4b04a09..26d4202 100644
--- a/chrome/browser/translate/translate_browsertest.cc
+++ b/chrome/browser/translate/translate_browsertest.cc
@@ -2,16 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/translate/translate_infobar_delegate.h"
+#include "chrome/browser/translate/translate_script.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/browser_test_utils.h"
@@ -21,10 +24,6 @@
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_fetcher_delegate.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 namespace {
 
 const base::FilePath::CharType kTranslateRoot[] =
@@ -79,7 +78,7 @@
 IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, DISABLED_Translate) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -135,7 +134,8 @@
     "  }\n"
     "} } } };\n"
     "cr.googleTranslate.onTranslateElementLoad();\n";
-  net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+  net::TestURLFetcher* fetcher =
+      factory.GetFetcherByID(TranslateScript::kFetcherId);
   ASSERT_TRUE(fetcher);
   net::URLRequestStatus status;
   status.set_status(net::URLRequestStatus::SUCCESS);
@@ -153,7 +153,7 @@
 IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, IgnoreRefreshMetaTag) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -190,7 +190,7 @@
                        IgnoreRefreshMetaTagInCaseInsensitive) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -226,7 +226,7 @@
 IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, IgnoreRefreshMetaTagAtOnload) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -262,7 +262,7 @@
 IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, UpdateLocation) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -298,7 +298,7 @@
 IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, UpdateLocationAtOnload) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/translate/translate_infobar_delegate.cc b/chrome/browser/translate/translate_infobar_delegate.cc
index 726be78..9cabbaa 100644
--- a/chrome/browser/translate/translate_infobar_delegate.cc
+++ b/chrome/browser/translate/translate_infobar_delegate.cc
@@ -32,6 +32,9 @@
 const char kRevertTranslation[] = "Translate.RevertTranslation";
 const char kShowErrorInfobar[] = "Translate.ShowErrorInfobar";
 const char kPerformTranslate[] = "Translate.Translate";
+const char kNeverTranslateLang[] = "Translate.NeverTranslateLang";
+const char kNeverTranslateSite[] = "Translate.NeverTranslateSite";
+const char kAlwaysTranslateLang[] = "Translate.AlwaysTranslateLang";
 
 }  // namespace
 
@@ -158,6 +161,8 @@
     prefs_.BlockLanguage(original_lang);
     RemoveSelf();
   }
+
+  UMA_HISTOGRAM_BOOLEAN(kNeverTranslateLang, true);
 }
 
 bool TranslateInfoBarDelegate::IsSiteBlacklisted() {
@@ -176,6 +181,8 @@
     prefs_.BlacklistSite(host);
     RemoveSelf();
   }
+
+  UMA_HISTOGRAM_BOOLEAN(kNeverTranslateSite, true);
 }
 
 bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() {
@@ -190,6 +197,8 @@
     prefs_.RemoveLanguagePairFromWhitelist(original_lang, target_lang);
   else
     prefs_.WhitelistLanguagePair(original_lang, target_lang);
+
+  UMA_HISTOGRAM_BOOLEAN(kAlwaysTranslateLang, true);
 }
 
 void TranslateInfoBarDelegate::AlwaysTranslatePageLanguage() {
diff --git a/chrome/browser/translate/translate_language_list.cc b/chrome/browser/translate/translate_language_list.cc
index d5b13c0..5a58151 100644
--- a/chrome/browser/translate/translate_language_list.cc
+++ b/chrome/browser/translate/translate_language_list.cc
@@ -103,8 +103,6 @@
 const char kAlphaLanguageQueryName[] = "alpha";
 const char kAlphaLanguageQueryValue[] = "1";
 
-const int kFetcherId = 1;
-
 // Represent if the language list updater is disabled.
 bool update_is_disabled = false;
 
diff --git a/chrome/browser/translate/translate_language_list.h b/chrome/browser/translate/translate_language_list.h
index 5e818c0..4387a43 100644
--- a/chrome/browser/translate/translate_language_list.h
+++ b/chrome/browser/translate/translate_language_list.h
@@ -20,6 +20,8 @@
 // This class is defined to be owned only by TranslateManager.
 class TranslateLanguageList : public ResourceRequestAllowedNotifier::Observer {
  public:
+  static const int kFetcherId = 1;
+
   TranslateLanguageList();
   virtual ~TranslateLanguageList();
 
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc
index 7faeda1..4bfe4e0 100644
--- a/chrome/browser/translate/translate_manager_browsertest.cc
+++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -2,18 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-
 #include <algorithm>
 #include <set>
 #include <vector>
 
-#include "base/json/json_writer.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/prefs/pref_change_registrar.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/test_extension_system.h"
@@ -25,6 +21,7 @@
 #include "chrome/browser/translate/translate_language_list.h"
 #include "chrome/browser/translate/translate_manager.h"
 #include "chrome/browser/translate/translate_prefs.h"
+#include "chrome/browser/translate/translate_script.h"
 #include "chrome/browser/translate/translate_tab_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -42,31 +39,22 @@
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/test/mock_notification_observer.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_renderer_host.h"
-#include "grit/generated_resources.h"
-#include "ipc/ipc_test_sink.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_fetcher_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/WebKit/public/web/WebContextMenuData.h"
 
-using content::NavigationController;
 using content::RenderViewHostTester;
-using content::WebContents;
-using testing::_;
-using testing::Pointee;
-using testing::Property;
-using WebKit::WebContextMenuData;
 
 // An observer that keeps track of whether a navigation entry was committed.
 class NavEntryCommittedObserver : public content::NotificationObserver {
  public:
-  explicit NavEntryCommittedObserver(WebContents* web_contents) {
+  explicit NavEntryCommittedObserver(content::WebContents* web_contents) {
     registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
-                   content::Source<NavigationController>(
-                      &web_contents->GetController()));
+                   content::Source<content::NavigationController>(
+                       &web_contents->GetController()));
   }
 
   virtual void Observe(int type,
@@ -77,8 +65,7 @@
         *(content::Details<content::LoadCommittedDetails>(details).ptr());
   }
 
-  const content::LoadCommittedDetails&
-      get_load_commited_details() const {
+  const content::LoadCommittedDetails& load_committed_details() const {
     return details_;
   }
 
@@ -117,6 +104,22 @@
             0, details, page_translatable));
   }
 
+  void SimulateOnPageTranslated(int routing_id,
+                                const std::string& source_lang,
+                                const std::string& target_lang,
+                                TranslateErrors::Type error) {
+    RenderViewHostTester::TestOnMessageReceived(
+        rvh(),
+        ChromeViewHostMsg_PageTranslated(
+            routing_id, 0, source_lang, target_lang, error));
+  }
+
+  void SimulateOnPageTranslated(const std::string& source_lang,
+                                const std::string& target_lang) {
+    SimulateOnPageTranslated(0, source_lang, target_lang,
+                             TranslateErrors::NONE);
+  }
+
   bool GetTranslateMessage(int* page_id,
                            std::string* original_lang,
                            std::string* target_lang) {
@@ -167,16 +170,6 @@
     return found;
   }
 
-  // Returns true if at least one infobar was closed.
-  bool InfoBarRemoved() {
-    return !removed_infobars_.empty();
-  }
-
-  // Clears the list of stored removed infobars.
-  void ClearRemovedInfoBars() {
-    removed_infobars_.clear();
-  }
-
   void ExpireTranslateScriptImmediately() {
     TranslateManager::GetInstance()->SetTranslateScriptExpirationDelay(0);
   }
@@ -201,7 +194,7 @@
 
     // Ensures it is really handled a reload.
     const content::LoadCommittedDetails& nav_details =
-        nav_observer.get_load_commited_details();
+        nav_observer.load_committed_details();
     EXPECT_TRUE(nav_details.entry != NULL);  // There was a navigation.
     EXPECT_EQ(content::NAVIGATION_TYPE_EXISTING_PAGE, nav_details.type);
 
@@ -255,8 +248,8 @@
   }
 
   void SimulateTranslateScriptURLFetch(bool success) {
-    // TODO(hajimehoshi): 0 is a magic number.
-    net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
+    net::TestURLFetcher* fetcher =
+        url_fetcher_factory_.GetFetcherByID(TranslateScript::kFetcherId);
     ASSERT_TRUE(fetcher);
     net::URLRequestStatus status;
     status.set_status(success ? net::URLRequestStatus::SUCCESS :
@@ -304,8 +297,8 @@
 
       data += "}})";
     }
-    // TODO(hajimehoshi): 1 is a magic number.
-    net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(1);
+    net::TestURLFetcher* fetcher =
+        url_fetcher_factory_.GetFetcherByID(TranslateLanguageList::kFetcherId);
     ASSERT_TRUE(fetcher != NULL);
     fetcher->set_url(fetcher->GetOriginalURL());
     fetcher->set_status(status);
@@ -336,7 +329,7 @@
 class TestRenderViewContextMenu : public RenderViewContextMenu {
  public:
   static TestRenderViewContextMenu* CreateContextMenu(
-      WebContents* web_contents) {
+      content::WebContents* web_contents) {
     content::ContextMenuParams params;
     params.media_type = WebKit::WebContextMenuData::MediaTypeNone;
     params.x = 0;
@@ -351,7 +344,7 @@
     params.writing_direction_left_to_right = 0;
     params.writing_direction_right_to_left = 0;
 #endif  // OS_MACOSX
-    params.edit_flags = WebContextMenuData::CanTranslate;
+    params.edit_flags = WebKit::WebContextMenuData::CanTranslate;
     return new TestRenderViewContextMenu(web_contents, params);
   }
 
@@ -366,7 +359,7 @@
       ui::Accelerator* accelerator) OVERRIDE { return false; }
 
  private:
-  TestRenderViewContextMenu(WebContents* web_contents,
+  TestRenderViewContextMenu(content::WebContents* web_contents,
                             const content::ContextMenuParams& params)
       : RenderViewContextMenu(web_contents, params) {
   }
@@ -377,7 +370,6 @@
 }  // namespace
 
 TEST_F(TranslateManagerBrowserTest, NormalTranslate) {
-  // Simulate navigating to a page.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
   // We should have an infobar.
@@ -407,10 +399,7 @@
   EXPECT_EQ("en", target_lang);
 
   // Simulate the render notifying the translation has been done.
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, "fr", "en", TranslateErrors::NONE));
+  SimulateOnPageTranslated("fr", "en");
 
   // The after translate infobar should be showing.
   infobar = GetTranslateInfoBar();
@@ -426,10 +415,7 @@
   EXPECT_EQ(new_original_lang, original_lang);
   EXPECT_EQ("en", target_lang);
   // Simulate the render notifying the translation has been done.
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, new_original_lang, "en", TranslateErrors::NONE));
+  SimulateOnPageTranslated(new_original_lang, "en");
   // infobar is now invalid.
   TranslateInfoBarDelegate* new_infobar = GetTranslateInfoBar();
   ASSERT_TRUE(new_infobar != NULL);
@@ -444,10 +430,7 @@
   EXPECT_EQ(new_original_lang, original_lang);
   EXPECT_EQ(new_target_lang, target_lang);
   // Simulate the render notifying the translation has been done.
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, new_original_lang, new_target_lang, TranslateErrors::NONE));
+  SimulateOnPageTranslated(new_original_lang, new_target_lang);
   // infobar is now invalid.
   new_infobar = GetTranslateInfoBar();
   ASSERT_TRUE(new_infobar != NULL);
@@ -460,7 +443,6 @@
 }
 
 TEST_F(TranslateManagerBrowserTest, TranslateScriptNotAvailable) {
-  // Simulate navigating to a page.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
   // We should have an infobar.
@@ -472,7 +454,6 @@
   // Simulate clicking translate.
   process()->sink().ClearMessages();
   infobar->Translate();
-  // Simulate a failure retrieving the translate script.
   SimulateTranslateScriptURLFetch(false);
 
   // We should not have sent any message to translate to the renderer.
@@ -514,10 +495,7 @@
 
   // Simulate the render notifying the translation has been done, the server
   // having detected the page was in a known and supported language.
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, "fr", "en", TranslateErrors::NONE));
+  SimulateOnPageTranslated("fr", "en");
 
   // The after translate infobar should be showing.
   infobar = GetTranslateInfoBar();
@@ -532,10 +510,7 @@
   menu.reset(TestRenderViewContextMenu::CreateContextMenu(web_contents()));
   menu->Init();
   menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE, 0);
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          1, 0, "en", "en", TranslateErrors::IDENTICAL_LANGUAGES));
+  SimulateOnPageTranslated(1, "en", "en", TranslateErrors::IDENTICAL_LANGUAGES);
   infobar = GetTranslateInfoBar();
   ASSERT_TRUE(infobar != NULL);
   EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATION_ERROR,
@@ -548,10 +523,8 @@
   menu.reset(TestRenderViewContextMenu::CreateContextMenu(web_contents()));
   menu->Init();
   menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE, 0);
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          2, 0, std::string(), "en", TranslateErrors::UNKNOWN_LANGUAGE));
+  SimulateOnPageTranslated(2, std::string(), "en",
+                           TranslateErrors::UNKNOWN_LANGUAGE);
   infobar = GetTranslateInfoBar();
   ASSERT_TRUE(infobar != NULL);
   EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATION_ERROR,
@@ -581,7 +554,6 @@
     TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
     ASSERT_TRUE(infobar == NULL);
 
-    // Simulate navigating to a page.
     SimulateNavigation(url, lang, true);
 
     // Verify we have/don't have an info-bar as expected.
@@ -590,7 +562,6 @@
         lang != "en";
     EXPECT_EQ(expected, infobar != NULL);
 
-    // Close the info-bar if applicable.
     if (infobar != NULL)
       EXPECT_TRUE(CloseTranslateInfoBar());
   }
@@ -700,7 +671,6 @@
 
 // Tests auto-translate on page.
 TEST_F(TranslateManagerBrowserTest, AutoTranslateOnNavigate) {
-  // Simulate navigating to a page and getting its language.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
   // Simulate the user translating.
@@ -709,11 +679,7 @@
   infobar->Translate();
   // Simulate the translate script being retrieved.
   SimulateTranslateScriptURLFetch(true);
-
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, "fr", "en", TranslateErrors::NONE));
+  SimulateOnPageTranslated("fr", "en");
 
   // Now navigate to a new page in the same language.
   process()->sink().ClearMessages();
@@ -737,7 +703,6 @@
 
 // Tests that multiple OnPageContents do not cause multiple infobars.
 TEST_F(TranslateManagerBrowserTest, MultipleOnPageContents) {
-  // Simulate navigating to a page and getting its language.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
   // Simulate clicking 'Nope' (don't translate).
@@ -759,17 +724,14 @@
 // Test that reloading the page brings back the infobar if the
 // reload succeeded and does not bring it back the reload fails.
 TEST_F(TranslateManagerBrowserTest, Reload) {
-  // Simulate navigating to a page and getting its language.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
-  // Close the infobar.
   EXPECT_TRUE(CloseTranslateInfoBar());
 
   // Reload should bring back the infobar if the page succeds
   ReloadAndWait(true);
   EXPECT_TRUE(GetTranslateInfoBar() != NULL);
 
-  // Close the infobar.
   EXPECT_TRUE(CloseTranslateInfoBar());
 
   // And not show it if the reload fails
@@ -787,25 +749,22 @@
 // location bar brings back the infobar.
 TEST_F(TranslateManagerBrowserTest, ReloadFromLocationBar) {
   GURL url("http://www.google.fr");
-
-  // Simulate navigating to a page and getting its language.
   SimulateNavigation(url, "fr", true);
 
-  // Close the infobar.
   EXPECT_TRUE(CloseTranslateInfoBar());
 
   // Create a pending navigation and simulate a page load.  That should be the
   // equivalent of typing the URL again in the location bar.
   NavEntryCommittedObserver nav_observer(web_contents());
   web_contents()->GetController().LoadURL(url, content::Referrer(),
-                                      content::PAGE_TRANSITION_TYPED,
-                                      std::string());
+                                          content::PAGE_TRANSITION_TYPED,
+                                          std::string());
   rvh_tester()->SendNavigate(0, url);
 
   // Test that we are really getting a same page navigation, the test would be
   // useless if it was not the case.
   const content::LoadCommittedDetails& nav_details =
-      nav_observer.get_load_commited_details();
+      nav_observer.load_committed_details();
   EXPECT_TRUE(nav_details.entry != NULL);  // There was a navigation.
   EXPECT_EQ(content::NAVIGATION_TYPE_SAME_PAGE, nav_details.type);
 
@@ -818,15 +777,12 @@
 // Tests that a closed translate infobar does not reappear when navigating
 // in-page.
 TEST_F(TranslateManagerBrowserTest, CloseInfoBarInPageNavigation) {
-  // Simulate navigating to a page and getting its language.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
-  // Close the infobar.
   EXPECT_TRUE(CloseTranslateInfoBar());
 
   // Navigate in page, no infobar should be shown.
-  SimulateNavigation(GURL("http://www.google.fr/#ref1"), "fr",
-                     true);
+  SimulateNavigation(GURL("http://www.google.fr/#ref1"), "fr", true);
   EXPECT_TRUE(GetTranslateInfoBar() == NULL);
 
   // Navigate out of page, a new infobar should show.
@@ -837,10 +793,8 @@
 // Tests that a closed translate infobar does not reappear when navigating
 // in a subframe. (http://crbug.com/48215)
 TEST_F(TranslateManagerBrowserTest, CloseInfoBarInSubframeNavigation) {
-  // Simulate navigating to a page and getting its language.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
-  // Close the infobar.
   EXPECT_TRUE(CloseTranslateInfoBar());
 
   // Simulate a sub-frame auto-navigating.
@@ -860,7 +814,6 @@
 
 // Tests that denying translation is sticky when navigating in page.
 TEST_F(TranslateManagerBrowserTest, DenyTranslateInPageNavigation) {
-  // Simulate navigating to a page and getting its language.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
   // Simulate clicking 'Nope' (don't translate).
@@ -878,7 +831,6 @@
 // Tests that after translating and closing the infobar, the infobar does not
 // return when navigating in page.
 TEST_F(TranslateManagerBrowserTest, TranslateCloseInfoBarInPageNavigation) {
-  // Simulate navigating to a page and getting its language.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
   // Simulate the user translating.
@@ -887,12 +839,8 @@
   infobar->Translate();
   // Simulate the translate script being retrieved.
   SimulateTranslateScriptURLFetch(true);
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, "fr", "en", TranslateErrors::NONE));
+  SimulateOnPageTranslated("fr", "en");
 
-  // Close the infobar.
   EXPECT_TRUE(CloseTranslateInfoBar());
 
   // Navigate in page, no infobar should be shown.
@@ -910,19 +858,14 @@
 // Tests that the after translate the infobar still shows when navigating
 // in-page.
 TEST_F(TranslateManagerBrowserTest, TranslateInPageNavigation) {
-  // Simulate navigating to a page and getting its language.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
   // Simulate the user translating.
   TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
   ASSERT_TRUE(infobar != NULL);
   infobar->Translate();
-  // Simulate the translate script being retrieved.
   SimulateTranslateScriptURLFetch(true);
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, "fr", "en", TranslateErrors::NONE));
+  SimulateOnPageTranslated("fr", "en");
   // The after translate infobar is showing.
   infobar = GetTranslateInfoBar();
   ASSERT_TRUE(infobar != NULL);
@@ -951,7 +894,6 @@
 // server.
 // The translation server might return a language we don't support.
 TEST_F(TranslateManagerBrowserTest, ServerReportsUnsupportedLanguage) {
-  // Simulate navigating to a page and translating it.
   SimulateNavigation(GURL("http://mail.google.fr"), "fr", true);
   TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
   ASSERT_TRUE(infobar != NULL);
@@ -960,10 +902,7 @@
   SimulateTranslateScriptURLFetch(true);
   // Simulate the render notifying the translation has been done, but it
   // reports a language we don't support.
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, "qbz", "en", TranslateErrors::NONE));
+  SimulateOnPageTranslated("qbz", "en");
 
   // An error infobar should be showing to report that we don't support this
   // language.
@@ -1053,7 +992,6 @@
   PrefService* prefs = profile->GetPrefs();
   prefs->SetBoolean(prefs::kEnableTranslate, true);
 
-  // Simulate navigating to a page and getting its language.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
 
   // An infobar should be shown.
@@ -1078,7 +1016,6 @@
 
 // Tests the "Never translate <language>" pref.
 TEST_F(TranslateManagerBrowserTest, NeverTranslateLanguagePref) {
-  // Simulate navigating to a page and getting its language.
   GURL url("http://www.google.fr");
   SimulateNavigation(url, "fr", true);
 
@@ -1102,7 +1039,6 @@
   EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(url.host()));
   EXPECT_FALSE(translate_prefs.CanTranslateLanguage(profile, "fr"));
 
-  // Close the infobar.
   EXPECT_TRUE(CloseTranslateInfoBar());
 
   // Navigate to a new page also in French.
@@ -1127,7 +1063,6 @@
 
 // Tests the "Never translate this site" pref.
 TEST_F(TranslateManagerBrowserTest, NeverTranslateSitePref) {
-  // Simulate navigating to a page and getting its language.
   GURL url("http://www.google.fr");
   std::string host(url.host());
   SimulateNavigation(url, "fr", true);
@@ -1150,7 +1085,6 @@
   EXPECT_TRUE(translate_prefs.IsSiteBlacklisted(host));
   EXPECT_TRUE(translate_prefs.CanTranslateLanguage(profile, "fr"));
 
-  // Close the infobar.
   EXPECT_TRUE(CloseTranslateInfoBar());
 
   // Navigate to a new page also on the same site.
@@ -1194,7 +1128,6 @@
   TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
   ASSERT_TRUE(infobar != NULL);
   EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATING, infobar->infobar_type());
-  // Simulate the translate script being retrieved.
   SimulateTranslateScriptURLFetch(true);
   int page_id = 0;
   std::string original_lang, target_lang;
@@ -1268,7 +1201,6 @@
   TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
   ASSERT_TRUE(infobar != NULL);
   EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATING, infobar->infobar_type());
-  // Simulate the translate script being retrieved.
   SimulateTranslateScriptURLFetch(true);
   int page_id = 0;
   std::string original_lang, target_lang;
@@ -1282,10 +1214,7 @@
   EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(url.host()));
 
   // Let's simulate the page being translated.
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, "fr", "en", TranslateErrors::NONE));
+  SimulateOnPageTranslated("fr", "en");
 
   // The translate menu should now be disabled.
   menu.reset(TestRenderViewContextMenu::CreateContextMenu(web_contents()));
@@ -1320,10 +1249,7 @@
   menu.reset(TestRenderViewContextMenu::CreateContextMenu(web_contents()));
   menu->Init();
   EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, "de", "en", TranslateErrors::NONE));
+  SimulateOnPageTranslated("de", "en");
   menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE, 0);
   // No message expected since the translation should have been ignored.
   EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
@@ -1428,7 +1354,6 @@
 // Tests that we don't show a translate infobar when a page instructs that it
 // should not be translated.
 TEST_F(TranslateManagerBrowserTest, NonTranslatablePage) {
-  // Simulate navigating to a page.
   SimulateNavigation(GURL("http://mail.google.fr"), "fr", false);
 
   // We should not have an infobar.
@@ -1446,17 +1371,13 @@
 TEST_F(TranslateManagerBrowserTest, ScriptExpires) {
   ExpireTranslateScriptImmediately();
 
-  // Simulate navigating to a page and translating it.
   SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
   TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
   ASSERT_TRUE(infobar != NULL);
   process()->sink().ClearMessages();
   infobar->Translate();
   SimulateTranslateScriptURLFetch(true);
-  RenderViewHostTester::TestOnMessageReceived(
-      rvh(),
-      ChromeViewHostMsg_PageTranslated(
-          0, 0, "fr", "en", TranslateErrors::NONE));
+  SimulateOnPageTranslated("fr", "en");
 
   // A task should have been posted to clear the script, run it.
   base::MessageLoop::current()->RunUntilIdle();
@@ -1471,8 +1392,7 @@
   // for the script and no message should have been sent to the renderer.
   EXPECT_TRUE(
       process()->sink().GetFirstMessageMatching(
-          ChromeViewMsg_TranslatePage::ID) ==
-          NULL);
+          ChromeViewMsg_TranslatePage::ID) == NULL);
   // Now simulate the URL fetch.
   SimulateTranslateScriptURLFetch(true);
   // Now the message should have been sent.
@@ -1503,11 +1423,11 @@
   SessionStartupPref pref(SessionStartupPref::LAST);
   SessionStartupPref::SetStartupPref(browser()->profile(), pref);
 
-  WebContents* current_web_contents =
+  content::WebContents* current_web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   TranslateTabHelper* translate_tab_helper =
       TranslateTabHelper::FromWebContents(current_web_contents);
-  content::Source<WebContents> source(current_web_contents);
+  content::Source<content::WebContents> source(current_web_contents);
 
   ui_test_utils::WindowedNotificationObserverWithDetails<
     LanguageDetectionDetails>
@@ -1531,9 +1451,9 @@
 #define MAYBE_TranslateSessionRestore TranslateSessionRestore
 #endif
 IN_PROC_BROWSER_TEST_F(InProcessBrowserTest, MAYBE_TranslateSessionRestore) {
-  WebContents* current_web_contents =
+  content::WebContents* current_web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  content::Source<WebContents> source(current_web_contents);
+  content::Source<content::WebContents> source(current_web_contents);
 
   ui_test_utils::WindowedNotificationObserverWithDetails<
     LanguageDetectionDetails>
diff --git a/chrome/browser/translate/translate_script.cc b/chrome/browser/translate/translate_script.cc
index 1be1e2b..48184aa 100644
--- a/chrome/browser/translate/translate_script.cc
+++ b/chrome/browser/translate/translate_script.cc
@@ -25,7 +25,6 @@
 const char kScriptURL[] =
     "https://translate.google.com/translate_a/element.js";
 const char kRequestHeader[] = "Google-Translate-Element-Mode: library";
-const int kFetcherId = 0;
 
 // Used in kTranslateScriptURL to specify a callback function name.
 const char kCallbackQueryName[] = "cb";
diff --git a/chrome/browser/translate/translate_script.h b/chrome/browser/translate/translate_script.h
index 5fe0f61..e303243 100644
--- a/chrome/browser/translate/translate_script.h
+++ b/chrome/browser/translate/translate_script.h
@@ -18,6 +18,8 @@
  public:
   typedef base::Callback<void(bool, const std::string&)> Callback;
 
+  static const int kFetcherId = 0;
+
   TranslateScript();
   virtual ~TranslateScript();
 
diff --git a/chrome/browser/ui/app_list/app_list_service_mac.mm b/chrome/browser/ui/app_list/app_list_service_mac.mm
index 1ae9635..56b9db4 100644
--- a/chrome/browser/ui/app_list/app_list_service_mac.mm
+++ b/chrome/browser/ui/app_list/app_list_service_mac.mm
@@ -7,6 +7,7 @@
 
 #include "apps/app_launcher.h"
 #include "apps/app_shim/app_shim_handler_mac.h"
+#include "apps/app_shim/app_shim_mac.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/file_util.h"
@@ -174,7 +175,7 @@
   // TODO(tapted): Create a dock icon using chrome/browser/mac/dock.h .
   web_app::CreateShortcuts(shortcut_info,
                            ShellIntegration::ShortcutLocations(),
-                           web_app::SHORTCUT_CREATION_BY_USER);
+                           web_app::SHORTCUT_CREATION_AUTOMATED);
 }
 
 // Check that there is an app list shim. If enabling and there is not, make one.
@@ -240,8 +241,7 @@
 
 bool AppListControllerDelegateCocoa::CanDoCreateShortcutsFlow(
     bool is_platform_app) {
-  return is_platform_app &&
-      CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppShims);
+  return is_platform_app && apps::IsAppShimsEnabled();
 }
 
 void AppListControllerDelegateCocoa::DoCreateShortcutsFlow(
diff --git a/chrome/browser/ui/app_list/chrome_signin_delegate.cc b/chrome/browser/ui/app_list/chrome_signin_delegate.cc
index ed9639d..e74ca6f 100644
--- a/chrome/browser/ui/app_list/chrome_signin_delegate.cc
+++ b/chrome/browser/ui/app_list/chrome_signin_delegate.cc
@@ -8,12 +8,12 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/host_desktop.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "content/public/common/page_transition_types.h"
 #include "grit/chromium_strings.h"
@@ -53,7 +53,7 @@
 
   Browser* browser = FindOrCreateTabbedBrowser(profile_,
                                                chrome::GetActiveDesktop());
-  chrome::ShowBrowserSignin(browser, SyncPromoUI::SOURCE_APP_LAUNCHER);
+  chrome::ShowBrowserSignin(browser, signin::SOURCE_APP_LAUNCHER);
 }
 
 void ChromeSigninDelegate::OpenLearnMore() {
diff --git a/chrome/browser/ui/app_list/extension_app_item.cc b/chrome/browser/ui/app_list/extension_app_item.cc
index 97912f1..66137eb 100644
--- a/chrome/browser/ui/app_list/extension_app_item.cc
+++ b/chrome/browser/ui/app_list/extension_app_item.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/ui/app_list/app_context_menu.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
-#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
+#include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/extension_icon_set.h"
@@ -258,7 +258,7 @@
   if (RunExtensionEnableFlow())
     return;
 
-  AppLauncherHandler::RecordAppListMainLaunch(extension);
+  CoreAppLauncherHandler::RecordAppListMainLaunch(extension);
   controller_->ActivateApp(profile_, extension, event_flags);
 }
 
diff --git a/chrome/browser/ui/app_list/search/app_result.cc b/chrome/browser/ui/app_list/search/app_result.cc
index 9301f5d..3877c74 100644
--- a/chrome/browser/ui/app_list/search/app_result.cc
+++ b/chrome/browser/ui/app_list/search/app_result.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/search/tokenized_string.h"
 #include "chrome/browser/ui/app_list/search/tokenized_string_match.h"
-#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
+#include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_icon_set.h"
 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
@@ -66,7 +66,7 @@
   if (!extension)
     return;
 
-  AppLauncherHandler::RecordAppListSearchLaunch(extension);
+  CoreAppLauncherHandler::RecordAppListSearchLaunch(extension);
   content::RecordAction(
       content::UserMetricsAction("AppList_ClickOnAppFromSearch"));
 
diff --git a/chrome/browser/ui/app_list/search/webstore_result.cc b/chrome/browser/ui/app_list/search/webstore_result.cc
index f2f36de..87812a1 100644
--- a/chrome/browser/ui/app_list/search/webstore_result.cc
+++ b/chrome/browser/ui/app_list/search/webstore_result.cc
@@ -183,6 +183,7 @@
 
   SetIsInstalling(false);
   UpdateActions();
+  NotifyItemInstalled();
 }
 
 void WebstoreResult::OnExtensionLoaded(
diff --git a/chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.cc b/chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.cc
index 965281b..682a2d9 100644
--- a/chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.cc
+++ b/chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.cc
@@ -61,7 +61,10 @@
       bool accept,
       const string16* prompt_override) OVERRIDE;
 
-  virtual void ResetJavaScriptState(WebContents* web_contents) OVERRIDE;
+  virtual void CancelActiveAndPendingDialogs(
+      WebContents* web_contents) OVERRIDE;
+
+  virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
 
  private:
   ChromeJavaScriptDialogManager();
@@ -77,8 +80,6 @@
                     const std::string& accept_lang,
                     bool is_alert);
 
-  void CancelPendingDialogs(WebContents* web_contents);
-
   // Wrapper around a DialogClosedCallback so that we can intercept it before
   // passing it onto the original callback.
   void OnDialogClosed(DialogClosedCallback callback,
@@ -145,11 +146,13 @@
       extra_data->last_javascript_message_dismissal_;
   bool display_suppress_checkbox = false;
   // Show a checkbox offering to suppress further messages if this message is
-  // being displayed within kJavascriptMessageExpectedDelay of the last one.
+  // being displayed within kJavaScriptMessageExpectedDelay of the last one.
   if (time_since_last_message <
       base::TimeDelta::FromMilliseconds(
-          chrome::kJavascriptMessageExpectedDelay)) {
+          chrome::kJavaScriptMessageExpectedDelay)) {
     display_suppress_checkbox = true;
+  } else {
+    display_suppress_checkbox = false;
   }
 
   bool is_alert = message_type == content::JAVASCRIPT_MESSAGE_TYPE_ALERT;
@@ -223,9 +226,9 @@
   return true;
 }
 
-void ChromeJavaScriptDialogManager::ResetJavaScriptState(
+void ChromeJavaScriptDialogManager::WebContentsDestroyed(
     WebContents* web_contents) {
-  CancelPendingDialogs(web_contents);
+  CancelActiveAndPendingDialogs(web_contents);
   javascript_dialog_extra_data_.erase(web_contents);
 }
 
@@ -266,7 +269,7 @@
       base::i18n::GetDisplayStringInLTRDirectionality(url_string));
 }
 
-void ChromeJavaScriptDialogManager::CancelPendingDialogs(
+void ChromeJavaScriptDialogManager::CancelActiveAndPendingDialogs(
     WebContents* web_contents) {
   AppModalDialogQueue* queue = AppModalDialogQueue::GetInstance();
   AppModalDialog* active_dialog = queue->active_dialog();
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index c2d6888..1cb4879 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -314,6 +314,9 @@
     case ash::UMA_ACCEL_NEXTWINDOW_TAB:
       content::RecordAction(content::UserMetricsAction("Accel_NextWindow_Tab"));
       break;
+    case ash::UMA_ACCEL_OVERVIEW_F5:
+      content::RecordAction(content::UserMetricsAction("Accel_Overview_F5"));
+      break;
     case ash::UMA_ACCEL_PREVWINDOW_F5:
       content::RecordAction(content::UserMetricsAction("Accel_PrevWindow_F5"));
       break;
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index 9a0845a..807fcbd 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -47,12 +47,6 @@
 using extensions::Extension;
 using content::WebContents;
 
-namespace {
-// TODO(skuhne): Remove this temporary fix once M28 is out and CL 11596003
-// has landed. See also launcher.cc for the offset introduction.
-const int kChromeItemOffset = 1;
-}
-
 class LauncherPlatformAppBrowserTest
     : public extensions::PlatformAppBrowserTest {
  protected:
@@ -95,11 +89,6 @@
     return launcher_model()->items()[launcher_model()->item_count() - 1];
   }
 
-  // Activate the launcher item with the given |id|.
-  void ActivateLauncherItem(int id) {
-    launcher_->ActivateLauncherItem(id + kChromeItemOffset);
-  }
-
   ash::Launcher* launcher_;
   ChromeLauncherController* controller_;
 };
@@ -172,11 +161,6 @@
         name, model_->item_count());
   }
 
-  // Activate the launcher item with the given |id|.
-  void ActivateLauncherItem(int id) {
-    launcher_->ActivateLauncherItem(id);
-  }
-
   ash::Launcher* launcher_;
   ash::LauncherModel* model_;
 };
@@ -443,7 +427,7 @@
             launcher_model()->ItemByID(item_id1)->status);
 
   // Activate first one.
-  ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+  launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
   EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model()->ItemByID(item_id1)->status);
   EXPECT_EQ(ash::STATUS_RUNNING,
             launcher_model()->ItemByID(item_id2)->status);
@@ -451,7 +435,7 @@
   EXPECT_FALSE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
 
   // Activate second one.
-  ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2));
+  launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2));
   EXPECT_EQ(ash::STATUS_RUNNING,
             launcher_model()->ItemByID(item_id1)->status);
   EXPECT_EQ(ash::STATUS_ACTIVE, launcher_model()->ItemByID(item_id2)->status);
@@ -466,20 +450,20 @@
   EXPECT_TRUE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
 
   // Activate launcher item for app1, this will activate the first app window.
-  ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+  launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
   EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
   EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
-  ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+  launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
   EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
 
   // Activate the second app again
-  ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2));
+  launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id2));
   EXPECT_FALSE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
   EXPECT_TRUE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
   EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
 
   // Activate the first app again
-  ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
+  launcher_->ActivateLauncherItem(launcher_model()->ItemIndexByID(item_id1));
   EXPECT_TRUE(ash::wm::IsActiveWindow(window1->GetNativeWindow()));
   EXPECT_FALSE(ash::wm::IsActiveWindow(window2->GetNativeWindow()));
   EXPECT_FALSE(ash::wm::IsActiveWindow(window1b->GetNativeWindow()));
@@ -542,7 +526,7 @@
   int tab_count = tab_strip->count();
   ash::LauncherID shortcut_id = CreateShortcut("app1");
   EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status);
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
   EXPECT_EQ(++tab_count, tab_strip->count());
   EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
   WebContents* tab = tab_strip->GetActiveWebContents();
@@ -602,7 +586,7 @@
   ash::wm::MaximizeWindow(window2);
 
   ash::LauncherID shortcut_id = CreateShortcut("app1");
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
   EXPECT_EQ(++tab_count, tab_strip->count());
   EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
 
@@ -610,7 +594,7 @@
   ash::wm::ActivateWindow(window1);
   EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut_id)).status);
 
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
   EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
 }
 
@@ -651,13 +635,13 @@
   EXPECT_EQ(++item_count, model_->item_count());
 
   // Launch first app.
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
   EXPECT_EQ(++tab_count, tab_strip->count());
   WebContents* tab1 = tab_strip->GetActiveWebContents();
   EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status);
 
   // Launch second app.
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut2));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut2));
   EXPECT_EQ(++tab_count, tab_strip->count());
   WebContents* tab2 = tab_strip->GetActiveWebContents();
   ASSERT_NE(tab1, tab2);
@@ -665,7 +649,7 @@
   EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status);
 
   // Reactivate first app.
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
   EXPECT_EQ(tab_count, tab_strip->count());
   EXPECT_EQ(tab_strip->GetActiveWebContents(), tab1);
   EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status);
@@ -683,14 +667,14 @@
   EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut2)).status);
 
   // Reactivate first app.
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut1));
   EXPECT_EQ(tab_count, tab_strip->count());
   EXPECT_EQ(tab_strip->GetActiveWebContents(), tab1);
   EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut1)).status);
   EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut2)).status);
 
   // And second again. This time the second tab should become active.
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut2));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut2));
   EXPECT_EQ(tab_count, tab_strip->count());
   EXPECT_EQ(tab_strip->GetActiveWebContents(), tab3);
   EXPECT_EQ(ash::STATUS_RUNNING, (*model_->ItemByID(shortcut1)).status);
@@ -702,7 +686,7 @@
 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, Navigation) {
   ash::LauncherID shortcut_id = CreateShortcut("app1");
   EXPECT_EQ(ash::STATUS_CLOSED, (*model_->ItemByID(shortcut_id)).status);
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
   EXPECT_EQ(ash::STATUS_ACTIVE, (*model_->ItemByID(shortcut_id)).status);
 
   // Navigate away.
@@ -720,7 +704,7 @@
   TabStripModel* tab_strip = browser()->tab_strip_model();
   int tab_count = tab_strip->count();
   ash::LauncherID shortcut_id = CreateShortcut("app1");
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
   EXPECT_EQ(++tab_count, tab_strip->count());
   EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
 
@@ -746,7 +730,7 @@
   EXPECT_EQ(ash::STATUS_RUNNING, model_->ItemByID(shortcut_id)->status);
 
   // Activating app makes second tab active again.
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
   EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
   EXPECT_EQ(tab_strip->GetActiveWebContents(), second_tab);
 }
@@ -757,7 +741,7 @@
   TabStripModel* tab_strip = browser()->tab_strip_model();
   int tab_count = tab_strip->count();
   ash::LauncherID shortcut_id = CreateShortcut("app1");
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
   EXPECT_EQ(++tab_count, tab_strip->count());
   EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
   WebContents* first_tab = tab_strip->GetActiveWebContents();
@@ -786,7 +770,7 @@
 
   // Activating app makes first tab active again, because second tab isn't
   // in its refocus url path.
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
   EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
   EXPECT_EQ(tab_strip->GetActiveWebContents(), first_tab);
 }
@@ -813,7 +797,7 @@
 
   // Activating app should launch new tab, because second tab isn't
   // in its refocus url path.
-  ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
+  launcher_->ActivateLauncherItem(model_->ItemIndexByID(shortcut_id));
   EXPECT_EQ(++tab_count, tab_strip->count());
   WebContents* second_tab = tab_strip->GetActiveWebContents();
   EXPECT_EQ(ash::STATUS_ACTIVE, model_->ItemByID(shortcut_id)->status);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_browsertest.cc
index 18baf0e..d1800d8 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app_browsertest.cc
@@ -45,6 +45,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/common/switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/app_list/views/apps_grid_view.h"
 #include "ui/aura/test/event_generator.h"
@@ -695,7 +696,7 @@
                        AppPanelClickBehavior) {
   // Enable experimental APIs to allow panel creation.
   CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
+      extensions::switches::kEnableExperimentalExtensionApis);
   // Launch a platform app and create a panel window for it.
   const Extension* extension1 = LoadAndLaunchPlatformApp("launch");
   ShellWindow::CreateParams params;
@@ -752,7 +753,7 @@
 
   // Enable experimental APIs to allow panel creation.
   CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
+      extensions::switches::kEnableExperimentalExtensionApis);
 
   int base_launcher_item_count = launcher_model()->item_count();
   ExtensionTestMessageListener launched_listener("Launched", false);
diff --git a/chrome/browser/ui/ash/screenshot_taker.cc b/chrome/browser/ui/ash/screenshot_taker.cc
index a73c4b5..bb71f80 100644
--- a/chrome/browser/ui/ash/screenshot_taker.cc
+++ b/chrome/browser/ui/ash/screenshot_taker.cc
@@ -10,8 +10,10 @@
 #include "ash/shell.h"
 #include "base/bind.h"
 #include "base/file_util.h"
+#include "base/i18n/time_formatting.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/prefs/pref_service.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/sequenced_worker_pool.h"
@@ -21,8 +23,8 @@
 #include "chrome/browser/notifications/notification_ui_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/webui/screenshot_source.h"
 #include "chrome/browser/ui/window_snapshot/window_snapshot.h"
+#include "chrome/common/pref_names.h"
 #include "content/public/browser/browser_thread.h"
 #include "grit/ash_strings.h"
 #include "grit/theme_resources.h"
@@ -33,12 +35,18 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image.h"
 
+#if defined(USE_ASH)
+#include "ash/shell.h"
+#include "ash/shell_delegate.h"
+#endif
+
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
 #include "chrome/browser/notifications/desktop_notification_service.h"
 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
+#include "chromeos/login/login_state.h"
 #endif
 
 namespace {
@@ -69,7 +77,7 @@
     if (!success_)
       return;
 #if defined(OS_CHROMEOS)
-    file_manager_util::ShowFileInFolder(screenshot_path_);
+    file_manager::util::ShowFileInFolder(screenshot_path_);
 #else
     // TODO(sschmitz): perhaps add similar action for Windows.
 #endif
@@ -221,6 +229,68 @@
   return chrome::GrabWindowSnapshotForUser(window, png_data, snapshot_bounds);
 }
 
+bool ShouldUse24HourClock() {
+#if defined(OS_CHROMEOS)
+  Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
+  if (profile) {
+    return profile->GetPrefs()->GetBoolean(prefs::kUse24HourClock);
+  }
+#endif
+  return base::GetHourClockType() == base::k24HourClock;
+}
+
+std::string GetScreenshotBaseFilename() {
+  base::Time::Exploded now;
+  base::Time::Now().LocalExplode(&now);
+
+  // We don't use base/i18n/time_formatting.h here because it doesn't
+  // support our format.  Don't use ICU either to avoid i18n file names
+  // for non-English locales.
+  // TODO(mukai): integrate this logic somewhere time_formatting.h
+  std::string file_name = base::StringPrintf(
+      "Screenshot %d-%02d-%02d at ", now.year, now.month, now.day_of_month);
+
+  if (ShouldUse24HourClock()) {
+    file_name.append(base::StringPrintf(
+        "%02d.%02d.%02d", now.hour, now.minute, now.second));
+  } else {
+    int hour = now.hour;
+    if (hour > 12) {
+      hour -= 12;
+    } else if (hour == 0) {
+      hour = 12;
+    }
+    file_name.append(base::StringPrintf(
+        "%d.%02d.%02d ", hour, now.minute, now.second));
+    file_name.append((now.hour >= 12) ? "PM" : "AM");
+  }
+
+  return file_name;
+}
+
+bool GetScreenshotDirectory(base::FilePath* directory) {
+  if (g_browser_process->local_state()->GetBoolean(prefs::kDisableScreenshots))
+    return false;
+
+  bool is_logged_in = true;
+
+#if defined(OS_CHROMEOS)
+  is_logged_in = chromeos::LoginState::Get()->IsUserLoggedIn();
+#endif
+
+  if (is_logged_in) {
+    DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext(
+        ash::Shell::GetInstance()->delegate()->GetCurrentBrowserContext());
+    *directory = download_prefs->DownloadPath();
+  } else  {
+    if (!file_util::GetTempDir(directory)) {
+      LOG(ERROR) << "Failed to find temporary directory.";
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace
 
 ScreenshotTaker::ScreenshotTaker()
@@ -235,14 +305,13 @@
   base::FilePath screenshot_directory;
   if (!screenshot_directory_for_test_.empty()) {
     screenshot_directory = screenshot_directory_for_test_;
-  } else if (!ScreenshotSource::GetScreenshotDirectory(&screenshot_directory)) {
+  } else if (!GetScreenshotDirectory(&screenshot_directory)) {
     ShowNotification(ScreenshotTakerObserver::SCREENSHOT_GET_DIR_FAILED,
                      base::FilePath());
     return;
   }
   std::string screenshot_basename = !screenshot_basename_for_test_.empty() ?
-      screenshot_basename_for_test_ :
-      ScreenshotSource::GetScreenshotBaseFilename();
+      screenshot_basename_for_test_ : GetScreenshotBaseFilename();
 
   ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
   // Reorder root_windows to take the primary root window's snapshot at first.
@@ -284,7 +353,7 @@
   base::FilePath screenshot_directory;
   if (!screenshot_directory_for_test_.empty()) {
     screenshot_directory = screenshot_directory_for_test_;
-  } else if (!ScreenshotSource::GetScreenshotDirectory(&screenshot_directory)) {
+  } else if (!GetScreenshotDirectory(&screenshot_directory)) {
     ShowNotification(ScreenshotTakerObserver::SCREENSHOT_GET_DIR_FAILED,
                      base::FilePath());
     return;
@@ -293,8 +362,7 @@
   scoped_refptr<base::RefCountedBytes> png_data(new base::RefCountedBytes);
 
   std::string screenshot_basename = !screenshot_basename_for_test_.empty() ?
-      screenshot_basename_for_test_ :
-      ScreenshotSource::GetScreenshotBaseFilename();
+      screenshot_basename_for_test_ : GetScreenshotBaseFilename();
   base::FilePath screenshot_path =
       screenshot_directory.AppendASCII(screenshot_basename + ".png");
   if (GrabWindowSnapshot(window, rect, &png_data->data())) {
diff --git a/chrome/browser/ui/ash/session_state_delegate_chromeos.cc b/chrome/browser/ui/ash/session_state_delegate_chromeos.cc
index 790de57..43d15dd 100644
--- a/chrome/browser/ui/ash/session_state_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/session_state_delegate_chromeos.cc
@@ -5,11 +5,13 @@
 #include "chrome/browser/ui/ash/session_state_delegate_chromeos.h"
 
 #include "ash/session_state_observer.h"
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "chrome/browser/chromeos/login/screen_locker.h"
 #include "chrome/browser/chromeos/login/user.h"
 #include "chrome/browser/chromeos/login/user_adding_screen.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
+#include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
 
@@ -56,7 +58,10 @@
 }
 
 bool SessionStateDelegateChromeos::IsUserSessionBlocked() const {
-  return !IsActiveUserSessionStarted() || IsScreenLocked() ||
+  bool has_login_manager = CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kLoginManager);
+  return (has_login_manager && !IsActiveUserSessionStarted()) ||
+         IsScreenLocked() ||
          chromeos::UserAddingScreen::Get()->IsRunning();
 }
 
diff --git a/chrome/browser/ui/ash/volume_controller_browsertest_chromeos.cc b/chrome/browser/ui/ash/volume_controller_browsertest_chromeos.cc
index dc8fa68..57fc2e7 100644
--- a/chrome/browser/ui/ash/volume_controller_browsertest_chromeos.cc
+++ b/chrome/browser/ui/ash/volume_controller_browsertest_chromeos.cc
@@ -18,7 +18,6 @@
   virtual ~VolumeControllerTest() {}
 
   virtual void SetUpOnMainThread() OVERRIDE {
-    CHECK(ash::switches::UseNewAudioHandler());
     volume_controller_.reset(new VolumeController());
     audio_handler_ = chromeos::CrasAudioHandler::Get();
   }
diff --git a/chrome/browser/ui/ash/volume_controller_chromeos.cc b/chrome/browser/ui/ash/volume_controller_chromeos.cc
index 0116e68..b8fdff3 100644
--- a/chrome/browser/ui/ash/volume_controller_chromeos.cc
+++ b/chrome/browser/ui/ash/volume_controller_chromeos.cc
@@ -6,7 +6,6 @@
 
 #include "ash/ash_switches.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/audio/audio_handler.h"
 #include "chrome/browser/extensions/api/system_private/system_private_api.h"
 #include "content/public/browser/user_metrics.h"
 
@@ -20,12 +19,11 @@
 }  // namespace
 
 VolumeController::VolumeController() {
-  if (ash::switches::UseNewAudioHandler())
-    CrasAudioHandler::Get()->AddAudioObserver(this);
+  CrasAudioHandler::Get()->AddAudioObserver(this);
 }
 
 VolumeController::~VolumeController() {
-  if (ash::switches::UseNewAudioHandler() && CrasAudioHandler::IsInitialized())
+  if (CrasAudioHandler::IsInitialized())
     CrasAudioHandler::Get()->RemoveAudioObserver(this);
 }
 
@@ -33,19 +31,7 @@
   if (accelerator.key_code() == ui::VKEY_VOLUME_MUTE)
     content::RecordAction(content::UserMetricsAction("Accel_VolumeMute_F8"));
 
-  if (ash::switches::UseNewAudioHandler()) {
-    CrasAudioHandler::Get()->SetOutputMute(true);
-    return true;
-  }
-
-  chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
-
-  // Always muting (and not toggling) as per final decision on
-  // http://crosbug.com/3751
-  audio_handler->SetMuted(true);
-
-  extensions::DispatchVolumeChangedEvent(audio_handler->GetVolumePercent(),
-                                         audio_handler->IsMuted());
+  CrasAudioHandler::Get()->SetOutputMute(true);
   return true;
 }
 
@@ -53,27 +39,14 @@
   if (accelerator.key_code() == ui::VKEY_VOLUME_DOWN)
     content::RecordAction(content::UserMetricsAction("Accel_VolumeDown_F9"));
 
-  if (ash::switches::UseNewAudioHandler()) {
-    CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
-
-    if (audio_handler->IsOutputMuted()) {
-      audio_handler->SetOutputVolumePercent(0);
-    } else {
-      audio_handler->AdjustOutputVolumeByPercent(-kStepPercentage);
-      if (audio_handler->IsOutputVolumeBelowDefaultMuteLvel())
-        audio_handler->SetOutputMute(true);
-    }
-    return true;
+  CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
+  if (audio_handler->IsOutputMuted()) {
+    audio_handler->SetOutputVolumePercent(0);
+  } else {
+    audio_handler->AdjustOutputVolumeByPercent(-kStepPercentage);
+    if (audio_handler->IsOutputVolumeBelowDefaultMuteLvel())
+      audio_handler->SetOutputMute(true);
   }
-
-  chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
-  if (audio_handler->IsMuted())
-    audio_handler->SetVolumePercent(0.0);
-  else
-    audio_handler->AdjustVolumeByPercent(-kStepPercentage);
-
-  extensions::DispatchVolumeChangedEvent(audio_handler->GetVolumePercent(),
-                                         audio_handler->IsMuted());
   return true;
 }
 
@@ -81,61 +54,18 @@
   if (accelerator.key_code() == ui::VKEY_VOLUME_UP)
     content::RecordAction(content::UserMetricsAction("Accel_VolumeUp_F10"));
 
-  if (ash::switches::UseNewAudioHandler()) {
-    CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
-
-    if (audio_handler->IsOutputMuted()) {
+  CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
+  if (audio_handler->IsOutputMuted()) {
       audio_handler->SetOutputMute(false);
       audio_handler->AdjustOutputVolumeToAudibleLevel();
-    } else {
-      audio_handler->AdjustOutputVolumeByPercent(kStepPercentage);
-    }
-    return true;
-  }
-
-  chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
-  if (audio_handler->IsMuted()) {
-    audio_handler->SetMuted(false);
   } else {
-    audio_handler->AdjustVolumeByPercent(kStepPercentage);
+    audio_handler->AdjustOutputVolumeByPercent(kStepPercentage);
   }
 
-  extensions::DispatchVolumeChangedEvent(audio_handler->GetVolumePercent(),
-                                         audio_handler->IsMuted());
   return true;
 }
 
-bool VolumeController::IsAudioMuted() const {
-  DCHECK(!ash::switches::UseNewAudioHandler());
-  return chromeos::AudioHandler::GetInstance()->IsMuted();
-}
-
-void VolumeController::SetAudioMuted(bool muted) {
-  DCHECK(!ash::switches::UseNewAudioHandler());
-  chromeos::AudioHandler::GetInstance()->SetMuted(muted);
-}
-
-// Gets the volume level. The range is [0, 1.0].
-float VolumeController::GetVolumeLevel() const {
-  DCHECK(!ash::switches::UseNewAudioHandler());
-  return chromeos::AudioHandler::GetInstance()->GetVolumePercent() / 100.f;
-}
-
-// Sets the volume level. The range is [0, 1.0].
-void VolumeController::SetVolumeLevel(float level) {
-  SetVolumePercent(level * 100.f);
-}
-
-void VolumeController::SetVolumePercent(double percent) {
-  DCHECK(!ash::switches::UseNewAudioHandler());
-  chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
-  audio_handler->SetVolumePercent(percent);
-  extensions::DispatchVolumeChangedEvent(audio_handler->GetVolumePercent(),
-                                         audio_handler->IsMuted());
-}
-
 void VolumeController::OnOutputVolumeChanged() {
-  DCHECK(ash::switches::UseNewAudioHandler());
   CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
   extensions::DispatchVolumeChangedEvent(
       audio_handler->GetOutputVolumePercent(),
@@ -143,7 +73,6 @@
 }
 
 void VolumeController::OnOutputMuteChanged() {
-  DCHECK(ash::switches::UseNewAudioHandler());
   CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
   extensions::DispatchVolumeChangedEvent(
       audio_handler->GetOutputVolumePercent(),
diff --git a/chrome/browser/ui/ash/volume_controller_chromeos.h b/chrome/browser/ui/ash/volume_controller_chromeos.h
index 9510eee..f0a7b2a 100644
--- a/chrome/browser/ui/ash/volume_controller_chromeos.h
+++ b/chrome/browser/ui/ash/volume_controller_chromeos.h
@@ -22,11 +22,6 @@
   virtual bool HandleVolumeMute(const ui::Accelerator& accelerator) OVERRIDE;
   virtual bool HandleVolumeDown(const ui::Accelerator& accelerator) OVERRIDE;
   virtual bool HandleVolumeUp(const ui::Accelerator& accelerator) OVERRIDE;
-  virtual bool IsAudioMuted() const OVERRIDE;
-  virtual void SetAudioMuted(bool muted) OVERRIDE;
-  virtual float GetVolumeLevel() const OVERRIDE;
-  virtual void SetVolumeLevel(float level) OVERRIDE;
-  virtual void SetVolumePercent(double percent) OVERRIDE;
 
   // Overridden from chromeos::CrasAudioHandler::AudioObserver.
   virtual void OnOutputVolumeChanged() OVERRIDE;
diff --git a/chrome/browser/ui/ash/window_positioner.cc b/chrome/browser/ui/ash/window_positioner.cc
index 04fa3f2..41ad8dd 100644
--- a/chrome/browser/ui/ash/window_positioner.cc
+++ b/chrome/browser/ui/ash/window_positioner.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/ui/ash/window_positioner.h"
 
 #include "ash/shell.h"
-#include "ash/wm/window_cycle_controller.h"
+#include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_resizer.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/window.h"
@@ -100,7 +100,7 @@
     const gfx::Rect& work_area,
     int grid) {
   const std::vector<aura::Window*> windows =
-      ash::WindowCycleController::BuildWindowList(NULL, false);
+      ash::MruWindowTracker::BuildWindowList(false);
 
   std::vector<const gfx::Rect*> regions;
   // Process the window list and check if we can bail immediately.
diff --git a/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller.cc b/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller.cc
index 5075aea..70d0978 100644
--- a/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller.cc
+++ b/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/autofill/autofill_credit_card_bubble_controller.h"
 
+#include <climits>
 #include <string>
 
 #include "base/bind.h"
@@ -40,7 +41,9 @@
 
 namespace {
 
-static const int kMaxGeneratedCardTimesToShow = 4;
+// TODO(dbeam): add back a sensible limit once it's decided or remove
+// kMaxGeneratedCardTimesToShow if this behavior is finalized.
+static const int kMaxGeneratedCardTimesToShow = INT_MAX;
 static const char kWalletGeneratedCardLearnMoreLink[] =
     "http://support.google.com/wallet/bin/answer.py?hl=en&answer=2740044";
 
@@ -72,15 +75,7 @@
 }
 
 // static
-bool AutofillCreditCardBubbleController::ShouldShowGeneratedCardBubble(
-    Profile* profile) {
-  int times_shown = profile->GetPrefs()->GetInteger(
-      ::prefs::kAutofillGeneratedCardBubbleTimesShown);
-  return times_shown < kMaxGeneratedCardTimesToShow;
-}
-
-// static
-void AutofillCreditCardBubbleController::ShowGeneratedCardBubble(
+void AutofillCreditCardBubbleController::ShowGeneratedCardUI(
     content::WebContents* contents,
     const base::string16& fronting_card_name,
     const base::string16& backing_card_name) {
@@ -174,6 +169,11 @@
   return AutofillCreditCardBubble::Create(GetWeakPtr());
 }
 
+base::WeakPtr<AutofillCreditCardBubble> AutofillCreditCardBubbleController::
+    bubble() {
+  return bubble_;
+}
+
 bool AutofillCreditCardBubbleController::CanShow() const {
 #if !defined(OS_ANDROID)
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
@@ -183,6 +183,14 @@
 #endif
 }
 
+bool AutofillCreditCardBubbleController::ShouldDisplayBubbleInitially() const {
+  Profile* profile = Profile::FromBrowserContext(
+      web_contents_->GetBrowserContext());
+  int times_shown = profile->GetPrefs()->GetInteger(
+      ::prefs::kAutofillGeneratedCardBubbleTimesShown);
+  return times_shown < kMaxGeneratedCardTimesToShow;
+}
+
 void AutofillCreditCardBubbleController::ShowAsGeneratedCardBubble(
     const base::string16& fronting_card_name,
     const base::string16& backing_card_name) {
@@ -194,7 +202,9 @@
   backing_card_name_ = backing_card_name;
 
   SetUp();
-  Show(false);
+
+  if (ShouldDisplayBubbleInitially())
+    Show(false);
 }
 
 void AutofillCreditCardBubbleController::ShowAsNewCardSavedBubble(
diff --git a/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller.h b/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller.h
index 0021f49..e6d8885 100644
--- a/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller.h
+++ b/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller.h
@@ -56,16 +56,16 @@
   // Registers preferences this class cares about.
   static void RegisterUserPrefs(user_prefs::PrefRegistrySyncable* registry);
 
-  // Whether the generated card bubble should be shown.
-  static bool ShouldShowGeneratedCardBubble(Profile* profile);
+  // Shows a clickable icon in the omnibox that informs the user about generated
+  // (fronting) cards and how they are used to bill their original (backing)
+  // card. Additionally, if |ShouldDisplayBubbleInitially()| is true, the bubble
+  // will be shown initially (doesn't require being clicked).
+  static void ShowGeneratedCardUI(content::WebContents* contents,
+                                  const base::string16& backing_card_name,
+                                  const base::string16& fronting_card_name);
 
-  // Show a bubble to educate the user about generated (fronting) cards and how
-  // they are used to bill their original (backing) card.
-  static void ShowGeneratedCardBubble(content::WebContents* contents,
-                                      const base::string16& backing_card_name,
-                                      const base::string16& fronting_card_name);
-
-  // Show a bubble notifying the user that new credit card data has been saved.
+  // Show a bubble and clickable omnibox icon notifying the user that new credit
+  // card data has been saved. This bubble always shows initially.
   static void ShowNewCardSavedBubble(content::WebContents* contents,
                                      const base::string16& new_card_name);
 
@@ -115,9 +115,17 @@
   // Creates and returns an Autofill credit card bubble. Exposed for testing.
   virtual base::WeakPtr<AutofillCreditCardBubble> CreateBubble();
 
+  // Returns a weak reference to |bubble_|. May be invalid/NULL.
+  virtual base::WeakPtr<AutofillCreditCardBubble> bubble();
+
   // Whether the bubble can show currently.
   virtual bool CanShow() const;
 
+  // Whether the generated card bubble should be shown initially when showing
+  // the anchor icon. This does not affect whether the generated card's icon
+  // will show in the omnibox.
+  bool ShouldDisplayBubbleInitially() const;
+
   // Show a bubble to educate the user about generated (fronting) cards and how
   // they are used to bill their original (backing) card. Exposed for testing.
   virtual void ShowAsGeneratedCardBubble(
diff --git a/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller_unittest.cc
index 89c5cc3..942afa5 100644
--- a/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_credit_card_bubble_controller_unittest.cc
@@ -47,22 +47,24 @@
  public:
   explicit TestAutofillCreditCardBubbleController(
       content::WebContents* contents)
-      : AutofillCreditCardBubbleController(contents),
-        bubble_(GetWeakPtr()) {
+      : AutofillCreditCardBubbleController(contents) {
     contents->SetUserData(UserDataKey(), this);
     EXPECT_TRUE(IsInstalled());
   }
   virtual ~TestAutofillCreditCardBubbleController() {}
 
-  const TestAutofillCreditCardBubble* bubble() const { return &bubble_; }
-
   bool IsInstalled() const {
     return web_contents()->GetUserData(UserDataKey()) == this;
   }
 
+  TestAutofillCreditCardBubble* GetTestingBubble() {
+    return static_cast<TestAutofillCreditCardBubble*>(
+        AutofillCreditCardBubbleController::bubble().get());
+  }
+
  protected:
   virtual base::WeakPtr<AutofillCreditCardBubble> CreateBubble() OVERRIDE {
-    return bubble_.GetWeakPtr();
+    return TestAutofillCreditCardBubble::Create(GetWeakPtr());
   }
 
   virtual bool CanShow() const OVERRIDE {
@@ -70,8 +72,6 @@
   }
 
  private:
-  TestAutofillCreditCardBubble bubble_;
-
   DISALLOW_COPY_AND_ASSIGN(TestAutofillCreditCardBubbleController);
 };
 
@@ -100,26 +100,22 @@
         ::prefs::kAutofillGeneratedCardBubbleTimesShown);
   }
 
-  bool ShouldShow() {
-    return AutofillCreditCardBubbleController::ShouldShowGeneratedCardBubble(
-        profile());
-  }
-
   Profile* profile() { return &profile_; }
 
   content::WebContentsTester* test_web_contents() {
     return content::WebContentsTester::For(test_web_contents_.get());
   }
 
-  void ShowGeneratedCardBubble() {
-    EXPECT_TRUE(controller()->IsInstalled());
-    controller()->ShowGeneratedCardBubble(
+  void ShowGeneratedCardUI() {
+    ASSERT_TRUE(controller()->IsInstalled());
+    TestAutofillCreditCardBubbleController::ShowGeneratedCardUI(
         test_web_contents_.get(), BackingCard(), FrontingCard());
   }
 
   void ShowNewCardSavedBubble() {
     EXPECT_TRUE(controller()->IsInstalled());
-    controller()->ShowNewCardSavedBubble(test_web_contents_.get(), NewCard());
+    TestAutofillCreditCardBubbleController::ShowNewCardSavedBubble(
+        test_web_contents_.get(), NewCard());
   }
 
   void Navigate() {
@@ -147,26 +143,21 @@
 
 }  // namespace
 
-TEST_F(AutofillCreditCardBubbleControllerTest, ShouldShowGeneratedCardBubble) {
+TEST_F(AutofillCreditCardBubbleControllerTest, GeneratedCardBubbleTimesShown) {
   ASSERT_EQ(0, GeneratedCardBubbleTimesShown());
-  EXPECT_TRUE(ShouldShow());
 
-  ShowGeneratedCardBubble();
+  ShowGeneratedCardUI();
   EXPECT_EQ(1, GeneratedCardBubbleTimesShown());
-  EXPECT_TRUE(ShouldShow());
+  EXPECT_TRUE(controller()->GetTestingBubble()->showing());
 
-  ShowGeneratedCardBubble();
-  ShowGeneratedCardBubble();
+  ShowGeneratedCardUI();
+  ShowGeneratedCardUI();
   EXPECT_EQ(3, GeneratedCardBubbleTimesShown());
-  EXPECT_TRUE(ShouldShow());
-
-  ShowGeneratedCardBubble();
-  EXPECT_EQ(4, GeneratedCardBubbleTimesShown());
-  EXPECT_FALSE(ShouldShow());
+  EXPECT_TRUE(controller()->GetTestingBubble()->showing());
 }
 
 TEST_F(AutofillCreditCardBubbleControllerTest, BubbleText) {
-  ShowGeneratedCardBubble();
+  ShowGeneratedCardUI();
   base::string16 generated_text = controller()->BubbleText();
   EXPECT_NE(generated_text.find(BackingCard()), base::string16::npos);
   EXPECT_NE(generated_text.find(FrontingCard()), base::string16::npos);
@@ -179,7 +170,7 @@
   EXPECT_EQ(new_text.find(FrontingCard()), base::string16::npos);
   EXPECT_NE(new_text.find(NewCard()), base::string16::npos);
 
-  ShowGeneratedCardBubble();
+  ShowGeneratedCardUI();
   EXPECT_EQ(generated_text, controller()->BubbleText());
 
   ShowNewCardSavedBubble();
@@ -187,7 +178,7 @@
 }
 
 TEST_F(AutofillCreditCardBubbleControllerTest, BubbleTextRanges) {
-  ShowGeneratedCardBubble();
+  ShowGeneratedCardUI();
   base::string16 text = controller()->BubbleText();
   std::vector<ui::Range> ranges = controller()->BubbleTextRanges();
 
@@ -204,39 +195,39 @@
 }
 
 TEST_F(AutofillCreditCardBubbleControllerTest, HideOnNavigate) {
-  EXPECT_FALSE(controller()->bubble()->showing());
-  ShowGeneratedCardBubble();
-  EXPECT_TRUE(controller()->bubble()->showing());
+  EXPECT_FALSE(controller()->GetTestingBubble());
+  ShowGeneratedCardUI();
+  EXPECT_TRUE(controller()->GetTestingBubble()->showing());
 
   Navigate();
   EXPECT_FALSE(controller());
 
   SetUp();
 
-  EXPECT_FALSE(controller()->bubble()->showing());
+  EXPECT_FALSE(controller()->GetTestingBubble());
   ShowNewCardSavedBubble();
-  EXPECT_TRUE(controller()->bubble()->showing());
+  EXPECT_TRUE(controller()->GetTestingBubble()->showing());
 
   Navigate();
   EXPECT_FALSE(controller());
 }
 
 TEST_F(AutofillCreditCardBubbleControllerTest, StayOnRedirect) {
-  EXPECT_FALSE(controller()->bubble()->showing());
-  ShowGeneratedCardBubble();
-  EXPECT_TRUE(controller()->bubble()->showing());
+  EXPECT_FALSE(controller()->GetTestingBubble());
+  ShowGeneratedCardUI();
+  EXPECT_TRUE(controller()->GetTestingBubble()->showing());
 
   Redirect();
-  EXPECT_TRUE(controller()->bubble()->showing());
+  EXPECT_TRUE(controller()->GetTestingBubble()->showing());
 
   SetUp();
 
-  EXPECT_FALSE(controller()->bubble()->showing());
+  EXPECT_FALSE(controller()->GetTestingBubble());
   ShowNewCardSavedBubble();
-  EXPECT_TRUE(controller()->bubble()->showing());
+  EXPECT_TRUE(controller()->GetTestingBubble()->showing());
 
   Redirect();
-  EXPECT_TRUE(controller()->bubble()->showing());
+  EXPECT_TRUE(controller()->GetTestingBubble()->showing());
 }
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc
index d003cf7..478c15b 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc
@@ -5,6 +5,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -109,7 +110,8 @@
             Profile::FromBrowserContext(contents->GetBrowserContext())->
                 GetRequestContext(), this),
         message_loop_runner_(runner),
-        use_validation_(false) {}
+        use_validation_(false),
+        weak_ptr_factory_(this) {}
 
   virtual ~TestAutofillDialogController() {}
 
@@ -167,6 +169,10 @@
     use_validation_ = use_validation;
   }
 
+  base::WeakPtr<TestAutofillDialogController> AsWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
  protected:
   virtual PersonalDataManager* GetManager() OVERRIDE {
     return &test_manager_;
@@ -192,6 +198,9 @@
   // This is used to control what |CurrentNotifications()| returns for testing.
   std::vector<DialogNotification> notifications_;
 
+  // Allows generation of WeakPtrs, so controller liveness can be tested.
+  base::WeakPtrFactory<TestAutofillDialogController> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(TestAutofillDialogController);
 };
 
@@ -202,6 +211,19 @@
   AutofillDialogControllerTest() {}
   virtual ~AutofillDialogControllerTest() {}
 
+  virtual void SetUpOnMainThread() OVERRIDE {
+    autofill::test::DisableSystemServices(browser()->profile());
+  }
+
+  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+#if defined(OS_MACOSX)
+    // OSX support for requestAutocomplete is still hidden behind a switch.
+    // Pending resolution of http://crbug.com/157274
+    CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kEnableInteractiveAutocomplete);
+#endif
+  }
+
   void InitializeControllerOfType(DialogType dialog_type) {
     FormData form;
     form.name = ASCIIToUTF16("TestForm");
@@ -354,6 +376,23 @@
   EXPECT_EQ(DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric_logger().dialog_type());
 }
 
+// Ensure that Hide() will only destroy the controller object after the
+// message loop has run. Otherwise, there may be read-after-free issues
+// during some tests.
+IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, DeferredDestruction) {
+  InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
+
+  base::WeakPtr<TestAutofillDialogController> weak_ptr =
+      controller()->AsWeakPtr();
+  EXPECT_TRUE(weak_ptr.get());
+
+  controller()->Hide();
+  EXPECT_TRUE(weak_ptr.get());
+
+  RunMessageLoop();
+  EXPECT_FALSE(weak_ptr.get());
+}
+
 // Ensure that the expected metric is logged when the dialog is closed during
 // signin.
 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, CloseDuringSignin) {
@@ -504,19 +543,10 @@
   RunMessageLoop();
 }
 
-#if defined(OS_MACOSX)
-// TODO(groby): Implement the necessary functionality and enable this test:
-// http://crbug.com/256864
-#define MAYBE_RequestAutocompleteDoesntShowSteps \
-    DISABLED_RequestAutocompleteDoesntShowSteps
-#else
-#define MAYBE_RequestAutocompleteDoesntShowSteps \
-    RequestAutocompleteDoesntShowSteps
-#endif
 // Test that Autocheckout steps are not showing after submitting the
 // dialog for controller with type DIALOG_TYPE_REQUEST_AUTOCOMPLETE.
 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest,
-                       MAYBE_RequestAutocompleteDoesntShowSteps) {
+                       RequestAutocompleteDoesntShowSteps) {
   InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
   controller()->AddAutocheckoutStep(AUTOCHECKOUT_STEP_PROXY_CARD);
 
@@ -530,17 +560,10 @@
   EXPECT_FALSE(controller()->ShouldShowProgressBar());
 }
 
-#if defined(OS_MACOSX)
-// TODO(groby): Implement the necessary functionality and enable this test:
-// http://crbug.com/256864
-#define MAYBE_FillComboboxFromAutofill DISABLED_FillComboboxFromAutofill
-#else
-#define MAYBE_FillComboboxFromAutofill FillComboboxFromAutofill
-#endif
 // Tests that changing the value of a CC expiration date combobox works as
 // expected when Autofill is used to fill text inputs.
 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest,
-                       MAYBE_FillComboboxFromAutofill) {
+                       FillComboboxFromAutofill) {
   InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
 
   CreditCard card1;
@@ -718,14 +741,7 @@
             controller()->GetTestableView()->GetSize().width());
 }
 
-#if defined(OS_MACOSX)
-// TODO(groby): Implement the necessary functionality and enable this test:
-// http://crbug.com/256864
-#define MAYBE_AutocompleteEvent DISABLED_AutocompleteEvent
-#else
-#define MAYBE_AutocompleteEvent AutocompleteEvent
-#endif
-IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, MAYBE_AutocompleteEvent) {
+IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, AutocompleteEvent) {
   AutofillDialogControllerImpl* controller =
       SetUpHtmlAndInvoke("<input autocomplete='cc-name'>");
 
@@ -739,17 +755,8 @@
   ExpectDomMessage("success");
 }
 
-#if defined(OS_MACOSX)
-// TODO(groby): Implement the necessary functionality and enable this test:
-// http://crbug.com/256864
-#define MAYBE_AutocompleteErrorEventReasonInvalid \
-    DISABLED_AutocompleteErrorEventReasonInvalid
-#else
-#define MAYBE_AutocompleteErrorEventReasonInvalid \
-    AutocompleteErrorEventReasonInvalid
-#endif
 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest,
-                       MAYBE_AutocompleteErrorEventReasonInvalid) {
+                       AutocompleteErrorEventReasonInvalid) {
   AutofillDialogControllerImpl* controller =
       SetUpHtmlAndInvoke("<input autocomplete='cc-name' pattern='.*zebra.*'>");
 
@@ -767,17 +774,8 @@
   ExpectDomMessage("error: invalid");
 }
 
-#if defined(OS_MACOSX)
-// TODO(groby): Implement the necessary functionality and enable this test:
-// http://crbug.com/256864
-#define MAYBE_AutocompleteErrorEventReasonCancel \
-    DISABLED_AutocompleteErrorEventReasonCancel
-#else
-#define MAYBE_AutocompleteErrorEventReasonCancel \
-    AutocompleteErrorEventReasonCancel
-#endif
 IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest,
-                       MAYBE_AutocompleteErrorEventReasonCancel) {
+                       AutocompleteErrorEventReasonCancel) {
   SetUpHtmlAndInvoke("<input autocomplete='cc-name'>")->GetTestableView()->
       CancelForTesting();
   ExpectDomMessage("error: cancel");
@@ -795,14 +793,7 @@
       controller()->GetTestableView()->SubmitForTesting());
 }
 
-#if defined(OS_MACOSX)
-// TODO(groby): Implement the necessary functionality and enable this test:
-// http://crbug.com/256864
-#define MAYBE_PreservedSections  DISABLED_PreservedSections
-#else
-#define MAYBE_PreservedSections PreservedSections
-#endif
-IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, MAYBE_PreservedSections) {
+IN_PROC_BROWSER_TEST_F(AutofillDialogControllerTest, PreservedSections) {
   InitializeControllerOfType(DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
   controller()->set_use_validation(true);
 
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
index 4c4d652..32d1383 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
@@ -70,7 +70,6 @@
 #include "grit/generated_resources.h"
 #include "grit/theme_resources.h"
 #include "grit/webkit_resources.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/cert/cert_status_flags.h"
 #include "ui/base/base_window.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -139,7 +138,7 @@
   // The credit card name is filled from the billing section's data.
   if (field.type() == CREDIT_CARD_NAME &&
       (section == SECTION_BILLING || section == SECTION_CC_BILLING)) {
-    return input.type == NAME_FULL;
+    return input.type == NAME_BILLING_FULL;
   }
 
   return InputTypeMatchesFieldType(input, field.type());
@@ -556,7 +555,7 @@
   };
 
   const DetailInput kBillingInputs[] = {
-    { 4, NAME_FULL, IDS_AUTOFILL_DIALOG_PLACEHOLDER_CARDHOLDER_NAME },
+    { 4, NAME_BILLING_FULL, IDS_AUTOFILL_DIALOG_PLACEHOLDER_CARDHOLDER_NAME },
     { 5, ADDRESS_BILLING_LINE1,
       IDS_AUTOFILL_DIALOG_PLACEHOLDER_ADDRESS_LINE_1 },
     { 6, ADDRESS_BILLING_LINE2,
@@ -1395,7 +1394,7 @@
 
 const wallet::Address* AutofillDialogControllerImpl::
     ActiveShippingAddress() const {
-  if (!IsPayingWithWallet())
+  if (!IsPayingWithWallet() || !IsShippingAddressRequired())
     return NULL;
 
   const SuggestionsMenuModel* model =
@@ -2098,20 +2097,6 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// content::WebContentsObserver implementation.
-
-void AutofillDialogControllerImpl::DidNavigateMainFrame(
-    const content::LoadCommittedDetails& details,
-    const content::FrameNavigateParams& params) {
-  // Close view if necessary.
-  if (!net::registry_controlled_domains::SameDomainOrHost(
-          details.previous_url, details.entry->GetURL(),
-          net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES)) {
-    Hide();
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // SuggestionsMenuModelDelegate implementation.
 
 void AutofillDialogControllerImpl::SuggestionItemSelected(
@@ -2165,6 +2150,10 @@
   return wallet_cookie_value_;
 }
 
+bool AutofillDialogControllerImpl::IsShippingAddressRequired() const {
+  return cares_about_shipping_;
+}
+
 void AutofillDialogControllerImpl::OnDidAcceptLegalDocuments() {
   DCHECK(is_submitting_ && IsPayingWithWallet());
   has_accepted_legal_documents_ = true;
@@ -2349,8 +2338,7 @@
     const DialogType dialog_type,
     const base::Callback<void(const FormStructure*,
                               const std::string&)>& callback)
-    : WebContentsObserver(contents),
-      profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
+    : profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
       contents_(contents),
       initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
       dialog_type_(dialog_type),
@@ -2787,9 +2775,7 @@
     DialogSection section) const {
   // For now, only SECTION_SHIPPING may be omitted due to a site not asking for
   // any of the fields.
-  // TODO(estade): remove !IsPayingWithWallet() check once WalletClient support
-  // is added. http://crbug.com/243514
-  if (section == SECTION_SHIPPING && !IsPayingWithWallet())
+  if (section == SECTION_SHIPPING)
     return cares_about_shipping_;
 
   return true;
@@ -3059,7 +3045,8 @@
 
   const wallet::Address* active_address = ActiveShippingAddress();
   if (!IsManuallyEditingSection(SECTION_SHIPPING) &&
-      !ShouldUseBillingForShipping()) {
+      !ShouldUseBillingForShipping() &&
+      IsShippingAddressRequired()) {
     active_address_id_ = active_address->object_id();
     DCHECK(!active_address_id_.empty());
   }
@@ -3077,7 +3064,7 @@
   }
 
   scoped_ptr<wallet::Address> inputted_address;
-  if (active_address_id_.empty()) {
+  if (active_address_id_.empty() && IsShippingAddressRequired()) {
     if (ShouldUseBillingForShipping()) {
       const wallet::Address& address = inputted_instrument ?
           *inputted_instrument->address() : active_instrument->address();
@@ -3103,7 +3090,8 @@
 
   // If there's neither an address nor instrument to save, |GetFullWallet()|
   // is called when the risk fingerprint is loaded.
-  if (!active_instrument_id_.empty() && !active_address_id_.empty()) {
+  if (!active_instrument_id_.empty() &&
+      (!active_address_id_.empty() || !IsShippingAddressRequired())) {
     GetFullWallet();
     return;
   }
@@ -3147,7 +3135,7 @@
   DCHECK(IsPayingWithWallet());
   DCHECK(wallet_items_);
   DCHECK(!active_instrument_id_.empty());
-  DCHECK(!active_address_id_.empty());
+  DCHECK(!active_address_id_.empty() || !IsShippingAddressRequired());
 
   std::vector<wallet::WalletClient::RiskCapability> capabilities;
   capabilities.push_back(wallet::WalletClient::VERIFY_CVC);
@@ -3464,11 +3452,12 @@
     return;
   }
 
-  if (!full_wallet_ || !full_wallet_->billing_address() ||
-      !AutofillCreditCardBubbleController::ShouldShowGeneratedCardBubble(
-          profile())) {
-    // If this run of the dialog didn't result in a valid |full_wallet_| or the
-    // generated card bubble shouldn't be shown now, don't show it again.
+  if (!full_wallet_ || !full_wallet_->billing_address())
+    return;
+
+  // Don't show GeneratedCardBubble if Autocheckout failed.
+  if (GetDialogType() == DIALOG_TYPE_AUTOCHECKOUT &&
+      autocheckout_state_ != AUTOCHECKOUT_SUCCESS) {
     return;
   }
 
@@ -3482,7 +3471,7 @@
     GetBillingInfoFromOutputs(output, &card, NULL, NULL);
     backing_last_four = card.TypeAndLastFourDigits();
   }
-  AutofillCreditCardBubbleController::ShowGeneratedCardBubble(
+  AutofillCreditCardBubbleController::ShowGeneratedCardUI(
       web_contents(), backing_last_four, full_wallet_->TypeAndLastFourDigits());
 }
 
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
index 7769189..cac7e46 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
@@ -33,7 +33,6 @@
 #include "components/autofill/core/browser/personal_data_manager_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/ssl_status.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/base/ui_base_types.h"
@@ -70,7 +69,6 @@
 class AutofillDialogControllerImpl : public AutofillDialogController,
                                      public AutofillPopupDelegate,
                                      public content::NotificationObserver,
-                                     public content::WebContentsObserver,
                                      public SuggestionsMenuModelDelegate,
                                      public wallet::WalletClientDelegate,
                                      public wallet::WalletSigninHelperDelegate,
@@ -196,11 +194,6 @@
                        const content::NotificationSource& source,
                        const content::NotificationDetails& details) OVERRIDE;
 
-  // content::WebContentsObserver implementation.
-  virtual void DidNavigateMainFrame(
-      const content::LoadCommittedDetails& details,
-      const content::FrameNavigateParams& params) OVERRIDE;
-
   // SuggestionsMenuModelDelegate implementation.
   virtual void SuggestionItemSelected(SuggestionsMenuModel* model,
                                       size_t index) OVERRIDE;
@@ -210,6 +203,7 @@
   virtual DialogType GetDialogType() const OVERRIDE;
   virtual std::string GetRiskData() const OVERRIDE;
   virtual std::string GetWalletCookieValue() const OVERRIDE;
+  virtual bool IsShippingAddressRequired() const OVERRIDE;
   virtual void OnDidAcceptLegalDocuments() OVERRIDE;
   virtual void OnDidAuthenticateInstrument(bool success) OVERRIDE;
   virtual void OnDidGetFullWallet(
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc
index c1686d2..1db6496 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc
@@ -87,11 +87,11 @@
 void SetOutputValue(const DetailInputs& inputs,
                     DetailOutputMap* outputs,
                     AutofillFieldType type,
-                    const std::string& value) {
+                    const base::string16& value) {
   for (size_t i = 0; i < inputs.size(); ++i) {
     const DetailInput& input = inputs[i];
     (*outputs)[&input] = input.type == type ?
-        ASCIIToUTF16(value) :
+        value :
         input.initial_value;
   }
 }
@@ -285,11 +285,11 @@
  public:
   explicit TestAutofillCreditCardBubbleController(
       content::WebContents* contents)
-      : AutofillCreditCardBubbleController(contents),
-        bubble_(GetWeakPtr()) {
+      : AutofillCreditCardBubbleController(contents) {
     contents->SetUserData(UserDataKey(), this);
-    EXPECT_EQ(contents->GetUserData(UserDataKey()), this);
+    CHECK_EQ(contents->GetUserData(UserDataKey()), this);
   }
+
   virtual ~TestAutofillCreditCardBubbleController() {}
 
   MOCK_METHOD2(ShowAsGeneratedCardBubble,
@@ -300,7 +300,7 @@
 
  protected:
   virtual base::WeakPtr<AutofillCreditCardBubble> CreateBubble() OVERRIDE {
-    return bubble_.GetWeakPtr();
+    return TestAutofillCreditCardBubble::Create(GetWeakPtr());
   }
 
   virtual bool CanShow() const OVERRIDE {
@@ -308,8 +308,6 @@
   }
 
  private:
-  TestAutofillCreditCardBubble bubble_;
-
   DISALLOW_COPY_AND_ASSIGN(TestAutofillCreditCardBubbleController);
 };
 
@@ -430,7 +428,8 @@
     const DetailInputs& inputs =
         controller()->RequestedFieldsForSection(section);
 
-    SetOutputValue(inputs, &outputs, CREDIT_CARD_NUMBER, cc_number);
+    SetOutputValue(inputs, &outputs, CREDIT_CARD_NUMBER,
+                   ASCIIToUTF16(cc_number));
     ValidityData validity_data =
         controller()->InputsAreValid(section, outputs, VALIDATE_FINAL);
     EXPECT_EQ(should_pass ? 0U : 1U, validity_data.count(CREDIT_CARD_NUMBER));
@@ -530,7 +529,7 @@
     const DetailInputs& inputs =
         controller()->RequestedFieldsForSection(section);
     // Make sure country is United States.
-    SetOutputValue(inputs, &outputs, address, "United States");
+    SetOutputValue(inputs, &outputs, address, ASCIIToUTF16("United States"));
 
     // Existing data should have no errors.
     ValidityData validity_data =
@@ -538,7 +537,7 @@
     EXPECT_EQ(0U, validity_data.count(phone));
 
     // Input an empty phone number with VALIDATE_FINAL.
-    SetOutputValue( inputs, &outputs, phone, "");
+    SetOutputValue( inputs, &outputs, phone, base::string16());
     validity_data =
         controller()->InputsAreValid(section, outputs, VALIDATE_FINAL);
     EXPECT_EQ(1U, validity_data.count(phone));
@@ -549,37 +548,111 @@
     EXPECT_EQ(0U, validity_data.count(phone));
 
     // Input an invalid phone number.
-    SetOutputValue(inputs, &outputs, phone, "ABC");
+    SetOutputValue(inputs, &outputs, phone, ASCIIToUTF16("ABC"));
     validity_data =
         controller()->InputsAreValid(section, outputs, VALIDATE_EDIT);
     EXPECT_EQ(1U, validity_data.count(phone));
 
     // Input a local phone number.
-    SetOutputValue(inputs, &outputs, phone, "2155546699");
+    SetOutputValue(inputs, &outputs, phone, ASCIIToUTF16("2155546699"));
     validity_data =
         controller()->InputsAreValid(section, outputs, VALIDATE_EDIT);
     EXPECT_EQ(0U, validity_data.count(phone));
 
     // Input an invalid local phone number.
-    SetOutputValue(inputs, &outputs, phone, "215554669");
+    SetOutputValue(inputs, &outputs, phone, ASCIIToUTF16("215554669"));
     validity_data =
         controller()->InputsAreValid(section, outputs, VALIDATE_EDIT);
     EXPECT_EQ(1U, validity_data.count(phone));
 
     // Input an international phone number.
-    SetOutputValue(inputs, &outputs, phone, "+33 892 70 12 39");
+    SetOutputValue(inputs, &outputs, phone, ASCIIToUTF16("+33 892 70 12 39"));
     validity_data =
         controller()->InputsAreValid(section, outputs, VALIDATE_EDIT);
     EXPECT_EQ(0U, validity_data.count(phone));
 
     // Input an invalid international phone number.
-    SetOutputValue(inputs, &outputs, phone, "+112333 892 70 12 39");
+    SetOutputValue(inputs, &outputs, phone,
+                   ASCIIToUTF16("+112333 892 70 12 39"));
     validity_data =
         controller()->InputsAreValid(section, outputs, VALIDATE_EDIT);
     EXPECT_EQ(1U, validity_data.count(phone));
   }
 }
 
+TEST_F(AutofillDialogControllerTest, ExpirationDateValidity) {
+  DetailOutputMap outputs;
+  const DetailInputs& inputs =
+      controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
+
+  ui::ComboboxModel* exp_year_model =
+      controller()->ComboboxModelForAutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+  ui::ComboboxModel* exp_month_model =
+      controller()->ComboboxModelForAutofillType(CREDIT_CARD_EXP_MONTH);
+
+  base::string16 default_year_value =
+      exp_year_model->GetItemAt(exp_year_model->GetDefaultIndex());
+
+  base::string16 other_year_value =
+      exp_year_model->GetItemAt(exp_year_model->GetItemCount() - 1);
+  base::string16 other_month_value =
+      exp_month_model->GetItemAt(exp_month_model->GetItemCount() - 1);
+
+  SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_4_DIGIT_YEAR,
+                 default_year_value);
+
+  // Expiration default values "validate" with VALIDATE_EDIT.
+  ValidityData validity_data =
+      controller()->InputsAreValid(SECTION_CC_BILLING, outputs, VALIDATE_EDIT);
+  EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+  EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_MONTH));
+
+  // Expiration date with default month "validates" with VALIDATE_EDIT.
+  SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_4_DIGIT_YEAR,
+                 other_year_value);
+
+  validity_data =
+      controller()->InputsAreValid(SECTION_CC_BILLING, outputs, VALIDATE_EDIT);
+  EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+  EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_MONTH));
+
+  // Expiration date with default year "validates" with VALIDATE_EDIT.
+  SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_MONTH, other_month_value);
+
+  validity_data =
+      controller()->InputsAreValid(SECTION_CC_BILLING, outputs, VALIDATE_EDIT);
+  EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+  EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_MONTH));
+
+  // Expiration default values fail with VALIDATE_FINAL.
+  SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_4_DIGIT_YEAR,
+                 default_year_value);
+
+  validity_data =
+      controller()->InputsAreValid(SECTION_CC_BILLING, outputs, VALIDATE_FINAL);
+  EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+  EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_EXP_MONTH));
+
+  // Expiration date with default month fails with VALIDATE_FINAL.
+  SetOutputValue(inputs,
+                 &outputs,
+                 CREDIT_CARD_EXP_4_DIGIT_YEAR,
+                 other_year_value);
+
+  validity_data =
+      controller()->InputsAreValid(SECTION_CC_BILLING, outputs, VALIDATE_FINAL);
+  EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+  EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_EXP_MONTH));
+
+  // Expiration date with default year fails with VALIDATE_FINAL.
+  SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_MONTH, other_month_value);
+
+  validity_data =
+      controller()->InputsAreValid(SECTION_CC_BILLING, outputs, VALIDATE_FINAL);
+  EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+  EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_MONTH));
+}
+
 TEST_F(AutofillDialogControllerTest, BillingNameValidation) {
   // Construct DetailOutputMap from AutofillProfile data.
   SwitchToAutofill();
@@ -589,21 +662,21 @@
       controller()->RequestedFieldsForSection(SECTION_BILLING);
 
   // Input an empty billing name with VALIDATE_FINAL.
-  SetOutputValue(inputs, &outputs, NAME_FULL, "");
+  SetOutputValue(inputs, &outputs, NAME_BILLING_FULL, base::string16());
   ValidityData validity_data =
       controller()->InputsAreValid(SECTION_BILLING, outputs, VALIDATE_FINAL);
-  EXPECT_EQ(1U, validity_data.count(NAME_FULL));
+  EXPECT_EQ(1U, validity_data.count(NAME_BILLING_FULL));
 
   // Input an empty billing name with VALIDATE_EDIT.
   validity_data =
       controller()->InputsAreValid(SECTION_BILLING, outputs, VALIDATE_EDIT);
-  EXPECT_EQ(0U, validity_data.count(NAME_FULL));
+  EXPECT_EQ(0U, validity_data.count(NAME_BILLING_FULL));
 
   // Input a non-empty billing name.
-  SetOutputValue(inputs, &outputs, NAME_FULL, "Bob");
+  SetOutputValue(inputs, &outputs, NAME_BILLING_FULL, ASCIIToUTF16("Bob"));
   validity_data =
       controller()->InputsAreValid(SECTION_BILLING, outputs, VALIDATE_FINAL);
-  EXPECT_EQ(0U, validity_data.count(NAME_FULL));
+  EXPECT_EQ(0U, validity_data.count(NAME_BILLING_FULL));
 
   // Switch to Wallet which only considers names with with at least two names to
   // be valid.
@@ -619,49 +692,52 @@
 
   // Input an empty billing name with VALIDATE_FINAL. Data source should not
   // change this behavior.
-  SetOutputValue(wallet_inputs, &wallet_outputs, NAME_FULL, "");
+  SetOutputValue(wallet_inputs, &wallet_outputs, NAME_BILLING_FULL,
+                 base::string16());
   validity_data =
       controller()->InputsAreValid(
           SECTION_CC_BILLING, wallet_outputs, VALIDATE_FINAL);
-  EXPECT_EQ(1U, validity_data.count(NAME_FULL));
+  EXPECT_EQ(1U, validity_data.count(NAME_BILLING_FULL));
 
   // Input an empty billing name with VALIDATE_EDIT. Data source should not
   // change this behavior.
   validity_data =
       controller()->InputsAreValid(
           SECTION_CC_BILLING, wallet_outputs, VALIDATE_EDIT);
-  EXPECT_EQ(0U, validity_data.count(NAME_FULL));
+  EXPECT_EQ(0U, validity_data.count(NAME_BILLING_FULL));
 
   // Input a one name billing name. Wallet does not currently support this.
-  SetOutputValue(wallet_inputs, &wallet_outputs, NAME_FULL, "Bob");
+  SetOutputValue(wallet_inputs, &wallet_outputs, NAME_BILLING_FULL,
+                 ASCIIToUTF16("Bob"));
   validity_data =
       controller()->InputsAreValid(
           SECTION_CC_BILLING, wallet_outputs, VALIDATE_FINAL);
-  EXPECT_EQ(1U, validity_data.count(NAME_FULL));
+  EXPECT_EQ(1U, validity_data.count(NAME_BILLING_FULL));
 
   // Input a two name billing name.
-  SetOutputValue(wallet_inputs, &wallet_outputs, NAME_FULL,
-                 "Bob Barker");
+  SetOutputValue(wallet_inputs, &wallet_outputs, NAME_BILLING_FULL,
+                 ASCIIToUTF16("Bob Barker"));
   validity_data =
       controller()->InputsAreValid(
           SECTION_CC_BILLING, wallet_outputs, VALIDATE_FINAL);
-  EXPECT_EQ(0U, validity_data.count(NAME_FULL));
+  EXPECT_EQ(0U, validity_data.count(NAME_BILLING_FULL));
 
   // Input a more than two name billing name.
-  SetOutputValue(wallet_inputs, &wallet_outputs, NAME_FULL,
-                 "John Jacob Jingleheimer Schmidt");
+  SetOutputValue(wallet_inputs, &wallet_outputs, NAME_BILLING_FULL,
+                 ASCIIToUTF16("John Jacob Jingleheimer Schmidt"));
   validity_data =
       controller()->InputsAreValid(
           SECTION_CC_BILLING, wallet_outputs, VALIDATE_FINAL);
-  EXPECT_EQ(0U, validity_data.count(NAME_FULL));
+  EXPECT_EQ(0U, validity_data.count(NAME_BILLING_FULL));
 
   // Input a billing name with lots of crazy whitespace.
-  SetOutputValue(wallet_inputs, &wallet_outputs, NAME_FULL,
-                 "     \\n\\r John \\n  Jacob Jingleheimer \\t Schmidt  ");
+  SetOutputValue(
+      wallet_inputs, &wallet_outputs, NAME_BILLING_FULL,
+      ASCIIToUTF16("     \\n\\r John \\n  Jacob Jingleheimer \\t Schmidt  "));
   validity_data =
       controller()->InputsAreValid(
           SECTION_CC_BILLING, wallet_outputs, VALIDATE_FINAL);
-  EXPECT_EQ(0U, validity_data.count(NAME_FULL));
+  EXPECT_EQ(0U, validity_data.count(NAME_BILLING_FULL));
 }
 
 TEST_F(AutofillDialogControllerTest, CreditCardNumberValidation) {
@@ -945,7 +1021,7 @@
 
   EXPECT_EQ(CREDIT_CARD_NAME, form_structure()->field(1)->type());
   string16 cc_name = form_structure()->field(1)->value;
-  EXPECT_EQ(CREDIT_CARD_NAME, form_structure()->field(6)->type());
+  EXPECT_EQ(NAME_BILLING_FULL, form_structure()->field(6)->type());
   string16 billing_name = form_structure()->field(6)->value;
   EXPECT_EQ(NAME_FULL, form_structure()->field(13)->type());
   string16 shipping_name = form_structure()->field(13)->value;
@@ -982,7 +1058,7 @@
 
   EXPECT_EQ(CREDIT_CARD_NAME, form_structure()->field(1)->type());
   string16 cc_name = form_structure()->field(1)->value;
-  EXPECT_EQ(CREDIT_CARD_NAME, form_structure()->field(6)->type());
+  EXPECT_EQ(NAME_BILLING_FULL, form_structure()->field(6)->type());
   string16 billing_name = form_structure()->field(6)->value;
   EXPECT_EQ(NAME_FULL, form_structure()->field(13)->type());
   string16 shipping_name = form_structure()->field(13)->value;
@@ -2097,6 +2173,34 @@
   EXPECT_TRUE(form_structure());
 }
 
+TEST_F(AutofillDialogControllerTest, ShippingSectionCanBeHiddenForWallet) {
+  SwitchToWallet();
+
+  FormFieldData email_field;
+  email_field.autocomplete_attribute = "email";
+  FormFieldData cc_field;
+  cc_field.autocomplete_attribute = "cc-number";
+  FormFieldData billing_field;
+  billing_field.autocomplete_attribute = "billing region";
+
+  FormData form_data;
+  form_data.fields.push_back(email_field);
+  form_data.fields.push_back(cc_field);
+  form_data.fields.push_back(billing_field);
+
+  SetUpControllerWithFormData(form_data);
+  EXPECT_FALSE(controller()->SectionIsActive(SECTION_SHIPPING));
+  EXPECT_FALSE(controller()->IsShippingAddressRequired());
+
+  EXPECT_CALL(*controller()->GetTestingWalletClient(),
+              GetFullWallet(_)).Times(1);
+  scoped_ptr<wallet::WalletItems> wallet_items = wallet::GetTestWalletItems();
+  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
+  SubmitWithWalletItems(wallet_items.Pass());
+  controller()->OnDidGetFullWallet(wallet::GetTestFullWalletInstrumentOnly());
+  EXPECT_TRUE(form_structure());
+}
+
 TEST_F(AutofillDialogControllerTest, NotProdNotification) {
   // To make IsPayingWithWallet() true.
   controller()->OnDidGetWalletItems(wallet::GetTestWalletItems());
@@ -2123,7 +2227,7 @@
   const DetailInputs& inputs =
       controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
   DetailOutputMap outputs;
-  SetOutputValue(inputs, &outputs, COMPANY_NAME, "Bluth Company");
+  SetOutputValue(inputs, &outputs, COMPANY_NAME, ASCIIToUTF16("Bluth Company"));
 
   ValidityData validity_data =
       controller()->InputsAreValid(SECTION_CC_BILLING, outputs, VALIDATE_EDIT);
@@ -2131,7 +2235,8 @@
   EXPECT_EQ(1U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
 
   // Make the local input year differ from the instrument.
-  SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_4_DIGIT_YEAR, "3002");
+  SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_4_DIGIT_YEAR,
+                 ASCIIToUTF16("3002"));
 
   validity_data =
       controller()->InputsAreValid(SECTION_CC_BILLING, outputs, VALIDATE_EDIT);
@@ -2139,7 +2244,7 @@
   EXPECT_EQ(0U, validity_data.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
 
   // Make the local input month differ from the instrument.
-  SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_MONTH, "06");
+  SetOutputValue(inputs, &outputs, CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("06"));
 
   validity_data =
       controller()->InputsAreValid(SECTION_CC_BILLING, outputs, VALIDATE_EDIT);
@@ -2356,4 +2461,16 @@
       3, controller()->MenuModelForSection(SECTION_SHIPPING)->GetItemCount());
 }
 
+TEST_F(AutofillDialogControllerTest, GeneratedCardBubbleNotShown) {
+  EXPECT_CALL(*test_bubble_controller(),
+              ShowAsGeneratedCardBubble(_, _)).Times(0);
+  EXPECT_CALL(*test_bubble_controller(), ShowAsNewCardSavedBubble(_)).Times(0);
+
+  SubmitWithWalletItems(CompleteAndValidWalletItems());
+  controller()->set_dialog_type(DIALOG_TYPE_AUTOCHECKOUT);
+  controller()->OnDidGetFullWallet(wallet::GetTestFullWallet());
+  controller()->OnAutocheckoutError();
+  controller()->ViewClosed();
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/autofill_dialog_types.cc b/chrome/browser/ui/autofill/autofill_dialog_types.cc
index 6a2a4a2..a10aa43 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_types.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_types.cc
@@ -28,7 +28,7 @@
     case DialogNotification::AUTOCHECKOUT_SUCCESS:
     case DialogNotification::EXPLANATORY_MESSAGE:
     case DialogNotification::WALLET_USAGE_CONFIRMATION:
-      return SkColorSetRGB(0x47, 0x89, 0xfa);
+      return SkColorSetRGB(0xf5, 0xf5, 0xf5);
     case DialogNotification::REQUIRED_ACTION:
     case DialogNotification::WALLET_ERROR:
     case DialogNotification::AUTOCHECKOUT_ERROR:
@@ -45,16 +45,36 @@
   return SK_ColorTRANSPARENT;
 }
 
+SkColor DialogNotification::GetBorderColor() const {
+  switch (type_) {
+    case DialogNotification::AUTOCHECKOUT_SUCCESS:
+    case DialogNotification::EXPLANATORY_MESSAGE:
+    case DialogNotification::WALLET_USAGE_CONFIRMATION:
+      return SkColorSetRGB(0xe5, 0xe5, 0xe5);
+    case DialogNotification::REQUIRED_ACTION:
+    case DialogNotification::WALLET_ERROR:
+    case DialogNotification::AUTOCHECKOUT_ERROR:
+    case DialogNotification::DEVELOPER_WARNING:
+    case DialogNotification::SECURITY_WARNING:
+    case DialogNotification::VALIDATION_ERROR:
+    case DialogNotification::NONE:
+      return GetBackgroundColor();
+  }
+
+  NOTREACHED();
+  return SK_ColorTRANSPARENT;
+}
+
 SkColor DialogNotification::GetTextColor() const {
   switch (type_) {
     case DialogNotification::REQUIRED_ACTION:
     case DialogNotification::WALLET_ERROR:
     case DialogNotification::AUTOCHECKOUT_ERROR:
-      return SkColorSetRGB(102, 102, 102);
     case DialogNotification::AUTOCHECKOUT_SUCCESS:
-    case DialogNotification::DEVELOPER_WARNING:
     case DialogNotification::EXPLANATORY_MESSAGE:
     case DialogNotification::WALLET_USAGE_CONFIRMATION:
+      return SkColorSetRGB(102, 102, 102);
+    case DialogNotification::DEVELOPER_WARNING:
     case DialogNotification::SECURITY_WARNING:
     case DialogNotification::VALIDATION_ERROR:
       return SK_ColorWHITE;
diff --git a/chrome/browser/ui/autofill/autofill_dialog_types.h b/chrome/browser/ui/autofill/autofill_dialog_types.h
index 4f24d34..94f312b 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_types.h
+++ b/chrome/browser/ui/autofill/autofill_dialog_types.h
@@ -96,9 +96,10 @@
   DialogNotification();
   DialogNotification(Type type, const string16& display_text);
 
-  // Returns the appropriate background or text color for the view's
+  // Returns the appropriate background, border, or text color for the view's
   // notification area based on |type_|.
   SkColor GetBackgroundColor() const;
+  SkColor GetBorderColor() const;
   SkColor GetTextColor() const;
 
   // Whether this notification has an arrow pointing up at the account chooser.
diff --git a/chrome/browser/ui/autofill/test_autofill_credit_card_bubble.cc b/chrome/browser/ui/autofill/test_autofill_credit_card_bubble.cc
index 00e81f0..237b969 100644
--- a/chrome/browser/ui/autofill/test_autofill_credit_card_bubble.cc
+++ b/chrome/browser/ui/autofill/test_autofill_credit_card_bubble.cc
@@ -6,20 +6,21 @@
 
 namespace autofill {
 
-TestAutofillCreditCardBubble::TestAutofillCreditCardBubble(
-    const base::WeakPtr<AutofillCreditCardBubbleController>& controller)
-    : showing_(false),
-      weak_ptr_factory_(this) {}
+// static
+base::WeakPtr<TestAutofillCreditCardBubble>
+    TestAutofillCreditCardBubble::Create(
+        const base::WeakPtr<AutofillCreditCardBubbleController>& controller) {
+  return (new TestAutofillCreditCardBubble(controller))->GetWeakPtr();
+}
 
 TestAutofillCreditCardBubble::~TestAutofillCreditCardBubble() {}
 
-// AutofillCreditCardBubble:
 void TestAutofillCreditCardBubble::Show() {
   showing_ = true;
 }
 
 void TestAutofillCreditCardBubble::Hide() {
-  showing_ = false;
+  delete this;
 }
 
 bool TestAutofillCreditCardBubble::IsHiding() const {
@@ -31,4 +32,9 @@
   return weak_ptr_factory_.GetWeakPtr();
 }
 
+TestAutofillCreditCardBubble::TestAutofillCreditCardBubble(
+    const base::WeakPtr<AutofillCreditCardBubbleController>& controller)
+    : showing_(false),
+      weak_ptr_factory_(this) {}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/test_autofill_credit_card_bubble.h b/chrome/browser/ui/autofill/test_autofill_credit_card_bubble.h
index 491eac1..4eef7b7 100644
--- a/chrome/browser/ui/autofill/test_autofill_credit_card_bubble.h
+++ b/chrome/browser/ui/autofill/test_autofill_credit_card_bubble.h
@@ -21,7 +21,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 class TestAutofillCreditCardBubble : public AutofillCreditCardBubble {
  public:
-  explicit TestAutofillCreditCardBubble(
+  // Creates a bubble and returns a weak reference to it.
+  static base::WeakPtr<TestAutofillCreditCardBubble> Create(
       const base::WeakPtr<AutofillCreditCardBubbleController>& controller);
 
   virtual ~TestAutofillCreditCardBubble();
@@ -36,6 +37,9 @@
   bool showing() const { return showing_; }
 
  private:
+  explicit TestAutofillCreditCardBubble(
+      const base::WeakPtr<AutofillCreditCardBubbleController>& controller);
+
   // Whether the bubble is currently showing or not.
   bool showing_;
 
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index dfb10d9..114bb99 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -27,6 +27,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/notification_service.h"
@@ -36,10 +37,6 @@
 #include "content/public/test/browser_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::WebContents;
 
 namespace {
@@ -231,6 +228,63 @@
     InProcessBrowserTest::SetUpCommandLine(command_line);
   }
 
+  // Navigates to the test indicated by |test_name| which is expected to try to
+  // open a popup. Verifies that the popup was blocked and then opens the
+  // blocked popup. Once the popup stopped loading, verifies that the title of
+  // the page is "PASS".
+  //
+  // If |expect_new_browser| is true, the popup is expected to open a new
+  // window, or a background tab if it is false.
+  void RunCheckTest(const base::FilePath& test_name, bool expect_new_browser) {
+    GURL url(ui_test_utils::GetTestUrl(base::FilePath(kTestDir), test_name));
+
+    CountRenderViewHosts counter;
+
+    ui_test_utils::NavigateToURL(browser(), url);
+
+    // Since the popup blocker blocked the window.open, there should be only one
+    // tab.
+    EXPECT_EQ(1u, chrome::GetBrowserCount(browser()->profile(),
+                                          browser()->host_desktop_type()));
+    EXPECT_EQ(1, browser()->tab_strip_model()->count());
+    WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    EXPECT_EQ(url, web_contents->GetURL());
+
+    // And no new RVH created.
+    EXPECT_EQ(0, counter.GetRenderViewHostCreatedCount());
+
+    content::WindowedNotificationObserver observer(
+        chrome::NOTIFICATION_TAB_ADDED,
+        content::NotificationService::AllSources());
+    ui_test_utils::BrowserAddedObserver browser_observer;
+
+    // Launch the blocked popup.
+    PopupBlockerTabHelper* popup_blocker_helper =
+        PopupBlockerTabHelper::FromWebContents(web_contents);
+    EXPECT_EQ(1u, popup_blocker_helper->GetBlockedPopupsCount());
+    std::map<int32, GURL> blocked_requests =
+        popup_blocker_helper->GetBlockedPopupRequests();
+    std::map<int32, GURL>::const_iterator iter = blocked_requests.begin();
+    popup_blocker_helper->ShowBlockedPopup(iter->first);
+
+    observer.Wait();
+    Browser* new_browser;
+    if (expect_new_browser) {
+      new_browser  = browser_observer.WaitForSingleNewBrowser();
+      web_contents = new_browser->tab_strip_model()->GetActiveWebContents();
+    } else {
+      new_browser = browser();
+      EXPECT_EQ(2, browser()->tab_strip_model()->count());
+      web_contents = browser()->tab_strip_model()->GetWebContentsAt(1);
+    }
+
+    // Check that the check passed.
+    base::string16 expected_title(base::ASCIIToUTF16("PASS"));
+    content::TitleWatcher title_watcher(web_contents, expected_title);
+    EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(BetterPopupBlockerBrowserTest);
 };
@@ -239,7 +293,7 @@
                        BlockWebContentsCreation) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -288,7 +342,7 @@
                        PopupBlockedFakeClickOnAnchorNoTarget) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -372,91 +426,26 @@
 }
 
 IN_PROC_BROWSER_TEST_F(BetterPopupBlockerBrowserTest, CorrectReferrer) {
-  GURL url(ui_test_utils::GetTestUrl(
-      base::FilePath(kTestDir),
-      base::FilePath(FILE_PATH_LITERAL("popup-referrer.html"))));
-
-  CountRenderViewHosts counter;
-
-  ui_test_utils::NavigateToURL(browser(), url);
-
-  // If the popup blocker blocked the blank post, there should be only one tab.
-  EXPECT_EQ(1u, chrome::GetBrowserCount(browser()->profile(),
-                                        browser()->host_desktop_type()));
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-  WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_EQ(url, web_contents->GetURL());
-
-  // And no new RVH created.
-  EXPECT_EQ(0, counter.GetRenderViewHostCreatedCount());
-
-  content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_TAB_ADDED,
-      content::NotificationService::AllSources());
-  ui_test_utils::BrowserAddedObserver browser_observer;
-
-  // Launch the blocked popup.
-  PopupBlockerTabHelper* popup_blocker_helper =
-      PopupBlockerTabHelper::FromWebContents(web_contents);
-  EXPECT_EQ(1u, popup_blocker_helper->GetBlockedPopupsCount());
-  std::map<int32, GURL> blocked_requests =
-      popup_blocker_helper->GetBlockedPopupRequests();
-  std::map<int32, GURL>::const_iterator iter = blocked_requests.begin();
-  popup_blocker_helper->ShowBlockedPopup(iter->first);
-
-  observer.Wait();
-  Browser* new_browser = browser_observer.WaitForSingleNewBrowser();
-
-  // Check that the referrer was correctly set.
-  web_contents = new_browser->tab_strip_model()->GetActiveWebContents();
-  base::string16 expected_title(base::ASCIIToUTF16("PASS"));
-  content::TitleWatcher title_watcher(web_contents, expected_title);
-  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+  RunCheckTest(base::FilePath(FILE_PATH_LITERAL("popup-referrer.html")), true);
 }
 
 IN_PROC_BROWSER_TEST_F(BetterPopupBlockerBrowserTest, WindowFeaturesBarProps) {
-  GURL url(ui_test_utils::GetTestUrl(
-      base::FilePath(kTestDir),
-      base::FilePath(FILE_PATH_LITERAL("popup-windowfeatures.html"))));
+  RunCheckTest(base::FilePath(FILE_PATH_LITERAL("popup-windowfeatures.html")),
+               true);
+}
 
-  CountRenderViewHosts counter;
+IN_PROC_BROWSER_TEST_F(BetterPopupBlockerBrowserTest, SessionStorage) {
+  RunCheckTest(base::FilePath(FILE_PATH_LITERAL("popup-sessionstorage.html")),
+               true);
+}
 
-  ui_test_utils::NavigateToURL(browser(), url);
+IN_PROC_BROWSER_TEST_F(BetterPopupBlockerBrowserTest, Opener) {
+  RunCheckTest(base::FilePath(FILE_PATH_LITERAL("popup-opener.html")), true);
+}
 
-  // If the popup blocker blocked the blank post, there should be only one tab.
-  EXPECT_EQ(1u, chrome::GetBrowserCount(browser()->profile(),
-                                        browser()->host_desktop_type()));
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-  WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_EQ(url, web_contents->GetURL());
-
-  // And no new RVH created.
-  EXPECT_EQ(0, counter.GetRenderViewHostCreatedCount());
-
-  content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_TAB_ADDED,
-      content::NotificationService::AllSources());
-  ui_test_utils::BrowserAddedObserver browser_observer;
-
-  // Launch the blocked popup.
-  PopupBlockerTabHelper* popup_blocker_helper =
-      PopupBlockerTabHelper::FromWebContents(web_contents);
-  EXPECT_EQ(1u, popup_blocker_helper->GetBlockedPopupsCount());
-  std::map<int32, GURL> blocked_requests =
-      popup_blocker_helper->GetBlockedPopupRequests();
-  std::map<int32, GURL>::const_iterator iter = blocked_requests.begin();
-  popup_blocker_helper->ShowBlockedPopup(iter->first);
-
-  observer.Wait();
-  Browser* new_browser = browser_observer.WaitForSingleNewBrowser();
-
-  // Check that the referrer was correctly set.
-  web_contents = new_browser->tab_strip_model()->GetActiveWebContents();
-  base::string16 expected_title(base::ASCIIToUTF16("PASS"));
-  content::TitleWatcher title_watcher(web_contents, expected_title);
-  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+IN_PROC_BROWSER_TEST_F(BetterPopupBlockerBrowserTest, OpenerSuppressed) {
+  RunCheckTest(
+      base::FilePath(FILE_PATH_LITERAL("popup-openersuppressed.html")), false);
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
index ee2852c..7212bae 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
@@ -109,6 +109,7 @@
   nav_params.tabstrip_add_types = TabStripModel::ADD_ACTIVE;
   nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
   nav_params.user_gesture = user_gesture;
+  nav_params.should_set_opener = !opener_suppressed;
   web_contents()->GetView()->GetContainerBounds(&nav_params.window_bounds);
   if (features.xSet)
     nav_params.window_bounds.set_x(features.x);
diff --git a/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.cc b/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.cc
index 9ac9c8c..cb2d723 100644
--- a/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.cc
@@ -4,12 +4,12 @@
 
 #include "chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h"
 
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 
 BookmarkBubbleSignInDelegate::BookmarkBubbleSignInDelegate(Browser* browser)
@@ -25,7 +25,7 @@
 
 void BookmarkBubbleSignInDelegate::OnSignInLinkClicked() {
   EnsureBrowser();
-  chrome::ShowBrowserSignin(browser_, SyncPromoUI::SOURCE_BOOKMARK_BUBBLE);
+  chrome::ShowBrowserSignin(browser_, signin::SOURCE_BOOKMARK_BUBBLE);
   DCHECK(!browser_->tab_strip_model()->empty());
   browser_->window()->Show();
 }
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils.cc b/chrome/browser/ui/bookmarks/bookmark_utils.cc
index 71c8ad0..fa52139 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_utils.cc
@@ -28,10 +28,6 @@
 #include "net/base/net_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
-#if defined(ENABLE_MANAGED_USERS)
-#include "chrome/browser/managed_mode/managed_user_service.h"
-#endif
-
 namespace chrome {
 
 int num_bookmark_urls_before_prompting = 15;
@@ -288,12 +284,10 @@
 }
 
 bool ShouldShowAppsShortcutInBookmarkBar(Profile* profile) {
-#if defined(ENABLE_MANAGED_USERS)
   // Managed users can not have apps installed currently so there's no need to
   // show the apps shortcut.
-  if (ManagedUserService::ProfileIsManaged(profile))
+  if (profile->IsManaged())
     return false;
-#endif
 
   return IsAppsShortcutEnabled(profile) &&
       profile->GetPrefs()->GetBoolean(prefs::kShowAppsShortcutInBookmarkBar);
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index a9835f4..61b796c 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -404,7 +404,7 @@
   encoding_auto_detect_.Init(prefs::kWebKitUsesUniversalDetector,
                              profile_->GetPrefs());
 
-  if (is_type_tabbed())
+  if (chrome::IsInstantExtendedAPIEnabled() && is_type_tabbed())
     instant_controller_.reset(new BrowserInstantController(this));
 
   UpdateBookmarkBarState(BOOKMARK_BAR_STATE_CHANGE_INIT);
@@ -718,7 +718,7 @@
   // profile, and there are downloads associated with that profile,
   // those downloads would be cancelled by our window (-> profile) close.
   DownloadService* download_service =
-      DownloadServiceFactory::GetForProfile(profile());
+      DownloadServiceFactory::GetForBrowserContext(profile());
   if ((profile_window_count == 0) &&
       (download_service->DownloadCount() > 0) &&
       profile()->IsOffTheRecord()) {
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index f247638..0d58d60 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -88,10 +88,6 @@
 #include "chrome/browser/browser_process.h"
 #endif
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using content::InterstitialPage;
 using content::HostZoomMap;
 using content::NavigationController;
@@ -281,7 +277,7 @@
 IN_PROC_BROWSER_TEST_F(BrowserTest, NoTitle) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -301,7 +297,7 @@
 IN_PROC_BROWSER_TEST_F(BrowserTest, Title) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -1763,8 +1759,9 @@
 }
 #endif
 
-// TODO(jochen): Rerwite test to not rely on popup blocker to hide popups.
-IN_PROC_BROWSER_TEST_F(BrowserTest, DISABLED_WindowOpenClose) {
+IN_PROC_BROWSER_TEST_F(BrowserTest, WindowOpenClose) {
+  CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kDisablePopupBlocking);
   GURL url = ui_test_utils::GetTestUrl(
       base::FilePath(), base::FilePath().AppendASCII("window.close.html"));
 
@@ -1783,7 +1780,7 @@
 IN_PROC_BROWSER_TEST_F(BrowserTest, FullscreenBookmarkBar) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -1988,7 +1985,7 @@
 IN_PROC_BROWSER_TEST_F(AppModeTest, EnableAppModeTest) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/ui/browser_close_browsertest.cc b/chrome/browser/ui/browser_close_browsertest.cc
index 608c48d..4f2b145 100644
--- a/chrome/browser/ui/browser_close_browsertest.cc
+++ b/chrome/browser/ui/browser_close_browsertest.cc
@@ -151,7 +151,7 @@
     for (std::vector<Profile*>::const_iterator pit = profiles.begin();
          pit != profiles.end(); ++pit) {
       DownloadService* download_service =
-          DownloadServiceFactory::GetForProfile(*pit);
+          DownloadServiceFactory::GetForBrowserContext(*pit);
       if (download_service->HasCreatedDownloadManager()) {
         DownloadManager *mgr = BrowserContext::GetDownloadManager(*pit);
         scoped_refptr<content::DownloadTestFlushObserver> observer(
@@ -160,7 +160,7 @@
       }
       if ((*pit)->HasOffTheRecordProfile()) {
         DownloadService* incognito_download_service =
-          DownloadServiceFactory::GetForProfile(
+          DownloadServiceFactory::GetForBrowserContext(
               (*pit)->GetOffTheRecordProfile());
         if (incognito_download_service->HasCreatedDownloadManager()) {
           DownloadManager *mgr = BrowserContext::GetDownloadManager(
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 8726534..7429d58 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/sessions/tab_restore_service.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/browser/shell_integration.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
@@ -24,7 +25,6 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_utils.h"
 #include "chrome/common/content_restriction.h"
@@ -684,7 +684,7 @@
       ShowHelp(browser_, HELP_SOURCE_MENU);
       break;
     case IDC_SHOW_SIGNIN:
-      ShowBrowserSignin(browser_, SyncPromoUI::SOURCE_MENU);
+      ShowBrowserSignin(browser_, signin::SOURCE_MENU);
       break;
     case IDC_TOGGLE_SPEECH_INPUT:
       ToggleSpeechInput(browser_);
diff --git a/chrome/browser/ui/browser_instant_controller.cc b/chrome/browser/ui/browser_instant_controller.cc
index c36cad8..650da0d 100644
--- a/chrome/browser/ui/browser_instant_controller.cc
+++ b/chrome/browser/ui/browser_instant_controller.cc
@@ -38,7 +38,7 @@
 
 BrowserInstantController::BrowserInstantController(Browser* browser)
     : browser_(browser),
-      instant_(this, chrome::IsInstantExtendedAPIEnabled()),
+      instant_(this),
       instant_unload_handler_(browser) {
   profile_pref_registrar_.Init(profile()->GetPrefs());
   profile_pref_registrar_.Add(
@@ -206,15 +206,13 @@
   if (old_state.mode != new_state.mode) {
     const SearchMode& new_mode = new_state.mode;
 
-    if (chrome::IsInstantExtendedAPIEnabled()) {
-      // Record some actions corresponding to the mode change. Note that to get
-      // the full story, it's necessary to look at other UMA actions as well,
-      // such as tab switches.
-      if (new_mode.is_search_results())
-        content::RecordAction(UserMetricsAction("InstantExtended.ShowSRP"));
-      else if (new_mode.is_ntp())
-        content::RecordAction(UserMetricsAction("InstantExtended.ShowNTP"));
-    }
+    // Record some actions corresponding to the mode change. Note that to get
+    // the full story, it's necessary to look at other UMA actions as well,
+    // such as tab switches.
+    if (new_mode.is_search_results())
+      content::RecordAction(UserMetricsAction("InstantExtended.ShowSRP"));
+    else if (new_mode.is_ntp())
+      content::RecordAction(UserMetricsAction("InstantExtended.ShowNTP"));
 
     instant_.SearchModeChanged(old_state.mode, new_mode);
   }
diff --git a/chrome/browser/ui/browser_mac.cc b/chrome/browser/ui/browser_mac.cc
index d557eac..6c66af6 100644
--- a/chrome/browser/ui/browser_mac.cc
+++ b/chrome/browser/ui/browser_mac.cc
@@ -51,7 +51,7 @@
   browser->window()->Show();
 }
 
-void OpenSyncSetupWindow(Profile* profile, SyncPromoUI::Source source) {
+void OpenSyncSetupWindow(Profile* profile, signin::Source source) {
   Browser* browser =
       new Browser(Browser::CreateParams(profile,
                                         chrome::HOST_DESKTOP_TYPE_NATIVE));
diff --git a/chrome/browser/ui/browser_mac.h b/chrome/browser/ui/browser_mac.h
index 5031ac5..fec755c 100644
--- a/chrome/browser/ui/browser_mac.h
+++ b/chrome/browser/ui/browser_mac.h
@@ -5,8 +5,8 @@
 #ifndef CHROME_BROWSER_UI_BROWSER_MAC_H_
 #define CHROME_BROWSER_UI_BROWSER_MAC_H_
 
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 
 class Profile;
 
@@ -19,7 +19,7 @@
 void OpenDownloadsWindow(Profile* profile);
 void OpenHelpWindow(Profile* profile, HelpSource source);
 void OpenOptionsWindow(Profile* profile);
-void OpenSyncSetupWindow(Profile* profile, SyncPromoUI::Source source);
+void OpenSyncSetupWindow(Profile* profile, signin::Source source);
 void OpenClearBrowsingDataDialogWindow(Profile* profile);
 void OpenImportSettingsDialogWindow(Profile* profile);
 void OpenBookmarkManagerWindow(Profile* profile);
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 7250569..990693e 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -45,6 +45,7 @@
 #endif
 
 using content::GlobalRequestID;
+using content::NavigationController;
 using content::WebContents;
 
 class BrowserNavigatorWebContentsAdoption {
@@ -243,7 +244,7 @@
 void LoadURLInContents(WebContents* target_contents,
                        const GURL& url,
                        chrome::NavigateParams* params) {
-  content::NavigationController::LoadURLParams load_url_params(url);
+  NavigationController::LoadURLParams load_url_params(url);
   load_url_params.referrer = params->referrer;
   load_url_params.transition_type = params->transition;
   load_url_params.extra_headers = params->extra_headers;
@@ -257,6 +258,14 @@
   } else if (params->is_renderer_initiated) {
     load_url_params.is_renderer_initiated = true;
   }
+
+  // Only allows the browser-initiated navigation to use POST.
+  if (params->uses_post && !params->is_renderer_initiated) {
+    load_url_params.load_type =
+        NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
+    load_url_params.browser_initiated_post_data =
+        params->browser_initiated_post_data;
+  }
   target_contents->GetController().LoadURLWithParams(load_url_params);
 }
 
@@ -320,6 +329,8 @@
   if (params.source_contents) {
     create_params.initial_size =
         params.source_contents->GetView()->GetContainerSize();
+    if (params.should_set_opener)
+      create_params.opener = params.source_contents;
   }
 #if defined(USE_AURA)
   if (params.browser->window() &&
@@ -329,7 +340,8 @@
   }
 #endif
 
-  content::WebContents* target_contents = WebContents::Create(create_params);
+  WebContents* target_contents = WebContents::Create(create_params);
+
   // New tabs can have WebUI URLs that will make calls back to arbitrary
   // tab helpers, so the entire set of tab helpers needs to be set up
   // immediately.
@@ -379,6 +391,7 @@
                                const GURL& a_url,
                                content::PageTransition a_transition)
     : url(a_url),
+      uses_post(false),
       target_contents(NULL),
       source_contents(NULL),
       disposition(CURRENT_TAB),
@@ -393,12 +406,14 @@
       browser(a_browser),
       initiating_profile(NULL),
       host_desktop_type(GetHostDesktop(a_browser)),
-      should_replace_current_entry(false) {
+      should_replace_current_entry(false),
+      should_set_opener(false) {
 }
 
 NavigateParams::NavigateParams(Browser* a_browser,
                                WebContents* a_target_contents)
-    : target_contents(a_target_contents),
+    : uses_post(false),
+      target_contents(a_target_contents),
       source_contents(NULL),
       disposition(CURRENT_TAB),
       transition(content::PAGE_TRANSITION_LINK),
@@ -412,13 +427,15 @@
       browser(a_browser),
       initiating_profile(NULL),
       host_desktop_type(GetHostDesktop(a_browser)),
-      should_replace_current_entry(false) {
+      should_replace_current_entry(false),
+      should_set_opener(false) {
 }
 
 NavigateParams::NavigateParams(Profile* a_profile,
                                const GURL& a_url,
                                content::PageTransition a_transition)
     : url(a_url),
+      uses_post(false),
       target_contents(NULL),
       source_contents(NULL),
       disposition(NEW_FOREGROUND_TAB),
@@ -433,7 +450,8 @@
       browser(NULL),
       initiating_profile(a_profile),
       host_desktop_type(chrome::GetActiveDesktop()),
-      should_replace_current_entry(false) {
+      should_replace_current_entry(false),
+      should_set_opener(false) {
 }
 
 NavigateParams::~NavigateParams() {}
@@ -449,6 +467,8 @@
       params.transferred_global_request_id;
   nav_params->should_replace_current_entry =
       params.should_replace_current_entry;
+  nav_params->uses_post = params.uses_post;
+  nav_params->browser_initiated_post_data = params.browser_initiated_post_data;
 }
 
 void Navigate(NavigateParams* params) {
diff --git a/chrome/browser/ui/browser_navigator.h b/chrome/browser/ui/browser_navigator.h
index 1ba38fb..3d84109 100644
--- a/chrome/browser/ui/browser_navigator.h
+++ b/chrome/browser/ui/browser_navigator.h
@@ -7,6 +7,8 @@
 
 #include <string>
 
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_memory.h"
 #include "chrome/browser/ui/host_desktop.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/global_request_id.h"
@@ -63,6 +65,15 @@
   GURL url;
   content::Referrer referrer;
 
+  // Indicates whether this navigation will be sent using POST.
+  // The POST method is limited support for basic POST data by leveraging
+  // NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST.
+  // It is not for things like file uploads.
+  bool uses_post;
+
+  // The post data when the navigation uses POST.
+  scoped_refptr<base::RefCountedMemory> browser_initiated_post_data;
+
   // Extra headers to add to the request for this page.  Headers are
   // represented as "<name>: <value>" and separated by \r\n.  The entire string
   // is terminated by \r\n.  May be empty if no extra headers are needed.
@@ -212,6 +223,10 @@
   // navigation entry.
   bool should_replace_current_entry;
 
+  // Indicates whether |source_contents| should be set as opener when creating
+  // |target_contents|.
+  bool should_set_opener;
+
  private:
   NavigateParams();
 };
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc
index 8cd9080..28424fd 100644
--- a/chrome/browser/ui/browser_navigator_browsertest.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include "base/command_line.h"
 #include "base/prefs/pref_service.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
@@ -32,6 +33,9 @@
 
 namespace {
 
+const char kExpectedTitle[] = "PASSED!";
+const char kEchoTitleCommand[] = "echotitle";
+
 GURL GetGoogleURL() {
   return GURL("http://www.google.com/");
 }
@@ -62,7 +66,7 @@
   return GURL(url_string);
 }
 
-} // namespace
+}  // namespace
 
 chrome::NavigateParams BrowserNavigatorTest::MakeNavigateParams() const {
   return MakeNavigateParams(browser());
@@ -76,6 +80,34 @@
   return params;
 }
 
+bool BrowserNavigatorTest::OpenPOSTURLInNewForegroundTabAndGetTitle(
+    const GURL& url, const std::string& post_data, bool is_browser_initiated,
+    base::string16* title) {
+  chrome::NavigateParams param(MakeNavigateParams());
+  param.disposition = NEW_FOREGROUND_TAB;
+  param.url = url;
+  param.is_renderer_initiated = !is_browser_initiated;
+  param.uses_post = true;
+  param.browser_initiated_post_data = new base::RefCountedStaticMemory(
+      reinterpret_cast<const uint8*>(post_data.data()), post_data.size());
+
+  ui_test_utils::NavigateToURL(&param);
+  if (!param.target_contents)
+    return false;
+
+  // Navigate() should have opened the contents in new foreground tab in the
+  // current Browser.
+  EXPECT_EQ(browser(), param.browser);
+  EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(),
+            param.target_contents);
+  // We should have one window, with one tab.
+  EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+  EXPECT_EQ(2, browser()->tab_strip_model()->count());
+
+  *title = param.target_contents->GetTitle();
+  return true;
+}
+
 Browser* BrowserNavigatorTest::CreateEmptyBrowserForType(Browser::Type type,
                                                          Profile* profile) {
   Browser* browser = new Browser(
@@ -1291,4 +1323,36 @@
   EXPECT_EQ(-1, chrome::GetIndexOfSingletonTab(&singleton_params));
 }
 
-} // namespace
+// This test verifies that browser initiated navigations can send requests
+// using POST.
+IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
+                       SendBrowserInitiatedRequestUsingPOST) {
+  // Uses a test sever to verify POST request.
+  ASSERT_TRUE(test_server()->Start());
+
+  // Open a browser initiated POST request in new foreground tab.
+  string16 expected_title(base::ASCIIToUTF16(kExpectedTitle));
+  std::string post_data = kExpectedTitle;
+  string16 title;
+  ASSERT_TRUE(OpenPOSTURLInNewForegroundTabAndGetTitle(
+      test_server()->GetURL(kEchoTitleCommand), post_data, true, &title));
+  EXPECT_EQ(expected_title, title);
+}
+
+// This test verifies that renderer initiated navigations can NOT send requests
+// using POST.
+IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
+                       SendRendererInitiatedRequestUsingPOST) {
+  // Uses a test sever to verify POST request.
+  ASSERT_TRUE(test_server()->Start());
+
+  // Open a renderer initiated POST request in new foreground tab.
+  string16 expected_title(base::ASCIIToUTF16(kExpectedTitle));
+  std::string post_data = kExpectedTitle;
+  string16 title;
+  ASSERT_TRUE(OpenPOSTURLInNewForegroundTabAndGetTitle(
+      test_server()->GetURL(kEchoTitleCommand), post_data, false, &title));
+  EXPECT_NE(expected_title, title);
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/browser_navigator_browsertest.h b/chrome/browser/ui/browser_navigator_browsertest.h
index 4fbf814..45f507e 100644
--- a/chrome/browser/ui/browser_navigator_browsertest.h
+++ b/chrome/browser/ui/browser_navigator_browsertest.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_BROWSER_NAVIGATOR_BROWSERTEST_H_
 #define CHROME_BROWSER_UI_BROWSER_NAVIGATOR_BROWSERTEST_H_
 
+#include <string>
+
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/browser/notification_types.h"
@@ -42,6 +44,11 @@
                        const content::NotificationSource& source,
                        const content::NotificationDetails& details) OVERRIDE;
 
+  bool OpenPOSTURLInNewForegroundTabAndGetTitle(const GURL& url,
+                                                const std::string& post_data,
+                                                bool is_browser_initiated,
+                                                base::string16* title);
+
   size_t created_tab_contents_count_;
 };
 
diff --git a/chrome/browser/ui/browser_tab_contents.cc b/chrome/browser/ui/browser_tab_contents.cc
index 02c1691..7beb272 100644
--- a/chrome/browser/ui/browser_tab_contents.cc
+++ b/chrome/browser/ui/browser_tab_contents.cc
@@ -55,7 +55,6 @@
 
 #if defined(ENABLE_MANAGED_USERS)
 #include "chrome/browser/managed_mode/managed_mode_navigation_observer.h"
-#include "chrome/browser/managed_mode/managed_user_service.h"
 #endif
 
 #if defined(ENABLE_PRINTING)
@@ -158,10 +157,13 @@
   captive_portal::CaptivePortalTabHelper::CreateForWebContents(web_contents);
 #endif
 
+  if (profile->IsManaged()) {
 #if defined(ENABLE_MANAGED_USERS)
-  if (ManagedUserService::ProfileIsManaged(profile))
     ManagedModeNavigationObserver::CreateForWebContents(web_contents);
+#else
+    NOTREACHED();
 #endif
+  }
 
 #if defined(ENABLE_PRINTING)
   printing::PrintPreviewMessageHandler::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index 2a09364..a9caa99 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -204,7 +204,7 @@
   ShowSettingsSubPage(browser, kSearchEnginesSubPage);
 }
 
-void ShowBrowserSignin(Browser* browser, SyncPromoUI::Source source) {
+void ShowBrowserSignin(Browser* browser, signin::Source source) {
   Profile* original_profile = browser->profile()->GetOriginalProfile();
   SigninManagerBase* manager =
       SigninManagerFactory::GetForProfile(original_profile);
@@ -223,8 +223,7 @@
     }
 
     NavigateToSingletonTab(browser,
-                            GURL(SyncPromoUI::GetSyncPromoURL(source,
-                                                              false)));
+                           GURL(signin::GetPromoURL(source, false)));
     DCHECK_GT(browser->tab_strip_model()->count(), 0);
   }
 }
diff --git a/chrome/browser/ui/chrome_pages.h b/chrome/browser/ui/chrome_pages.h
index 3420c5d..d4174fa 100644
--- a/chrome/browser/ui/chrome_pages.h
+++ b/chrome/browser/ui/chrome_pages.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/common/content_settings_types.h"
 
 class Browser;
@@ -58,7 +58,7 @@
 void ShowSearchEngineSettings(Browser* browser);
 // If the user is already signed in, shows the "Signin" portion of Settings,
 // otherwise initiates signin.
-void ShowBrowserSignin(Browser* browser, SyncPromoUI::Source source);
+void ShowBrowserSignin(Browser* browser, signin::Source source);
 
 // Open a tab to sign into GAIA.
 void ShowGaiaSignin(Browser* browser,
diff --git a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h
index 650cf92..cb2d859 100644
--- a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h
+++ b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h
@@ -7,6 +7,7 @@
 
 #include "base/mac/scoped_nsobject.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/autofill/autofill_dialog_types.h"
 #include "chrome/browser/ui/autofill/autofill_dialog_view.h"
 #include "chrome/browser/ui/autofill/testable_autofill_dialog_view.h"
@@ -79,12 +80,19 @@
 
   AutofillDialogController* controller() { return controller_; }
 
+  // Posts a close request on the current message loop.
   void PerformClose();
 
  private:
+  // Closes the sheet and ends the modal loop. Triggers cleanup sequence.
+  void CloseNow();
+
   scoped_ptr<ConstrainedWindowMac> constrained_window_;
   base::scoped_nsobject<AutofillDialogWindowController> sheet_controller_;
 
+  // WeakPtrFactory for deferred close.
+  base::WeakPtrFactory<AutofillDialogCocoa> close_weak_ptr_factory_;
+
   // The controller |this| queries for logic and state.
   AutofillDialogController* controller_;
 };
@@ -135,9 +143,13 @@
 
 // Mirrors the TestableAutofillDialogView API on the C++ side.
 @interface AutofillDialogWindowController (TestableAutofillDialogView)
+
 - (void)setTextContents:(NSString*)text
                forInput:(const autofill::DetailInput&)input;
+- (void)setTextContents:(NSString*)text
+ ofSuggestionForSection:(autofill::DialogSection)section;
 - (void)activateFieldForInput:(const autofill::DetailInput&)input;
+
 @end
 
 #endif  // CHROME_BROWSER_UI_COCOA_AUTOFILL_AUTOFILL_DIALOG_COCOA_H_
diff --git a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm
index de6b6fc..1967e39 100644
--- a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm
+++ b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm
@@ -4,8 +4,10 @@
 
 #include "chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h"
 
+#include "base/bind.h"
 #include "base/mac/bundle_locations.h"
 #include "base/mac/scoped_nsobject.h"
+#include "base/message_loop/message_loop.h"
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/browser/ui/autofill/autofill_dialog_controller.h"
 #include "chrome/browser/ui/chrome_style.h"
@@ -41,7 +43,8 @@
 }
 
 AutofillDialogCocoa::AutofillDialogCocoa(AutofillDialogController* controller)
-    : controller_(controller) {
+    : close_weak_ptr_factory_(this),
+      controller_(controller) {
 }
 
 AutofillDialogCocoa::~AutofillDialogCocoa() {
@@ -58,15 +61,22 @@
           initWithCustomWindow:[sheet_controller_ window]]);
   constrained_window_.reset(
       new ConstrainedWindowMac(this, controller_->web_contents(), sheet));
-  constrained_window_->SetPreventCloseOnLoadStart(true);
 }
 
 void AutofillDialogCocoa::Hide() {
   [sheet_controller_ hide];
 }
 
-// Closes the sheet and ends the modal loop. Triggers cleanup sequence.
 void AutofillDialogCocoa::PerformClose() {
+  if (!close_weak_ptr_factory_.HasWeakPtrs()) {
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(&AutofillDialogCocoa::CloseNow,
+                   close_weak_ptr_factory_.GetWeakPtr()));
+  }
+}
+
+void AutofillDialogCocoa::CloseNow() {
   constrained_window_->CloseWebContentsModalDialog();
 }
 
@@ -165,7 +175,8 @@
 void AutofillDialogCocoa::SetTextContentsOfSuggestionInput(
     DialogSection section,
     const base::string16& text) {
-  // TODO(groby): Implement Mac support for this: http://crbug.com/256864
+  [sheet_controller_ setTextContents:base::SysUTF16ToNSString(text)
+              ofSuggestionForSection:section];
 }
 
 void AutofillDialogCocoa::ActivateInput(const DetailInput& input) {
@@ -405,6 +416,11 @@
   }
 }
 
+- (void)setTextContents:(NSString*)text
+ ofSuggestionForSection:(autofill::DialogSection)section {
+  [[mainContainer_ sectionForId:section] setSuggestionFieldValue:text];
+}
+
 - (void)activateFieldForInput:(const autofill::DetailInput&)input {
   for (size_t i = autofill::SECTION_MIN; i <= autofill::SECTION_MAX; ++i) {
     autofill::DialogSection section = static_cast<autofill::DialogSection>(i);
diff --git a/chrome/browser/ui/cocoa/autofill/autofill_section_container.h b/chrome/browser/ui/cocoa/autofill/autofill_section_container.h
index e192919..0e0caf3 100644
--- a/chrome/browser/ui/cocoa/autofill/autofill_section_container.h
+++ b/chrome/browser/ui/cocoa/autofill/autofill_section_container.h
@@ -93,6 +93,9 @@
 - (void)setFieldValue:(NSString*)text
               forInput:(const autofill::DetailInput&)input;
 
+// Sets the value for the suggestion text field.
+- (void)setSuggestionFieldValue:(NSString*)text;
+
 // Activates a given input field, determined by |input|. Does nothing if the
 // field is not part of this section.
 - (void)activateFieldForInput:(const autofill::DetailInput&)input;
diff --git a/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm b/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm
index 56b1c7d..08ab7d0 100644
--- a/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm
+++ b/chrome/browser/ui/cocoa/autofill/autofill_section_container.mm
@@ -618,6 +618,10 @@
   [field setFieldValue:text];
 }
 
+- (void)setSuggestionFieldValue:(NSString*)text {
+  [[suggestContainer_ inputField] setFieldValue:text];
+}
+
 - (void)activateFieldForInput:(const autofill::DetailInput&)input {
   if ([self detailInputForType:input.type] != &input)
     return;
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller.mm
index 3886719..f46c89a 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller.mm
@@ -4,7 +4,6 @@
 
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller.h"
 
-#include "base/command_line.h"
 #include "base/mac/bundle_locations.h"
 #include "base/mac/mac_util.h"
 #include "base/strings/sys_string_conversions.h"
@@ -17,7 +16,6 @@
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
 #include "chrome/browser/ui/sync/sync_promo_ui.h"
-#include "chrome/common/chrome_switches.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/notification_service.h"
@@ -75,9 +73,7 @@
   [[nameTextField_ cell] setUsesSingleLineMode:YES];
 
   Browser* browser = chrome::FindBrowserWithWindow(self.parentWindow);
-  if (SyncPromoUI::ShouldShowSyncPromo(browser->profile()) &&
-      CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableBookmarkSyncPromo)) {
+  if (SyncPromoUI::ShouldShowSyncPromo(browser->profile())) {
     syncPromoController_.reset(
         [[BookmarkSyncPromoController alloc] initWithBrowser:browser]);
     [syncPromoPlaceholder_ addSubview:[syncPromoController_ view]];
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm
index bd8e4ff..5333af2 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm
@@ -5,7 +5,6 @@
 #import <Cocoa/Cocoa.h>
 
 #include "base/basictypes.h"
-#include "base/command_line.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
@@ -18,7 +17,6 @@
 #include "chrome/browser/ui/cocoa/browser_window_controller.h"
 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
-#include "chrome/common/chrome_switches.h"
 #include "content/public/browser/notification_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
@@ -75,12 +73,6 @@
     edits_ = 0;
   }
 
-  virtual void SetUp() OVERRIDE {
-    CocoaProfileTest::SetUp();
-    CommandLine* command_line = CommandLine::ForCurrentProcess();
-    command_line->AppendSwitch(switches::kEnableBookmarkSyncPromo);
-  }
-
   virtual void TearDown() OVERRIDE {
     [controller_ close];
     CocoaProfileTest::TearDown();
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_sync_promo_controller.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_sync_promo_controller.mm
index ce2ee47..dc1fe93 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_sync_promo_controller.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_sync_promo_controller.mm
@@ -5,10 +5,10 @@
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_sync_promo_controller.h"
 
 #include "base/strings/sys_string_conversions.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/chrome_style.h"
 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "grit/generated_resources.h"
 #include "skia/ext/skia_utils_mac.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -108,7 +108,7 @@
 - (BOOL)textView:(NSTextView *)textView
    clickedOnLink:(id)link
          atIndex:(NSUInteger)charIndex {
-  chrome::ShowBrowserSignin(browser_, SyncPromoUI::SOURCE_BOOKMARK_BUBBLE);
+  chrome::ShowBrowserSignin(browser_, signin::SOURCE_BOOKMARK_BUBBLE);
   return YES;
 }
 
diff --git a/chrome/browser/ui/cocoa/browser/avatar_button_controller.mm b/chrome/browser/ui/cocoa/browser/avatar_button_controller.mm
index 2428978..471b64d 100644
--- a/chrome/browser/ui/cocoa/browser/avatar_button_controller.mm
+++ b/chrome/browser/ui/cocoa/browser/avatar_button_controller.mm
@@ -9,7 +9,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/command_updater.h"
-#include "chrome/browser/managed_mode/managed_user_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_info_cache.h"
 #include "chrome/browser/profiles/profile_info_util.h"
@@ -152,7 +151,7 @@
 
       // Managed users cannot enter incognito mode, so we only need to check
       // it in this code path.
-      if (ManagedUserService::ProfileIsManaged(profile)) {
+      if (profile->IsManaged()) {
         labelButton_.reset([[NSButton alloc] initWithFrame:NSZeroRect]);
         [labelButton_ setButtonType:NSMomentaryLightButton];
         [labelButton_ setBezelStyle:NSRecessedBezelStyle];
diff --git a/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller_unittest.mm b/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller_unittest.mm
index b80a2a8..6c2dbd7 100644
--- a/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller_unittest.mm
@@ -30,9 +30,9 @@
     ASSERT_TRUE(manager_.SetUp());
 
     manager_.CreateTestingProfile("test1", scoped_ptr<PrefServiceSyncable>(),
-                                  ASCIIToUTF16("Test 1"), 1);
+                                  ASCIIToUTF16("Test 1"), 1, false);
     manager_.CreateTestingProfile("test2", scoped_ptr<PrefServiceSyncable>(),
-                                  ASCIIToUTF16("Test 2"), 0);
+                                  ASCIIToUTF16("Test 2"), 0, false);
 
     model_ = new AvatarMenuModel(manager_.profile_info_cache(), NULL, NULL);
 
@@ -121,7 +121,7 @@
 
   // Now create a new profile and notify the delegate.
   manager()->CreateTestingProfile("test3", scoped_ptr<PrefServiceSyncable>(),
-                                  ASCIIToUTF16("Test 3"), 0);
+                                  ASCIIToUTF16("Test 3"), 0, false);
 
   // Testing the bridge is not worth the effort...
   [controller() performLayout];
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_private.mm b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
index 382e28a..55cb3f7 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_private.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
@@ -234,8 +234,12 @@
 
   // If in presentation mode, reset |maxY| to top of screen, so that the
   // floating bar slides over the things which appear to be in the content area.
-  if (inPresentationMode || !fullscreenUrl_.is_empty())
+  BOOL useSimplifiedFullscreen = CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableSimplifiedFullscreen);
+  if (inPresentationMode ||
+      (useSimplifiedFullscreen && !fullscreenUrl_.is_empty())) {
     maxY = NSMaxY(contentBounds);
+  }
 
   // Also place the info bar container immediate below the toolbar, except in
   // presentation mode in which case it's at the top of the visual content area.
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h
index d6f0715..73202e8 100644
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h
+++ b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h
@@ -41,8 +41,6 @@
   void PulseWebContentsModalDialog();
   web_modal::NativeWebContentsModalDialog GetNativeDialog();
 
-  void SetPreventCloseOnLoadStart(bool prevent);
-
  private:
   // Gets the parent window of the dialog.
   NSWindow* GetParentWindow() const;
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm
index 49d753f..df302df 100644
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm
+++ b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.mm
@@ -78,14 +78,6 @@
   return this;
 }
 
-void ConstrainedWindowMac::SetPreventCloseOnLoadStart(bool prevent) {
-  WebContentsModalDialogManager* web_contents_modal_dialog_manager =
-      WebContentsModalDialogManager::FromWebContents(web_contents_);
-  web_contents_modal_dialog_manager->SetPreventCloseOnLoadStart(
-      GetNativeDialog(),
-      prevent);
-}
-
 NSWindow* ConstrainedWindowMac::GetParentWindow() const {
   // Tab contents in a tabbed browser may not be inside a window. For this
   // reason use a browser window if possible.
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
index 51e4bee..9e2cb81 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
@@ -196,7 +196,7 @@
   // No warnings should trigger skinny prompt.
   ExtensionInstallPrompt::Prompt inline_prompt(
       ExtensionInstallPrompt::INLINE_INSTALL_PROMPT);
-  inline_prompt.SetInlineInstallWebstoreData("1,000", 3.5, 200);
+  inline_prompt.SetInlineInstallWebstoreData("1,000", true, 3.5, 200);
   inline_prompt.set_extension(extension_.get());
   inline_prompt.set_icon(chrome::LoadInstallPromptIcon());
 
diff --git a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm
index 55b9db3..735420f 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm
@@ -15,6 +15,7 @@
 #include "chrome/browser/extensions/extension_action.h"
 #include "chrome/browser/extensions/extension_action_manager.h"
 #include "chrome/browser/extensions/extension_install_ui.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -201,8 +202,7 @@
          atIndex:(NSUInteger)charIndex {
   DCHECK_EQ(promo_.get(), aTextView);
   GURL promo_url =
-      SyncPromoUI::GetSyncPromoURL(
-          SyncPromoUI::SOURCE_EXTENSION_INSTALL_BUBBLE, false);
+      signin::GetPromoURL(signin::SOURCE_EXTENSION_INSTALL_BUBBLE, false);
   chrome::NavigateParams params(
       chrome::GetSingletonTabNavigateParams(browser_, promo_url));
   chrome::Navigate(&params);
diff --git a/chrome/browser/ui/cocoa/extensions/native_app_window_cocoa.h b/chrome/browser/ui/cocoa/extensions/native_app_window_cocoa.h
index b9072a2..1eefbfb 100644
--- a/chrome/browser/ui/cocoa/extensions/native_app_window_cocoa.h
+++ b/chrome/browser/ui/cocoa/extensions/native_app_window_cocoa.h
@@ -138,7 +138,12 @@
     return shell_window_->extension();
   }
 
+  // Returns the WindowStyleMask based on the type of window frame.
+  // Specifically, this includes NSResizableWindowMask if the window is
+  // resizable, and does not include NSTexturedBackgroundWindowMask when a
+  // native frame is used.
   NSUInteger GetWindowStyleMask() const;
+
   void InstallView();
   void UninstallView();
   void InstallDraggableRegionViews();
diff --git a/chrome/browser/ui/cocoa/first_run_dialog.mm b/chrome/browser/ui/cocoa/first_run_dialog.mm
index bb0e4e0..6fc2b67 100644
--- a/chrome/browser/ui/cocoa/first_run_dialog.mm
+++ b/chrome/browser/ui/cocoa/first_run_dialog.mm
@@ -121,8 +121,6 @@
   // We don't show the dialog in Chromium.
 #endif  // GOOGLE_CHROME_BUILD
 
-  first_run::CreateSentinel();
-
   // Set preference to show first run bubble and welcome page.
   // Only display the bubble if there is a default search provider.
   TemplateURLService* search_engines_model =
diff --git a/chrome/browser/ui/cocoa/location_bar/page_action_decoration.mm b/chrome/browser/ui/cocoa/location_bar/page_action_decoration.mm
index b885b80..74f86ee 100644
--- a/chrome/browser/ui/cocoa/location_bar/page_action_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/page_action_decoration.mm
@@ -144,7 +144,9 @@
       break;
 
     case LocationBarController::ACTION_SHOW_SCRIPT_POPUP:
-      ShowPopup(frame, ExtensionInfoUI::GetURL(page_action_->extension_id()));
+      ShowPopup(
+          frame,
+          extensions::ExtensionInfoUI::GetURL(page_action_->extension_id()));
       break;
   }
 
diff --git a/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge.mm b/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge.mm
index 405aabf..2ece534 100644
--- a/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge.mm
+++ b/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge.mm
@@ -76,6 +76,7 @@
   [status_item_view_ setHighlight:NO];
   [tray_controller_ close];
   tray_controller_.autorelease();
+  UpdateStatusItem();
 }
 
 bool MessageCenterTrayBridge::ShowNotifierSettings() {
@@ -85,30 +86,27 @@
 }
 
 void MessageCenterTrayBridge::UpdateStatusItem() {
-  // Only show the status item if there are notifications.
-  if (message_center_->NotificationCount() == 0) {
-    [status_item_view_ removeItem];
-    status_item_view_.reset();
-    return;
-  }
-
   if (!status_item_view_) {
     status_item_view_.reset([[MCStatusItemView alloc] init]);
     [status_item_view_ setCallback:^{ tray_->ToggleMessageCenterBubble(); }];
   }
 
-  size_t unread_count = message_center_->UnreadNotificationCount();
-  [status_item_view_ setUnreadCount:unread_count];
+  // We want a static message center icon while it's visible.
+  if (message_center()->IsMessageCenterVisible())
+    return;
 
-  string16 product_name = l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME);
+  size_t unread_count = message_center_->UnreadNotificationCount();
+  bool quiet_mode = message_center_->IsQuietMode();
+  [status_item_view_ setUnreadCount:unread_count withQuietMode:quiet_mode];
+
   if (unread_count > 0) {
     string16 unread_count_string = base::FormatNumber(unread_count);
     [status_item_view_ setToolTip:
         l10n_util::GetNSStringF(IDS_MESSAGE_CENTER_TOOLTIP_UNREAD,
-            product_name, unread_count_string)];
+            unread_count_string)];
   } else {
     [status_item_view_ setToolTip:
-        l10n_util::GetNSStringF(IDS_MESSAGE_CENTER_TOOLTIP, product_name)];
+        l10n_util::GetNSString(IDS_MESSAGE_CENTER_TOOLTIP)];
   }
 }
 
diff --git a/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge_unittest.mm b/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge_unittest.mm
index a0c6cb0..57008c0 100644
--- a/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge_unittest.mm
+++ b/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge_unittest.mm
@@ -38,7 +38,7 @@
   scoped_ptr<MessageCenterTrayBridge> bridge_;
 };
 
-TEST_F(MessageCenterTrayBridgeTest, StatusItemOnlyWithNotifications) {
+TEST_F(MessageCenterTrayBridgeTest, StatusItemOnlyAfterFirstNotification) {
   EXPECT_FALSE(status_item());
 
   message_center::RichNotificationData data;
@@ -65,5 +65,5 @@
 
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_FALSE(status_item());
+  EXPECT_TRUE(status_item());
 }
diff --git a/chrome/browser/ui/cocoa/profile_menu_controller.mm b/chrome/browser/ui/cocoa/profile_menu_controller.mm
index f1ee54e..cdccc9e 100644
--- a/chrome/browser/ui/cocoa/profile_menu_controller.mm
+++ b/chrome/browser/ui/cocoa/profile_menu_controller.mm
@@ -138,6 +138,25 @@
   return YES;
 }
 
+- (BOOL)validateMenuItem:(NSMenuItem*)menuItem {
+  size_t activeProfileIndex = model_->GetActiveProfileIndex();
+  ProfileInfoCache* cache =
+      &g_browser_process->profile_manager()->GetProfileInfoCache();
+  BOOL profileIsManaged = cache->ProfileIsManagedAtIndex(activeProfileIndex);
+  if ([menuItem action] == @selector(switchToProfileFromDock:) ||
+      [menuItem action] == @selector(switchToProfileFromMenu:)) {
+    if (!profileIsManaged)
+      return YES;
+
+    return [menuItem tag] == static_cast<NSInteger>(activeProfileIndex);
+  }
+
+  if ([menuItem action] == @selector(newProfile:))
+    return !profileIsManaged;
+
+  return YES;
+}
+
 // Private /////////////////////////////////////////////////////////////////////
 
 - (NSMenu*)menu {
@@ -153,10 +172,9 @@
 
   [[self menu] addItem:[NSMenuItem separatorItem]];
 
-  NSMenuItem* item =
-      [self createItemWithTitle:l10n_util::GetNSStringWithFixup(
-                                    IDS_PROFILES_CUSTOMIZE_PROFILE)
-                         action:@selector(editProfile:)];
+  NSMenuItem* item = [self createItemWithTitle:
+          l10n_util::GetNSStringWithFixup(IDS_PROFILES_CUSTOMIZE_PROFILE)
+                                        action:@selector(editProfile:)];
   [[self menu] addItem:item];
 
   [[self menu] addItem:[NSMenuItem separatorItem]];
diff --git a/chrome/browser/ui/cocoa/profile_menu_controller_unittest.mm b/chrome/browser/ui/cocoa/profile_menu_controller_unittest.mm
index a0b001c..879b1de 100644
--- a/chrome/browser/ui/cocoa/profile_menu_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/profile_menu_controller_unittest.mm
@@ -5,8 +5,9 @@
 #import "chrome/browser/ui/cocoa/profile_menu_controller.h"
 
 #include "base/mac/scoped_nsobject.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_restrictions.h"
-#include "chrome/browser/profiles/avatar_menu_model.h"
+#include "chrome/browser/prefs/pref_service_syncable.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
@@ -241,3 +242,46 @@
   base::ThreadRestrictions::SetIOAllowed(io_was_allowed);
 }
 
+TEST_F(ProfileMenuControllerTest, ManagedProfile) {
+  TestingProfileManager* manager = testing_profile_manager();
+  TestingProfile* managed_profile =
+      manager->CreateTestingProfile("test1",
+                                    scoped_ptr<PrefServiceSyncable>(),
+                                    ASCIIToUTF16("Supervised User"),
+                                    0,
+                                    true);
+  BrowserList::SetLastActive(browser());
+
+  NSMenu* menu = [controller() menu];
+  ASSERT_EQ(6, [menu numberOfItems]);
+  NSMenuItem* item = [menu itemAtIndex:0];
+  ASSERT_EQ(@selector(switchToProfileFromMenu:), [item action]);
+  EXPECT_TRUE([controller() validateMenuItem:item]);
+
+  item = [menu itemAtIndex:1];
+  ASSERT_EQ(@selector(switchToProfileFromMenu:), [item action]);
+  EXPECT_TRUE([controller() validateMenuItem:item]);
+
+  item = [menu itemAtIndex:5];
+  ASSERT_EQ(@selector(newProfile:), [item action]);
+  EXPECT_TRUE([controller() validateMenuItem:item]);
+
+  // Open a new browser for the managed user and switch to it.
+  Browser::CreateParams managed_profile_params(
+      managed_profile, chrome::HOST_DESKTOP_TYPE_NATIVE);
+  scoped_ptr<Browser> managed_browser(
+      chrome::CreateBrowserWithTestWindowForParams(&managed_profile_params));
+  BrowserList::SetLastActive(managed_browser.get());
+
+  item = [menu itemAtIndex:0];
+  ASSERT_EQ(@selector(switchToProfileFromMenu:), [item action]);
+  EXPECT_FALSE([controller() validateMenuItem:item]);
+
+  item = [menu itemAtIndex:1];
+  ASSERT_EQ(@selector(switchToProfileFromMenu:), [item action]);
+  EXPECT_TRUE([controller() validateMenuItem:item]);
+
+  item = [menu itemAtIndex:5];
+  ASSERT_EQ(@selector(newProfile:), [item action]);
+  EXPECT_FALSE([controller() validateMenuItem:item]);
+}
diff --git a/chrome/browser/ui/cocoa/status_icons/status_tray_mac.h b/chrome/browser/ui/cocoa/status_icons/status_tray_mac.h
index 2bb19c4..7325d86 100644
--- a/chrome/browser/ui/cocoa/status_icons/status_tray_mac.h
+++ b/chrome/browser/ui/cocoa/status_icons/status_tray_mac.h
@@ -14,7 +14,10 @@
 
  protected:
   // Factory method for creating a status icon.
-  virtual StatusIcon* CreatePlatformStatusIcon(StatusIconType type) OVERRIDE;
+  virtual StatusIcon* CreatePlatformStatusIcon(
+      StatusIconType type,
+      const gfx::ImageSkia& image,
+      const string16& tool_tip) OVERRIDE;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(StatusTrayMac);
diff --git a/chrome/browser/ui/cocoa/status_icons/status_tray_mac.mm b/chrome/browser/ui/cocoa/status_icons/status_tray_mac.mm
index 7a4324d..50dc797 100644
--- a/chrome/browser/ui/cocoa/status_icons/status_tray_mac.mm
+++ b/chrome/browser/ui/cocoa/status_icons/status_tray_mac.mm
@@ -13,6 +13,11 @@
 StatusTrayMac::StatusTrayMac() {
 }
 
-StatusIcon* StatusTrayMac::CreatePlatformStatusIcon(StatusIconType type) {
-  return new StatusIconMac();
+StatusIcon* StatusTrayMac::CreatePlatformStatusIcon(StatusIconType type,
+                                                    const gfx::ImageSkia& image,
+                                                    const string16& tool_tip) {
+  StatusIcon* icon = new StatusIconMac();
+  icon->SetImage(image);
+  icon->SetToolTip(tool_tip);
+  return icon;
 }
diff --git a/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.h b/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.h
index 85aea22..4ef6fc4 100644
--- a/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.h
+++ b/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.h
@@ -38,14 +38,15 @@
   virtual void AcceptTabModalDialog() OVERRIDE;
   virtual void CancelTabModalDialog() OVERRIDE;
 
-  // TabModalConfirmDialogOperationsDelegate:
+  // TabModalConfirmDialogCloseDelegate:
   virtual void CloseDialog() OVERRIDE;
-  virtual void SetPreventCloseOnLoadStart(bool prevent) OVERRIDE;
 
   // ConstrainedWindowMacDelegate:
   virtual void OnConstrainedWindowClosed(
       ConstrainedWindowMac* window) OVERRIDE;
 
+  bool closing_;
+
   scoped_ptr<ConstrainedWindowMac> window_;
   scoped_ptr<TabModalConfirmDialogDelegate> delegate_;
   base::scoped_nsobject<ConstrainedWindowAlert> alert_;
diff --git a/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.mm b/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.mm
index a006fe9..40a949a 100644
--- a/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.mm
+++ b/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.mm
@@ -57,7 +57,8 @@
 TabModalConfirmDialogMac::TabModalConfirmDialogMac(
     TabModalConfirmDialogDelegate* delegate,
     content::WebContents* web_contents)
-    : delegate_(delegate) {
+    : closing_(false),
+      delegate_(delegate) {
   bridge_.reset([[TabModalConfirmDialogMacBridge alloc]
       initWithDelegate:delegate]);
 
@@ -88,7 +89,7 @@
       [[CustomConstrainedWindowSheet alloc]
           initWithCustomWindow:[alert_ window]]);
   window_.reset(new ConstrainedWindowMac(this, web_contents, sheet));
-  delegate_->set_operations_delegate(this);
+  delegate_->set_close_delegate(this);
 }
 
 TabModalConfirmDialogMac::~TabModalConfirmDialogMac() {
@@ -103,14 +104,16 @@
 }
 
 void TabModalConfirmDialogMac::CloseDialog() {
-  window_->CloseWebContentsModalDialog();
-}
-
-void TabModalConfirmDialogMac::SetPreventCloseOnLoadStart(bool prevent) {
-  window_->SetPreventCloseOnLoadStart(prevent);
+  if (!closing_) {
+    closing_ = true;
+    window_->CloseWebContentsModalDialog();
+  }
 }
 
 void TabModalConfirmDialogMac::OnConstrainedWindowClosed(
     ConstrainedWindowMac* window) {
+  // Provide a disposition in case the dialog was closed without accepting or
+  // cancelling.
+  delegate_->Close();
   delete this;
 }
diff --git a/chrome/browser/ui/cocoa/view_id_util_browsertest.mm b/chrome/browser/ui/cocoa/view_id_util_browsertest.mm
index 6fcafbc..2d9c026 100644
--- a/chrome/browser/ui/cocoa/view_id_util_browsertest.mm
+++ b/chrome/browser/ui/cocoa/view_id_util_browsertest.mm
@@ -15,11 +15,11 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/cocoa/view_id_util.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "extensions/common/switches.h"
 
 using content::OpenURLParams;
 using content::Referrer;
@@ -29,7 +29,7 @@
  public:
   ViewIDTest() : root_window_(nil) {
     CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kEnableExperimentalExtensionApis);
+        extensions::switches::kEnableExperimentalExtensionApis);
   }
 
   void CheckViewID(ViewID view_id, bool should_have) {
diff --git a/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.cc b/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.cc
index 114e8fa..b78d7e1 100644
--- a/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.cc
+++ b/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.cc
@@ -193,11 +193,9 @@
 
   gtk_util::SetLayoutText(layout_, text);
 
-  // We add one pixel to the width because if the text fills up the width
-  // pango will try to split it over 2 lines.
-  int required_width = font.GetStringWidth(text) + 1;
-
-  pango_layout_set_width(layout_, required_width * PANGO_SCALE);
+  // The popup is already the correct size for the text, so set the width to -1
+  // to prevent additional wrapping or ellipsization.
+  pango_layout_set_width(layout_, -1);
 }
 
 void AutofillPopupViewGtk::DrawSeparator(cairo_t* cairo_context,
diff --git a/chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.cc b/chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.cc
index c74edca..f252a83 100644
--- a/chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.cc
+++ b/chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.cc
@@ -8,7 +8,6 @@
 
 #include "base/basictypes.h"
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/i18n/rtl.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
@@ -19,6 +18,7 @@
 #include "chrome/browser/bookmarks/bookmark_utils.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
 #include "chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h"
@@ -29,7 +29,6 @@
 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
 #include "chrome/browser/ui/gtk/gtk_util.h"
 #include "chrome/browser/ui/sync/sync_promo_ui.h"
-#include "chrome/common/chrome_switches.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/browser/user_metrics.h"
 #include "grit/generated_resources.h"
@@ -214,9 +213,7 @@
 
   gtk_box_pack_start(GTK_BOX(bubble_container), content, TRUE, TRUE, 0);
 
-  const CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableBookmarkSyncPromo) &&
-      SyncPromoUI::ShouldShowSyncPromo(profile_)) {
+  if (SyncPromoUI::ShouldShowSyncPromo(profile_)) {
     std::string link_text =
         l10n_util::GetStringUTF8(IDS_BOOKMARK_SYNC_PROMO_LINK);
     char* link_markup = g_markup_printf_escaped(kPromoLinkMarkup,
@@ -350,7 +347,7 @@
 gboolean BookmarkBubbleGtk::OnSignInClicked(GtkWidget* widget, gchar* uri) {
   GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(anchor_));
   Browser* browser = chrome::FindBrowserWithWindow(window);
-  chrome::ShowBrowserSignin(browser, SyncPromoUI::SOURCE_BOOKMARK_BUBBLE);
+  chrome::ShowBrowserSignin(browser, signin::SOURCE_BOOKMARK_BUBBLE);
   bubble_->Close();
   return TRUE;
 }
diff --git a/chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk_browsertest.cc b/chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk_browsertest.cc
index f1fb093..7d8ce36 100644
--- a/chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk_browsertest.cc
+++ b/chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk_browsertest.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.h"
 
-#include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
@@ -14,7 +13,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/web_contents.h"
@@ -29,10 +27,6 @@
   BookmarkBubbleGtkBrowserTest() {}
 
   // content::BrowserTestBase:
-  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
-    command_line->AppendSwitch(switches::kEnableBookmarkSyncPromo);
-  }
-
   virtual void SetUpOnMainThread() OVERRIDE {
     bookmark_utils::AddIfNotBookmarked(
         BookmarkModelFactory::GetForProfile(browser()->profile()),
diff --git a/chrome/browser/ui/gtk/bookmarks/bookmark_sub_menu_model_gtk.cc b/chrome/browser/ui/gtk/bookmarks/bookmark_sub_menu_model_gtk.cc
index 036f988..e1de7ed 100644
--- a/chrome/browser/ui/gtk/bookmarks/bookmark_sub_menu_model_gtk.cc
+++ b/chrome/browser/ui/gtk/bookmarks/bookmark_sub_menu_model_gtk.cc
@@ -17,7 +17,7 @@
 #include "chrome/browser/ui/gtk/menu_gtk.h"
 #include "grit/generated_resources.h"
 #include "grit/theme_resources.h"
-#include "ui/base/gtk/menu_label_accelerator_util.h"
+#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/window_open_disposition.h"
diff --git a/chrome/browser/ui/gtk/browser_titlebar.cc b/chrome/browser/ui/gtk/browser_titlebar.cc
index f59fa66..3400691 100644
--- a/chrome/browser/ui/gtk/browser_titlebar.cc
+++ b/chrome/browser/ui/gtk/browser_titlebar.cc
@@ -20,7 +20,6 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/managed_mode/managed_user_service.h"
 #include "chrome/browser/profiles/avatar_menu_model.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_info_cache.h"
@@ -818,7 +817,7 @@
   gtk_widget_show_all(avatar_);
 
   Profile* profile = browser_window_->browser()->profile();
-  if (ManagedUserService::ProfileIsManaged(profile)) {
+  if (profile->IsManaged()) {
     avatar_label_ = gtk_label_new(NULL);
     gtk_misc_set_padding(GTK_MISC(avatar_label_), 10, 2);
     avatar_label_bg_ = gtk_event_box_new();
diff --git a/chrome/browser/ui/gtk/browser_window_gtk.cc b/chrome/browser/ui/gtk/browser_window_gtk.cc
index 8e987ea..0b43d96 100644
--- a/chrome/browser/ui/gtk/browser_window_gtk.cc
+++ b/chrome/browser/ui/gtk/browser_window_gtk.cc
@@ -708,7 +708,7 @@
     return is_active_;
 
   // This still works even though we don't get the activation notification.
-  return gtk_window_is_active(window_);
+  return window_ && gtk_window_is_active(window_);
 }
 
 void BrowserWindowGtk::FlashFrame(bool flash) {
diff --git a/chrome/browser/ui/gtk/bubble/bubble_gtk.cc b/chrome/browser/ui/gtk/bubble/bubble_gtk.cc
index 951f8a1..ed39e43 100644
--- a/chrome/browser/ui/gtk/bubble/bubble_gtk.cc
+++ b/chrome/browser/ui/gtk/bubble/bubble_gtk.cc
@@ -741,7 +741,11 @@
                                  GdkEventGrabBroken* grab_broken) {
   // |grab_input_| may have been changed to false.
   if (!grab_input_)
-    return false;
+    return FALSE;
+
+  // |grab_window| can be NULL.
+  if (!grab_broken->grab_window)
+    return FALSE;
 
   gpointer user_data;
   gdk_window_get_user_data(grab_broken->grab_window, &user_data);
diff --git a/chrome/browser/ui/gtk/certificate_viewer_gtk.cc b/chrome/browser/ui/gtk/certificate_viewer_gtk.cc
index f0b8672..d176036 100644
--- a/chrome/browser/ui/gtk/certificate_viewer_gtk.cc
+++ b/chrome/browser/ui/gtk/certificate_viewer_gtk.cc
@@ -19,8 +19,8 @@
 #include "chrome/common/net/x509_certificate_model.h"
 #include "grit/generated_resources.h"
 #include "net/cert/x509_certificate.h"
+#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
 #include "ui/base/gtk/gtk_hig_constants.h"
-#include "ui/base/gtk/menu_label_accelerator_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/pango_util.h"
diff --git a/chrome/browser/ui/gtk/first_run_dialog.cc b/chrome/browser/ui/gtk/first_run_dialog.cc
index f28925c..eca4ef2 100644
--- a/chrome/browser/ui/gtk/first_run_dialog.cc
+++ b/chrome/browser/ui/gtk/first_run_dialog.cc
@@ -160,9 +160,6 @@
   if (dialog_)
     gtk_widget_hide_all(dialog_);
 
-  // Mark that first run has ran.
-  first_run::CreateSentinel();
-
   // Check if user has opted into reporting.
   if (report_crashes_ &&
       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(report_crashes_))) {
diff --git a/chrome/browser/ui/gtk/global_history_menu.cc b/chrome/browser/ui/gtk/global_history_menu.cc
index e8e283a..0054641 100644
--- a/chrome/browser/ui/gtk/global_history_menu.cc
+++ b/chrome/browser/ui/gtk/global_history_menu.cc
@@ -95,6 +95,7 @@
 GlobalHistoryMenu::GlobalHistoryMenu(Browser* browser)
     : browser_(browser),
       profile_(browser_->profile()),
+      history_menu_(NULL),
       top_sites_(NULL),
       weak_ptr_factory_(this),
       tab_restore_service_(NULL) {
@@ -107,11 +108,17 @@
   STLDeleteContainerPairSecondPointers(menu_item_history_map_.begin(),
                                        menu_item_history_map_.end());
   menu_item_history_map_.clear();
+
+  if (history_menu_) {
+    gtk_widget_destroy(history_menu_);
+    g_object_unref(history_menu_);
+  }
 }
 
 void GlobalHistoryMenu::Init(GtkWidget* history_menu,
                              GtkWidget* history_menu_item) {
-  history_menu_.Own(history_menu);
+  history_menu_ = history_menu;
+  g_object_ref_sink(history_menu_);
 
   // We have to connect to |history_menu_item|'s "activate" signal instead of
   // |history_menu|'s "show" signal because we are not supposed to modify the
@@ -142,10 +149,10 @@
 
 void GlobalHistoryMenu::OnTopSitesReceived(
     const history::MostVisitedURLList& visited_list) {
-  ClearMenuSection(history_menu_.get(), GlobalMenuBar::TAG_MOST_VISITED);
+  ClearMenuSection(history_menu_, GlobalMenuBar::TAG_MOST_VISITED);
 
   int index = GetIndexOfMenuItemWithTag(
-      history_menu_.get(),
+      history_menu_,
       GlobalMenuBar::TAG_MOST_VISITED_HEADER) + 1;
 
   for (size_t i = 0; i < visited_list.size() && i < kMostVisitedCount; ++i) {
@@ -158,7 +165,7 @@
     item->url = visited.url;
 
     AddHistoryItemToMenu(item,
-                         history_menu_.get(),
+                         history_menu_,
                          GlobalMenuBar::TAG_MOST_VISITED,
                          index++);
   }
@@ -283,12 +290,12 @@
 void GlobalHistoryMenu::TabRestoreServiceChanged(TabRestoreService* service) {
   const TabRestoreService::Entries& entries = service->entries();
 
-  ClearMenuSection(history_menu_.get(), GlobalMenuBar::TAG_RECENTLY_CLOSED);
+  ClearMenuSection(history_menu_, GlobalMenuBar::TAG_RECENTLY_CLOSED);
 
   // We'll get the index the "Recently Closed" header. (This can vary depending
   // on the number of "Most Visited" items.
   int index = GetIndexOfMenuItemWithTag(
-      history_menu_.get(),
+      history_menu_,
       GlobalMenuBar::TAG_RECENTLY_CLOSED_HEADER) + 1;
 
   unsigned int added_count = 0;
@@ -355,14 +362,14 @@
                         GINT_TO_POINTER(GlobalMenuBar::TAG_RECENTLY_CLOSED));
       gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), submenu);
 
-      gtk_menu_shell_insert(GTK_MENU_SHELL(history_menu_.get()), parent_item,
+      gtk_menu_shell_insert(GTK_MENU_SHELL(history_menu_), parent_item,
                             index++);
       ++added_count;
     } else if (entry->type == TabRestoreService::TAB) {
       TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
       HistoryItem* item = HistoryItemForTab(*tab);
       AddHistoryItemToMenu(item,
-                           history_menu_.get(),
+                           history_menu_,
                            GlobalMenuBar::TAG_RECENTLY_CLOSED,
                            index++);
       ++added_count;
diff --git a/chrome/browser/ui/gtk/global_history_menu.h b/chrome/browser/ui/gtk/global_history_menu.h
index 7409ed6..74153ab 100644
--- a/chrome/browser/ui/gtk/global_history_menu.h
+++ b/chrome/browser/ui/gtk/global_history_menu.h
@@ -16,7 +16,6 @@
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "ui/base/gtk/gtk_signal.h"
-#include "ui/base/gtk/owned_widget_gtk.h"
 
 class Browser;
 class Profile;
@@ -107,7 +106,7 @@
 
   // The history menu. We keep this since we need to rewrite parts of it
   // periodically.
-  ui::OwnedWidgetGtk history_menu_;
+  GtkWidget* history_menu_;
 
   history::TopSites* top_sites_;
 
diff --git a/chrome/browser/ui/gtk/global_menu_bar.cc b/chrome/browser/ui/gtk/global_menu_bar.cc
index 46c3047..fa9b350 100644
--- a/chrome/browser/ui/gtk/global_menu_bar.cc
+++ b/chrome/browser/ui/gtk/global_menu_bar.cc
@@ -21,8 +21,8 @@
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_source.h"
 #include "grit/generated_resources.h"
+#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
 #include "ui/base/accelerators/platform_accelerator_gtk.h"
-#include "ui/base/gtk/menu_label_accelerator_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
 struct GlobalMenuBarCommand {
@@ -154,10 +154,11 @@
   // The global menu bar should never actually be shown in the app; it should
   // instead remain in our widget hierarchy simply to be noticed by third party
   // components.
-  gtk_widget_set_no_show_all(menu_bar_.get(), TRUE);
+  g_object_ref_sink(menu_bar_);
+  gtk_widget_set_no_show_all(menu_bar_, TRUE);
 
   // Set a nice name so it shows up in gtkparasite and others.
-  gtk_widget_set_name(menu_bar_.get(), "chrome-hidden-global-menubar");
+  gtk_widget_set_name(menu_bar_, "chrome-hidden-global-menubar");
 
   BuildGtkMenuFrom(IDS_FILE_MENU_LINUX, &id_to_menu_item_, file_menu, NULL);
   BuildGtkMenuFrom(IDS_EDIT_MENU_LINUX, &id_to_menu_item_, edit_menu, NULL);
@@ -201,6 +202,8 @@
 GlobalMenuBar::~GlobalMenuBar() {
   Disable();
   g_object_unref(dummy_accel_group_);
+  gtk_widget_destroy(menu_bar_);
+  g_object_unref(menu_bar_);
 }
 
 void GlobalMenuBar::Disable() {
@@ -239,7 +242,7 @@
 
   gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
   gtk_widget_show(menu_item);
-  gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar_.get()), menu_item);
+  gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar_), menu_item);
 }
 
 GtkWidget* GlobalMenuBar::BuildMenuItem(
diff --git a/chrome/browser/ui/gtk/global_menu_bar.h b/chrome/browser/ui/gtk/global_menu_bar.h
index fe9ba48..99f6f27 100644
--- a/chrome/browser/ui/gtk/global_menu_bar.h
+++ b/chrome/browser/ui/gtk/global_menu_bar.h
@@ -12,7 +12,6 @@
 #include "chrome/browser/command_observer.h"
 #include "chrome/browser/ui/gtk/global_history_menu.h"
 #include "ui/base/gtk/gtk_signal.h"
-#include "ui/base/gtk/owned_widget_gtk.h"
 
 class Browser;
 struct GlobalMenuBarCommand;
@@ -45,7 +44,7 @@
   // and command updates but not destroy the widgets.
   virtual void Disable();
 
-  GtkWidget* widget() { return menu_bar_.get(); }
+  GtkWidget* widget() { return menu_bar_; }
 
  private:
   typedef std::map<int, GtkWidget*> CommandIDMenuItemMap;
@@ -77,7 +76,7 @@
   PrefChangeRegistrar pref_change_registrar_;
 
   // Our menu bar widget.
-  ui::OwnedWidgetGtk menu_bar_;
+  GtkWidget* menu_bar_;
 
   // Listens to the TabRestoreService and the HistoryService and keeps the
   // history menu fresh.
diff --git a/chrome/browser/ui/gtk/gtk_theme_service.cc b/chrome/browser/ui/gtk/gtk_theme_service.cc
index b7f8201..e2272c4 100644
--- a/chrome/browser/ui/gtk/gtk_theme_service.cc
+++ b/chrome/browser/ui/gtk/gtk_theme_service.cc
@@ -16,7 +16,6 @@
 #include "base/prefs/pref_service.h"
 #include "base/stl_util.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/managed_mode/managed_user_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service_factory.h"
@@ -639,6 +638,11 @@
 }
 
 void GtkThemeService::LoadThemePrefs() {
+  // Initialize the values sent to webkit with the default values.
+  // ThemeService::LoadThemePrefs() will replace them with values for the native
+  // gtk theme if necessary.
+  LoadDefaultValues();
+
   // This takes care of calling SetNativeTheme() if necessary.
   ThemeService::LoadThemePrefs();
 
diff --git a/chrome/browser/ui/gtk/gtk_util.cc b/chrome/browser/ui/gtk/gtk_util.cc
index 3166f5f..b316dd3 100644
--- a/chrome/browser/ui/gtk/gtk_util.cc
+++ b/chrome/browser/ui/gtk/gtk_util.cc
@@ -34,10 +34,10 @@
 #include "chrome/browser/ui/host_desktop.h"
 #include "grit/chrome_unscaled_resources.h"
 #include "grit/theme_resources.h"
+#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
 #include "ui/base/gtk/gtk_compat.h"
 #include "ui/base/gtk/gtk_hig_constants.h"
 #include "ui/base/gtk/gtk_screen_util.h"
-#include "ui/base/gtk/menu_label_accelerator_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/text/text_elider.h"
diff --git a/chrome/browser/ui/gtk/location_bar_view_gtk.cc b/chrome/browser/ui/gtk/location_bar_view_gtk.cc
index 13adaa0..8f8b8b0 100644
--- a/chrome/browser/ui/gtk/location_bar_view_gtk.cc
+++ b/chrome/browser/ui/gtk/location_bar_view_gtk.cc
@@ -2068,7 +2068,7 @@
 
     case LocationBarController::ACTION_SHOW_SCRIPT_POPUP:
       ExtensionPopupGtk::Show(
-          ExtensionInfoUI::GetURL(extension->id()),
+          extensions::ExtensionInfoUI::GetURL(extension->id()),
           owner_->browser_,
           event_box_.get(),
           ExtensionPopupGtk::SHOW);
diff --git a/chrome/browser/ui/gtk/menu_gtk.cc b/chrome/browser/ui/gtk/menu_gtk.cc
index a3cfe5c..a9b9987 100644
--- a/chrome/browser/ui/gtk/menu_gtk.cc
+++ b/chrome/browser/ui/gtk/menu_gtk.cc
@@ -18,8 +18,8 @@
 #include "chrome/browser/ui/gtk/gtk_custom_menu_item.h"
 #include "chrome/browser/ui/gtk/gtk_util.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
 #include "ui/base/accelerators/platform_accelerator_gtk.h"
-#include "ui/base/gtk/menu_label_accelerator_util.h"
 #include "ui/base/models/button_menu_item_model.h"
 #include "ui/base/models/menu_model.h"
 #include "ui/base/window_open_disposition.h"
@@ -132,6 +132,7 @@
   switch (command_id) {
     case IDC_NEW_TAB:
     case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
+    case IDC_CONTENT_CONTEXT_SEARCHIMAGENEWTAB:
     case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB:
     case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
       stock = GTK_STOCK_NEW;
diff --git a/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
index c931846..15fa357 100644
--- a/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
+++ b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
@@ -39,11 +39,11 @@
 #include "grit/generated_resources.h"
 #include "net/base/escape.h"
 #include "third_party/undoview/undo_view.h"
+#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/gtk_dnd_util.h"
 #include "ui/base/gtk/gtk_compat.h"
 #include "ui/base/gtk/gtk_hig_constants.h"
-#include "ui/base/gtk/menu_label_accelerator_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/color_utils.h"
diff --git a/chrome/browser/ui/gtk/status_icons/status_tray_gtk.cc b/chrome/browser/ui/gtk/status_icons/status_tray_gtk.cc
index ecff525..a86f48f 100644
--- a/chrome/browser/ui/gtk/status_icons/status_tray_gtk.cc
+++ b/chrome/browser/ui/gtk/status_icons/status_tray_gtk.cc
@@ -12,8 +12,13 @@
 StatusTrayGtk::~StatusTrayGtk() {
 }
 
-StatusIcon* StatusTrayGtk::CreatePlatformStatusIcon(StatusIconType type) {
-  return new StatusIconGtk();
+StatusIcon* StatusTrayGtk::CreatePlatformStatusIcon(StatusIconType type,
+                                                    const gfx::ImageSkia& image,
+                                                    const string16& tool_tip) {
+  StatusIcon* icon = new StatusIconGtk();
+  icon->SetImage(image);
+  icon->SetToolTip(tool_tip);
+  return icon;
 }
 
 StatusTray* StatusTray::Create() {
diff --git a/chrome/browser/ui/gtk/status_icons/status_tray_gtk.h b/chrome/browser/ui/gtk/status_icons/status_tray_gtk.h
index 3e010d7..d24d386 100644
--- a/chrome/browser/ui/gtk/status_icons/status_tray_gtk.h
+++ b/chrome/browser/ui/gtk/status_icons/status_tray_gtk.h
@@ -15,7 +15,10 @@
 
  protected:
   // Overriden from StatusTray:
-  virtual StatusIcon* CreatePlatformStatusIcon(StatusIconType type) OVERRIDE;
+  virtual StatusIcon* CreatePlatformStatusIcon(
+      StatusIconType type,
+      const gfx::ImageSkia& image,
+      const string16& tool_tip) OVERRIDE;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(StatusTrayGtk);
diff --git a/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc b/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc
index 3cf40d0..3ca1df2 100644
--- a/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc
+++ b/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc
@@ -13,6 +13,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia.h"
 
 namespace {
 
@@ -29,12 +30,11 @@
 TEST(StatusTrayGtkTest, CreateIcon) {
   // Create an icon, set the images and tooltip, then shut it down.
   StatusTrayGtk tray;
-  StatusIcon* icon = tray.CreateStatusIcon(StatusTray::OTHER_ICON);
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
-  icon->SetImage(*image);
+  StatusIcon* icon = tray.CreateStatusIcon(
+      StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip"));
   icon->SetPressedImage(*image);
-  icon->SetToolTip(ASCIIToUTF16("tool tip"));
   ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(NULL);
   menu->AddItem(0, ASCIIToUTF16("foo"));
   icon->SetContextMenu(menu);
@@ -43,8 +43,10 @@
 TEST(StatusTrayGtkTest, ClickOnIcon) {
   // Create an icon, send a fake click event, make sure observer is called.
   StatusTrayGtk tray;
-  StatusIconGtk* icon = static_cast<StatusIconGtk*>(
-      tray.CreateStatusIcon(StatusTray::OTHER_ICON));
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
+  StatusIconGtk* icon = static_cast<StatusIconGtk*>(tray.CreateStatusIcon(
+      StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip")));
   MockStatusIconObserver observer;
   icon->AddObserver(&observer);
   EXPECT_CALL(observer, OnStatusIconClicked());
diff --git a/chrome/browser/ui/gtk/tab_contents/render_view_context_menu_gtk.cc b/chrome/browser/ui/gtk/tab_contents/render_view_context_menu_gtk.cc
index 9b9469a..e2ae379 100644
--- a/chrome/browser/ui/gtk/tab_contents/render_view_context_menu_gtk.cc
+++ b/chrome/browser/ui/gtk/tab_contents/render_view_context_menu_gtk.cc
@@ -14,7 +14,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/context_menu_params.h"
 #include "grit/generated_resources.h"
-#include "ui/base/gtk/menu_label_accelerator_util.h"
+#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
 #include "ui/base/l10n/l10n_util.h"
 
 using content::WebContents;
diff --git a/chrome/browser/ui/gtk/tab_modal_confirm_dialog_gtk.cc b/chrome/browser/ui/gtk/tab_modal_confirm_dialog_gtk.cc
index 625b7c8..48c26db 100644
--- a/chrome/browser/ui/gtk/tab_modal_confirm_dialog_gtk.cc
+++ b/chrome/browser/ui/gtk/tab_modal_confirm_dialog_gtk.cc
@@ -31,9 +31,9 @@
 TabModalConfirmDialogGtk::TabModalConfirmDialogGtk(
     TabModalConfirmDialogDelegate* delegate,
     content::WebContents* web_contents)
-    : web_contents_(web_contents),
-      delegate_(delegate),
-      window_(NULL) {
+    : delegate_(delegate),
+      window_(NULL),
+      closing_(false) {
   dialog_ = gtk_vbox_new(FALSE, ui::kContentAreaSpacing);
   GtkWidget* label = gtk_label_new(
       UTF16ToUTF8(delegate->GetMessage()).c_str());
@@ -101,7 +101,7 @@
   g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroyThunk), this);
 
   window_ = CreateWebContentsModalDialogGtk(dialog_, cancel_);
-  delegate_->set_operations_delegate(this);
+  delegate_->set_close_delegate(this);
 
   WebContentsModalDialogManager* web_contents_modal_dialog_manager =
       WebContentsModalDialogManager::FromWebContents(web_contents);
@@ -109,6 +109,10 @@
 }
 
 TabModalConfirmDialogGtk::~TabModalConfirmDialogGtk() {
+  // Provide a disposition in case the dialog was closed without accepting or
+  // cancelling.
+  delegate_->Close();
+
   gtk_widget_destroy(dialog_);
 }
 
@@ -121,14 +125,10 @@
 }
 
 void TabModalConfirmDialogGtk::CloseDialog() {
-  gtk_widget_destroy(window_);
-}
-
-void TabModalConfirmDialogGtk::SetPreventCloseOnLoadStart(bool prevent) {
-  WebContentsModalDialogManager* web_contents_modal_dialog_manager =
-      WebContentsModalDialogManager::FromWebContents(web_contents_);
-  web_contents_modal_dialog_manager->SetPreventCloseOnLoadStart(window_,
-                                                                prevent);
+  if (!closing_) {
+    closing_ = true;
+    gtk_widget_destroy(window_);
+  }
 }
 
 void TabModalConfirmDialogGtk::OnAccept(GtkWidget* widget) {
diff --git a/chrome/browser/ui/gtk/tab_modal_confirm_dialog_gtk.h b/chrome/browser/ui/gtk/tab_modal_confirm_dialog_gtk.h
index bdbff19..abf4564 100644
--- a/chrome/browser/ui/gtk/tab_modal_confirm_dialog_gtk.h
+++ b/chrome/browser/ui/gtk/tab_modal_confirm_dialog_gtk.h
@@ -38,9 +38,8 @@
   virtual void AcceptTabModalDialog() OVERRIDE;
   virtual void CancelTabModalDialog() OVERRIDE;
 
-  // TabModalConfirmDialogOperationsDelegate:
+  // TabModalConfirmDialogCloseDelegate:
   virtual void CloseDialog() OVERRIDE;
-  virtual void SetPreventCloseOnLoadStart(bool prevent) OVERRIDE;
 
   // Callbacks:
   CHROMEGTK_CALLBACK_0(TabModalConfirmDialogGtk, void, OnAccept);
@@ -48,8 +47,6 @@
   CHROMEGTK_CALLBACK_0(TabModalConfirmDialogGtk, void, OnDestroy);
   CHROMEGTK_CALLBACK_0(TabModalConfirmDialogGtk, void, OnLinkClicked);
 
-  content::WebContents* web_contents_;
-
   scoped_ptr<TabModalConfirmDialogDelegate> delegate_;
 
   GtkWidget* dialog_;
@@ -58,6 +55,8 @@
 
   GtkWidget* window_;
 
+  bool closing_;
+
   DISALLOW_COPY_AND_ASSIGN(TabModalConfirmDialogGtk);
 };
 
diff --git a/chrome/browser/ui/gtk/tabs/tab_gtk.cc b/chrome/browser/ui/gtk/tabs/tab_gtk.cc
index 1d20524..d4a6e94 100644
--- a/chrome/browser/ui/gtk/tabs/tab_gtk.cc
+++ b/chrome/browser/ui/gtk/tabs/tab_gtk.cc
@@ -64,7 +64,9 @@
       closing_(false),
       dragging_(false),
       last_mouse_down_(NULL),
+      drag_widget_(NULL),
       title_width_(0),
+      destroy_factory_(this),
       drag_end_factory_(this) {
   event_box_ = gtk_input_event_box_new();
   g_signal_connect(event_box_, "button-press-event",
@@ -75,14 +77,6 @@
                    G_CALLBACK(OnEnterNotifyEventThunk), this);
   g_signal_connect(event_box_, "leave-notify-event",
                    G_CALLBACK(OnLeaveNotifyEventThunk), this);
-
-  g_signal_connect(widget(), "drag-failed",
-                   G_CALLBACK(OnDragFailedThunk), this);
-  g_signal_connect(widget(), "button-release-event",
-                   G_CALLBACK(OnDragButtonReleasedThunk), this);
-  g_signal_connect_after(widget(), "drag-begin",
-                         G_CALLBACK(OnDragBeginThunk), this);
-
   gtk_widget_add_events(event_box_,
         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
@@ -91,11 +85,12 @@
 }
 
 TabGtk::~TabGtk() {
-  if (dragging_) {
+  if (drag_widget_) {
     // Shadow the drag grab so the grab terminates. We could do this using any
-    // widget.
-    gtk_grab_add(widget());
-    gtk_grab_remove(widget());
+    // widget, |drag_widget_| is just convenient.
+    gtk_grab_add(drag_widget_);
+    gtk_grab_remove(drag_widget_);
+    DestroyDragWidget();
   }
 
   if (menu_controller_.get()) {
@@ -151,10 +146,6 @@
 
 gboolean TabGtk::OnButtonReleaseEvent(GtkWidget* widget,
                                       GdkEventButton* event) {
-  // Pass event handling to OnDragButtonReleased when dragging.
-  if (dragging_)
-    return FALSE;
-
   if (event->button == 1) {
     if (IsActive() && !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
       delegate_->ActivateTab(this);
@@ -181,7 +172,7 @@
     // some state before closing the tab to avoid a crash.  Once the drag has
     // started, we don't get the middle mouse click here.
     if (last_mouse_down_) {
-      DCHECK(!dragging_);
+      DCHECK(!drag_widget_);
       observer_.reset();
       gdk_event_free(last_mouse_down_);
       last_mouse_down_ = NULL;
@@ -232,7 +223,7 @@
     return;
   }
 
-  if (dragging_) {
+  if (drag_widget_) {
     delegate_->ContinueDrag(NULL);
     return;
   }
@@ -315,16 +306,34 @@
   }
 }
 
+void TabGtk::CreateDragWidget() {
+  DCHECK(!drag_widget_);
+  drag_widget_ = gtk_invisible_new();
+  g_signal_connect(drag_widget_, "drag-failed",
+                   G_CALLBACK(OnDragFailedThunk), this);
+  g_signal_connect(drag_widget_, "button-release-event",
+                   G_CALLBACK(OnDragButtonReleasedThunk), this);
+  g_signal_connect_after(drag_widget_, "drag-begin",
+                         G_CALLBACK(OnDragBeginThunk), this);
+}
+
+void TabGtk::DestroyDragWidget() {
+  if (drag_widget_) {
+    gtk_widget_destroy(drag_widget_);
+    drag_widget_ = NULL;
+  }
+}
+
 void TabGtk::StartDragging(gfx::Point drag_offset) {
   // If the drag is processed after the selection change it's possible
   // that the tab has been deselected, in which case we don't want to drag.
   if (!IsSelected())
     return;
 
-  dragging_ = true;
+  CreateDragWidget();
 
   GtkTargetList* list = ui::GetTargetListFromCodeMask(ui::CHROME_TAB);
-  gtk_drag_begin(widget(), list, GDK_ACTION_MOVE,
+  gtk_drag_begin(drag_widget_, list, GDK_ACTION_MOVE,
                  1,  // Drags are always initiated by the left button.
                  last_mouse_down_);
   // gtk_drag_begin adds a reference to list, so unref it here.
@@ -337,7 +346,12 @@
   // to call EndDrag.
   drag_end_factory_.InvalidateWeakPtrs();
 
-  dragging_ = false;
+  // We must let gtk clean up after we handle the drag operation, otherwise
+  // there will be outstanding references to the drag widget when we try to
+  // destroy it.
+  base::MessageLoop::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&TabGtk::DestroyDragWidget, destroy_factory_.GetWeakPtr()));
 
   if (last_mouse_down_) {
     gdk_event_free(last_mouse_down_);
diff --git a/chrome/browser/ui/gtk/tabs/tab_gtk.h b/chrome/browser/ui/gtk/tabs/tab_gtk.h
index a4bb6e0..fccfe5b 100644
--- a/chrome/browser/ui/gtk/tabs/tab_gtk.h
+++ b/chrome/browser/ui/gtk/tabs/tab_gtk.h
@@ -159,6 +159,12 @@
   // the tab.
   void UpdateTooltipState();
 
+  // Creates the drag widget used to track a drag operation.
+  void CreateDragWidget();
+
+  // Destroys the drag widget.
+  void DestroyDragWidget();
+
   // Starts the dragging operation.  |drag_offset| is the offset inside the tab
   // bounds where the grab occurred.
   void StartDragging(gfx::Point drag_offset);
@@ -191,6 +197,11 @@
   // A copy of the last button press event, used to initiate a drag.
   GdkEvent* last_mouse_down_;
 
+  // A GtkInivisible used to track the drag event.  GtkInvisibles are of the
+  // type GInitiallyUnowned, but the widget initialization code sinks the
+  // reference, so we can't used an OwnedWidgetGtk here.
+  GtkWidget* drag_widget_;
+
   // The cached width of the title in pixels, updated whenever the title
   // changes.
   int title_width_;
@@ -198,6 +209,9 @@
   // Keep track of whether or not we have an observer.
   scoped_ptr<TabGtkObserverHelper> observer_;
 
+  // Used to destroy the drag widget after a return to the message loop.
+  base::WeakPtrFactory<TabGtk> destroy_factory_;
+
   // Due to a bug in GTK+, we need to force the end of a drag when we get a
   // mouse release event on the the dragged widget, otherwise, we don't know
   // when the drag has ended when the user presses space or enter.  We queue
diff --git a/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc b/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc
index ff4b79f..1e4a7bb 100644
--- a/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc
+++ b/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc
@@ -124,15 +124,18 @@
 
 namespace libgtk2ui {
 
-AppIndicatorIcon::AppIndicatorIcon(std::string id)
+AppIndicatorIcon::AppIndicatorIcon(std::string id,
+                                   const gfx::ImageSkia& image,
+                                   const string16& tool_tip)
     : id_(id),
       icon_(NULL),
       gtk_menu_(NULL),
       menu_model_(NULL),
       icon_change_count_(0),
-      block_activation_(false),
-      has_click_action_replacement_(false) {
+      block_activation_(false) {
   EnsureMethodsLoaded();
+  tool_tip_ = UTF16ToUTF8(tool_tip);
+  SetImage(image);
 }
 AppIndicatorIcon::~AppIndicatorIcon() {
   if (icon_) {
@@ -154,22 +157,25 @@
 }
 
 void AppIndicatorIcon::SetImage(const gfx::ImageSkia& image) {
-  if (opened) {
-    ++icon_change_count_;
-    gfx::ImageSkia safe_image = gfx::ImageSkia(image);
-    safe_image.MakeThreadSafe();
-    base::PostTaskAndReplyWithResult(
-        content::BrowserThread::GetBlockingPool()
-            ->GetTaskRunnerWithShutdownBehavior(
-                  base::SequencedWorkerPool::SKIP_ON_SHUTDOWN).get(),
-        FROM_HERE,
-        base::Bind(&AppIndicatorIcon::CreateTempImageFile,
-                   safe_image,
-                   icon_change_count_,
-                   id_),
-        base::Bind(&AppIndicatorIcon::SetImageFromFile,
-                   base::Unretained(this)));
-  }
+  if (!opened)
+    return;
+
+  ++icon_change_count_;
+
+  // We create a deep copy of the image since it may have been freed by the time
+  // it's accessed in the other thread.
+  scoped_ptr<gfx::ImageSkia> safe_image(image.DeepCopy());
+  base::PostTaskAndReplyWithResult(
+      content::BrowserThread::GetBlockingPool()
+          ->GetTaskRunnerWithShutdownBehavior(
+                base::SequencedWorkerPool::SKIP_ON_SHUTDOWN).get(),
+      FROM_HERE,
+      base::Bind(&AppIndicatorIcon::CreateTempImageFile,
+                 safe_image.release(),
+                 icon_change_count_,
+                 id_),
+      base::Bind(&AppIndicatorIcon::SetImageFromFile,
+                 base::Unretained(this)));
 }
 
 void AppIndicatorIcon::SetPressedImage(const gfx::ImageSkia& image) {
@@ -178,27 +184,22 @@
 }
 
 void AppIndicatorIcon::SetToolTip(const string16& tool_tip) {
-  // App-indicators don't support tool-tips. Ignore call.
-}
+  DCHECK(!tool_tip_.empty());
+  tool_tip_ = UTF16ToUTF8(tool_tip);
 
-void AppIndicatorIcon::SetClickActionLabel(const string16& label) {
-  click_action_label_ = UTF16ToUTF8(label);
-
-  // If the menu item has already been created, then find the menu item and
-  // change it's label.
-  if (has_click_action_replacement_) {
+  // We can set the click action label only if the icon exists. Also we only
+  // need to update the label if it is shown and it's only shown if we are sure
+  // that there is a click action or if there is no menu.
+  if (icon_ && (delegate()->HasClickAction() || menu_model_ == NULL)) {
     GList* children = gtk_container_get_children(GTK_CONTAINER(gtk_menu_));
     for (GList* child = children; child; child = g_list_next(child))
       if (g_object_get_data(G_OBJECT(child->data), "click-action-item") !=
           NULL) {
         gtk_menu_item_set_label(GTK_MENU_ITEM(child->data),
-                                click_action_label_.c_str());
+                                tool_tip_.c_str());
         break;
       }
     g_list_free(children);
-  } else if (icon_) {
-    CreateClickActionReplacement();
-    app_indicator_set_menu(icon_, GTK_MENU(gtk_menu_));
   }
 }
 
@@ -208,13 +209,12 @@
 
   if (gtk_menu_) {
     DestroyMenu();
-    has_click_action_replacement_ = false;
   }
   menu_model_ = model;
 
-  // If icon doesn't exist now it's okay, the menu will be set later along with
-  // the image. Both an icon and a menu are required to show an app indicator.
-  if (model && icon_)
+  // The icon is created asynchronously so it might not exist when the menu is
+  // set.
+  if (icon_)
     SetMenu();
 }
 
@@ -234,12 +234,8 @@
                                       APP_INDICATOR_CATEGORY_APPLICATION_STATUS,
                                       icon_dir.c_str());
       app_indicator_set_status(icon_, APP_INDICATOR_STATUS_ACTIVE);
-      if (menu_model_) {
-        SetMenu();
-      } else if (!click_action_label_.empty()) {
-        CreateClickActionReplacement();
-        app_indicator_set_menu(icon_, GTK_MENU(gtk_menu_));
-      }
+
+      SetMenu();
     } else {
       // Currently we are creating a new temp directory every time the icon is
       // set. So we need to set the directory each time.
@@ -256,40 +252,38 @@
 
 void AppIndicatorIcon::SetMenu() {
   gtk_menu_ = gtk_menu_new();
-  BuildSubmenuFromModel(menu_model_,
-                        gtk_menu_,
-                        G_CALLBACK(OnMenuItemActivatedThunk),
-                        &block_activation_,
-                        this);
-  if (!click_action_label_.empty())
+
+  if (delegate()->HasClickAction() || menu_model_ == NULL) {
     CreateClickActionReplacement();
-  UpdateMenu();
-  menu_model_->MenuWillShow();
+    if (menu_model_) {
+      // Add separator before the other menu items.
+      GtkWidget* menu_item = gtk_separator_menu_item_new();
+      gtk_widget_show(menu_item);
+      gtk_menu_shell_append(GTK_MENU_SHELL(gtk_menu_), menu_item);
+    }
+  }
+  if (menu_model_) {
+    BuildSubmenuFromModel(menu_model_,
+                          gtk_menu_,
+                          G_CALLBACK(OnMenuItemActivatedThunk),
+                          &block_activation_,
+                          this);
+    UpdateMenu();
+    menu_model_->MenuWillShow();
+  }
   app_indicator_set_menu(icon_, GTK_MENU(gtk_menu_));
 }
 
 void AppIndicatorIcon::CreateClickActionReplacement() {
-  GtkWidget* menu_item = NULL;
-
-  // If a menu doesn't exist create one just for the click action replacement.
-  if (!gtk_menu_) {
-    gtk_menu_ = gtk_menu_new();
-  } else {
-    // Add separator before the other menu items.
-    menu_item = gtk_separator_menu_item_new();
-    gtk_widget_show(menu_item);
-    gtk_menu_shell_prepend(GTK_MENU_SHELL(gtk_menu_), menu_item);
-  }
+  DCHECK(!tool_tip_.empty());
 
   // Add "click replacement menu item".
-  menu_item = gtk_menu_item_new_with_mnemonic(click_action_label_.c_str());
+  GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic(tool_tip_.c_str());
   g_object_set_data(
       G_OBJECT(menu_item), "click-action-item", GINT_TO_POINTER(1));
   g_signal_connect(menu_item, "activate", G_CALLBACK(OnClickThunk), this);
   gtk_widget_show(menu_item);
   gtk_menu_shell_prepend(GTK_MENU_SHELL(gtk_menu_), menu_item);
-
-  has_click_action_replacement_ = true;
 }
 
 void AppIndicatorIcon::DestroyMenu() {
@@ -300,11 +294,13 @@
   menu_model_ = NULL;
 }
 
-base::FilePath AppIndicatorIcon::CreateTempImageFile(gfx::ImageSkia image,
+base::FilePath AppIndicatorIcon::CreateTempImageFile(gfx::ImageSkia* image_ptr,
                                                      int icon_change_count,
                                                      std::string id) {
+  scoped_ptr<gfx::ImageSkia> image(image_ptr);
+
   scoped_refptr<base::RefCountedMemory> png_data =
-      gfx::Image(image).As1xPNGBytes();
+      gfx::Image(*image.get()).As1xPNGBytes();
   if (png_data->size() == 0) {
     // If the bitmap could not be encoded to PNG format, skip it.
     LOG(WARNING) << "Could not encode icon";
diff --git a/chrome/browser/ui/libgtk2ui/app_indicator_icon.h b/chrome/browser/ui/libgtk2ui/app_indicator_icon.h
index 33bfd5e..5e2e60c 100644
--- a/chrome/browser/ui/libgtk2ui/app_indicator_icon.h
+++ b/chrome/browser/ui/libgtk2ui/app_indicator_icon.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_LIBGTK2UI_APP_INDICATOR_ICON_H_
 
 #include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
 #include "chrome/browser/ui/libgtk2ui/gtk2_signal.h"
 #include "ui/linux_ui/status_icon_linux.h"
 
@@ -22,7 +23,9 @@
  public:
   // The id uniquely identifies the new status icon from other chrome status
   // icons.
-  explicit AppIndicatorIcon(std::string id);
+  explicit AppIndicatorIcon(std::string id,
+                            const gfx::ImageSkia& image,
+                            const string16& tool_tip);
   virtual ~AppIndicatorIcon();
 
   // Indicates whether libappindicator so could be opened.
@@ -32,7 +35,6 @@
   virtual void SetImage(const gfx::ImageSkia& image) OVERRIDE;
   virtual void SetPressedImage(const gfx::ImageSkia& image) OVERRIDE;
   virtual void SetToolTip(const string16& tool_tip) OVERRIDE;
-  virtual void SetClickActionLabel(const string16& label) OVERRIDE;
 
  protected:
   // Overridden from StatusIcon.
@@ -49,7 +51,7 @@
   void CreateClickActionReplacement();
   void DestroyMenu();
 
-  static base::FilePath CreateTempImageFile(gfx::ImageSkia image,
+  static base::FilePath CreateTempImageFile(gfx::ImageSkia* image,
                                             int icon_change_count,
                                             std::string id);
   static void DeletePath(base::FilePath icon_file_path);
@@ -64,7 +66,7 @@
   CHROMEGTK_CALLBACK_0(AppIndicatorIcon, void, OnMenuItemActivated);
 
   std::string id_;
-  std::string click_action_label_;
+  std::string tool_tip_;
 
   // Gtk status icon wrapper
   AppIndicator* icon_;
@@ -75,7 +77,6 @@
   base::FilePath icon_file_path_;
   int icon_change_count_;
   bool block_activation_;
-  bool has_click_action_replacement_;
 };
 
 }  // namespace libgtk2ui
diff --git a/chrome/browser/ui/libgtk2ui/gtk2_ui.cc b/chrome/browser/ui/libgtk2ui/gtk2_ui.cc
index 4167edc..1376db2 100644
--- a/chrome/browser/ui/libgtk2ui/gtk2_ui.cc
+++ b/chrome/browser/ui/libgtk2ui/gtk2_ui.cc
@@ -395,11 +395,15 @@
   return AppIndicatorIcon::CouldOpen();
 }
 
-scoped_ptr<StatusIconLinux> Gtk2UI::CreateLinuxStatusIcon() const {
+scoped_ptr<StatusIconLinux> Gtk2UI::CreateLinuxStatusIcon(
+    const gfx::ImageSkia& image,
+    const string16& tool_tip) const {
   if (AppIndicatorIcon::CouldOpen()) {
     ++indicators_count;
     return scoped_ptr<StatusIconLinux>(new AppIndicatorIcon(
-        base::StringPrintf("%s%d", kAppIndicatorIdPrefix, indicators_count)));
+        base::StringPrintf("%s%d", kAppIndicatorIdPrefix, indicators_count),
+        image,
+        tool_tip));
   } else {
     return scoped_ptr<StatusIconLinux>();
   }
diff --git a/chrome/browser/ui/libgtk2ui/gtk2_ui.h b/chrome/browser/ui/libgtk2ui/gtk2_ui.h
index 69a7dde..af57462 100644
--- a/chrome/browser/ui/libgtk2ui/gtk2_ui.h
+++ b/chrome/browser/ui/libgtk2ui/gtk2_ui.h
@@ -48,7 +48,9 @@
   virtual void SetDownloadCount(int count) const OVERRIDE;
   virtual void SetProgressFraction(float percentage) const OVERRIDE;
   virtual bool IsStatusIconSupported() const OVERRIDE;
-  virtual scoped_ptr<StatusIconLinux> CreateLinuxStatusIcon() const OVERRIDE;
+  virtual scoped_ptr<StatusIconLinux> CreateLinuxStatusIcon(
+      const gfx::ImageSkia& image,
+      const string16& tool_tip) const OVERRIDE;
 
  private:
   typedef std::map<int, SkColor> ColorMap;
diff --git a/chrome/browser/ui/libgtk2ui/gtk2_util.cc b/chrome/browser/ui/libgtk2ui/gtk2_util.cc
index 7fd3d50..7994336 100644
--- a/chrome/browser/ui/libgtk2ui/gtk2_util.cc
+++ b/chrome/browser/ui/libgtk2ui/gtk2_util.cc
@@ -35,33 +35,6 @@
   }
 }
 
-// Replaces all ampersands (as used in our grd files to indicate mnemonics)
-// to |target|, except ampersands appearing in pairs which are replaced by
-// a single ampersand. Any underscores get replaced with two underscores as
-// is needed by GTK.
-std::string ConvertAmpersandsTo(const std::string& label,
-                                const std::string& target) {
-  std::string ret;
-  ret.reserve(label.length() * 2);
-  for (size_t i = 0; i < label.length(); ++i) {
-    if ('_' == label[i]) {
-      ret.push_back('_');
-      ret.push_back('_');
-    } else if ('&' == label[i]) {
-      if (i + 1 < label.length() && '&' == label[i + 1]) {
-        ret.push_back('&');
-        ++i;
-      } else {
-        ret.append(target);
-      }
-    } else {
-      ret.push_back(label[i]);
-    }
-  }
-
-  return ret;
-}
-
 }  // namespace
 
 namespace libgtk2ui {
@@ -92,10 +65,6 @@
       GTK_IMAGE_MENU_ITEM(image_menu_item), TRUE);
 }
 
-std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label) {
-  return ConvertAmpersandsTo(label, "_");
-}
-
 guint GetGdkKeyCodeForAccelerator(const ui::Accelerator& accelerator) {
   // The second parameter is false because accelerator keys are expressed in
   // terms of the non-shift-modified key.
diff --git a/chrome/browser/ui/libgtk2ui/gtk2_util.h b/chrome/browser/ui/libgtk2ui/gtk2_util.h
index ca5274c..d58038d 100644
--- a/chrome/browser/ui/libgtk2ui/gtk2_util.h
+++ b/chrome/browser/ui/libgtk2ui/gtk2_util.h
@@ -31,10 +31,6 @@
 // crucial to its functionality.
 void SetAlwaysShowImage(GtkWidget* image_menu_item);
 
-// Change windows accelerator style to GTK style. (GTK uses _ for
-// accelerators.  Windows uses & with && as an escape for &.)
-std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label);
-
 guint GetGdkKeyCodeForAccelerator(const ui::Accelerator& accelerator);
 
 GdkModifierType GetGdkModifierForAccelerator(
diff --git a/chrome/browser/ui/libgtk2ui/menu_util.cc b/chrome/browser/ui/libgtk2ui/menu_util.cc
index 90bb54e..c6d8a54 100644
--- a/chrome/browser/ui/libgtk2ui/menu_util.cc
+++ b/chrome/browser/ui/libgtk2ui/menu_util.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
 #include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
 #include "ui/base/accelerators/accelerator.h"
+#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
 #include "ui/base/models/menu_model.h"
 
 namespace libgtk2ui {
@@ -99,8 +100,8 @@
   GtkWidget* menu_item = NULL;
   for (int i = 0; i < model->GetItemCount(); ++i) {
     gfx::Image icon;
-    std::string label =
-        ConvertAcceleratorsFromWindowsStyle(UTF16ToUTF8(model->GetLabelAt(i)));
+    std::string label = ui::ConvertAcceleratorsFromWindowsStyle(
+        UTF16ToUTF8(model->GetLabelAt(i)));
 
     bool connect_to_activate = true;
 
@@ -227,7 +228,7 @@
     if (model->IsVisibleAt(id)) {
       // Update the menu item label if it is dynamic.
       if (model->IsItemDynamicAt(id)) {
-        std::string label = ConvertAcceleratorsFromWindowsStyle(
+        std::string label = ui::ConvertAcceleratorsFromWindowsStyle(
             UTF16ToUTF8(model->GetLabelAt(id)));
 
         gtk_menu_item_set_label(GTK_MENU_ITEM(widget), label.c_str());
diff --git a/chrome/browser/ui/libgtk2ui/select_file_dialog_impl_gtk2.cc b/chrome/browser/ui/libgtk2ui/select_file_dialog_impl_gtk2.cc
index e671825..3626255 100644
--- a/chrome/browser/ui/libgtk2ui/select_file_dialog_impl_gtk2.cc
+++ b/chrome/browser/ui/libgtk2ui/select_file_dialog_impl_gtk2.cc
@@ -108,8 +108,11 @@
   // us when we were told to show the dialog.
   void FileNotSelected(GtkWidget* dialog);
 
-  GtkWidget* CreateSelectFolderDialog(const std::string& title,
-      const base::FilePath& default_path, gfx::NativeWindow parent);
+  GtkWidget* CreateSelectFolderDialog(
+      Type type,
+      const std::string& title,
+      const base::FilePath& default_path,
+      gfx::NativeWindow parent);
 
   GtkWidget* CreateFileOpenDialog(const std::string& title,
       const base::FilePath& default_path, gfx::NativeWindow parent);
@@ -237,7 +240,8 @@
   GtkWidget* dialog = NULL;
   switch (type) {
     case SELECT_FOLDER:
-      dialog = CreateSelectFolderDialog(title_string, default_path,
+    case SELECT_UPLOAD_FOLDER:
+      dialog = CreateSelectFolderDialog(type, title_string, default_path,
                                         owning_window);
       break;
     case SELECT_OPEN_FILE:
@@ -396,17 +400,26 @@
 }
 
 GtkWidget* SelectFileDialogImplGTK::CreateSelectFolderDialog(
+    Type type,
     const std::string& title,
     const base::FilePath& default_path,
     gfx::NativeWindow parent) {
-  std::string title_string = !title.empty() ? title :
+  std::string title_string = title;
+  if (title_string.empty()) {
+    title_string = (type == SELECT_UPLOAD_FOLDER) ?
+        l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE) :
         l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE);
+  }
+  std::string accept_button_label = (type == SELECT_UPLOAD_FOLDER) ?
+      l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON) :
+      GTK_STOCK_OPEN;
 
   GtkWidget* dialog =
       gtk_file_chooser_dialog_new(title_string.c_str(), NULL,
                                   GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                                  accept_button_label.c_str(),
+                                  GTK_RESPONSE_ACCEPT,
                                   NULL);
   SetGtkTransientForAura(dialog, parent);
 
diff --git a/chrome/browser/ui/libgtk2ui/select_file_dialog_impl_kde.cc b/chrome/browser/ui/libgtk2ui/select_file_dialog_impl_kde.cc
index 717e9d7..d651144 100644
--- a/chrome/browser/ui/libgtk2ui/select_file_dialog_impl_kde.cc
+++ b/chrome/browser/ui/libgtk2ui/select_file_dialog_impl_kde.cc
@@ -113,7 +113,8 @@
   // us when we were told to show the dialog.
   void FileNotSelected(void *params);
 
-  void CreateSelectFolderDialog(const std::string& title,
+  void CreateSelectFolderDialog(Type type,
+                                const std::string& title,
                                 const base::FilePath& default_path,
                                 gfx::NativeWindow parent, void* params);
 
@@ -209,7 +210,8 @@
 
   switch (type) {
     case SELECT_FOLDER:
-      CreateSelectFolderDialog(title_string, default_path,
+    case SELECT_UPLOAD_FOLDER:
+      CreateSelectFolderDialog(type, title_string, default_path,
                                owning_window, params);
       return;
     case SELECT_OPEN_FILE:
@@ -351,8 +353,11 @@
 }
 
 void SelectFileDialogImplKDE::CreateSelectFolderDialog(
-    const std::string& title, const base::FilePath& default_path,
+    Type type, const std::string& title, const base::FilePath& default_path,
     gfx::NativeWindow parent, void *params) {
+  int title_message_id = (type == SELECT_UPLOAD_FOLDER)
+      ? IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE
+      : IDS_SELECT_FOLDER_DIALOG_TITLE;
   BrowserThread::PostTask(
       BrowserThread::FILE, FROM_HERE,
       base::Bind(
@@ -360,7 +365,7 @@
           this,
           KDialogParams(
               "--getexistingdirectory",
-              GetTitle(title, IDS_SELECT_FOLDER_DIALOG_TITLE),
+              GetTitle(title, title_message_id),
               default_path.empty() ? *last_opened_path_ : default_path,
               parent, false, false, params,
               &SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse)));
diff --git a/chrome/browser/ui/panels/base_panel_browser_test.cc b/chrome/browser/ui/panels/base_panel_browser_test.cc
index 2a33629..69eb6ef 100644
--- a/chrome/browser/ui/panels/base_panel_browser_test.cc
+++ b/chrome/browser/ui/panels/base_panel_browser_test.cc
@@ -556,8 +556,10 @@
   EXPECT_TRUE(extension.get());
   EXPECT_STREQ("", error.c_str());
   browser()->profile()->GetExtensionService()->
-      OnExtensionInstalled(extension.get(), syncer::StringOrdinal(),
+      OnExtensionInstalled(extension.get(),
+                           syncer::StringOrdinal(),
                            false /* no requirement errors */,
+                           extensions::Blacklist::NOT_BLACKLISTED,
                            false /* don't wait for idle */);
   return extension;
 }
diff --git a/chrome/browser/ui/search/instant_controller.cc b/chrome/browser/ui/search/instant_controller.cc
index 28ce7fa..8c86635 100644
--- a/chrome/browser/ui/search/instant_controller.cc
+++ b/chrome/browser/ui/search/instant_controller.cc
@@ -103,10 +103,8 @@
 
 }  // namespace
 
-InstantController::InstantController(BrowserInstantController* browser,
-                                     bool extended_enabled)
+InstantController::InstantController(BrowserInstantController* browser)
     : browser_(browser),
-      extended_enabled_(extended_enabled),
       omnibox_focus_state_(OMNIBOX_FOCUS_NONE),
       omnibox_focus_change_reason_(OMNIBOX_FOCUS_CHANGE_EXPLICIT),
       omnibox_bounds_(-1, -1, 0, 0) {
@@ -116,7 +114,7 @@
 }
 
 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
-  if (!extended_enabled() || omnibox_bounds_ == bounds)
+  if (omnibox_bounds_ == bounds)
     return;
 
   omnibox_bounds_ = bounds;
@@ -130,7 +128,7 @@
 }
 
 void InstantController::InstantPageLoadFailed(content::WebContents* contents) {
-  if (!chrome::ShouldPreferRemoteNTPOnStartup() || !extended_enabled()) {
+  if (!chrome::ShouldPreferRemoteNTPOnStartup()) {
     // We only need to fall back on errors if we're showing the online page
     // at startup, as otherwise we fall back correctly when trying to show
     // a page that hasn't yet indicated that it supports the InstantExtended
@@ -161,7 +159,7 @@
 }
 
 bool InstantController::SubmitQuery(const string16& search_terms) {
-  if (extended_enabled() && instant_tab_ && instant_tab_->supports_instant() &&
+  if (instant_tab_ && instant_tab_->supports_instant() &&
       search_mode_.is_origin_search()) {
     // Use |instant_tab_| to run the query if we're already on a search results
     // page. (NOTE: in particular, we do not send the query to NTPs.)
@@ -182,7 +180,7 @@
       state, reason));
 
   omnibox_focus_state_ = state;
-  if (!extended_enabled() || !instant_tab_)
+  if (!instant_tab_)
     return;
 
   content::NotificationService::current()->Notify(
@@ -200,9 +198,6 @@
 
 void InstantController::SearchModeChanged(const SearchMode& old_mode,
                                           const SearchMode& new_mode) {
-  if (!extended_enabled())
-    return;
-
   LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
       "SearchModeChanged: [origin:mode] %d:%d to %d:%d", old_mode.origin,
       old_mode.mode, new_mode.origin, new_mode.mode));
@@ -215,9 +210,6 @@
 }
 
 void InstantController::ActiveTabChanged() {
-  if (!extended_enabled())
-    return;
-
   LOG_INSTANT_DEBUG_EVENT(this, "ActiveTabChanged");
   ResetInstantTab();
 }
@@ -314,9 +306,6 @@
 
 void InstantController::FocusOmnibox(const content::WebContents* contents,
                                      OmniboxFocusState state) {
-  if (!extended_enabled())
-    return;
-
   DCHECK(IsContentsFrom(instant_tab(), contents));
 
   // Do not add a default case in the switch block for the following reasons:
@@ -351,9 +340,6 @@
 
   // TODO(samarth): handle case where contents are no longer "active" (e.g. user
   // has switched tabs).
-  if (!extended_enabled())
-    return;
-
   if (transition == content::PAGE_TRANSITION_AUTO_BOOKMARK) {
     content::RecordAction(
         content::UserMetricsAction("InstantExtended.MostVisitedClicked"));
@@ -365,10 +351,6 @@
   browser_->OpenURL(url, transition, disposition);
 }
 
-bool InstantController::extended_enabled() const {
-  return extended_enabled_;
-}
-
 void InstantController::ResetInstantTab() {
   if (!search_mode_.is_origin_default()) {
     content::WebContents* active_tab = browser_->GetActiveWebContents();
diff --git a/chrome/browser/ui/search/instant_controller.h b/chrome/browser/ui/search/instant_controller.h
index b204ba0..e117a3a 100644
--- a/chrome/browser/ui/search/instant_controller.h
+++ b/chrome/browser/ui/search/instant_controller.h
@@ -48,8 +48,7 @@
 // InstantController is owned by Browser via BrowserInstantController.
 class InstantController : public InstantPage::Delegate {
  public:
-  InstantController(BrowserInstantController* browser,
-                    bool extended_enabled);
+  explicit InstantController(BrowserInstantController* browser);
   virtual ~InstantController();
 
   // Sets the stored start-edge margin and width of the omnibox.
@@ -100,8 +99,6 @@
 
  protected:
   // Accessors are made protected for testing purposes.
-  virtual bool extended_enabled() const;
-
   virtual InstantTab* instant_tab() const;
 
   virtual Profile* profile() const;
@@ -198,10 +195,6 @@
 
   BrowserInstantController* const browser_;
 
-  // Whether the extended API and regular API are enabled. If both are false,
-  // Instant is effectively disabled.
-  const bool extended_enabled_;
-
   // The instance of InstantPage maintained by InstantController.
   scoped_ptr<InstantTab> instant_tab_;
 
diff --git a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
index 686d654..4f5757b 100644
--- a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
+++ b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
@@ -70,7 +70,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_view.h"
 #include "content/public/common/bindings_policy.h"
-#include "content/public/common/renderer_preferences.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "grit/generated_resources.h"
@@ -313,14 +312,21 @@
 
   void InstallThemeAndVerify(const std::string& theme_dir,
                              const std::string& theme_name) {
-    const base::FilePath theme_path = test_data_dir_.AppendASCII(theme_dir);
-    ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(
-        theme_path, 1, ExtensionBrowserTest::browser()));
     const extensions::Extension* theme =
         ThemeServiceFactory::GetThemeForProfile(
             ExtensionBrowserTest::browser()->profile());
-    ASSERT_NE(static_cast<extensions::Extension*>(NULL), theme);
-    ASSERT_EQ(theme->name(), theme_name);
+    // If there is already a theme installed, the current theme should be
+    // disabled and the new one installed + enabled.
+    int expected_change = theme ? 0 : 1;
+
+    const base::FilePath theme_path = test_data_dir_.AppendASCII(theme_dir);
+    ASSERT_TRUE(InstallExtensionWithUIAutoConfirm(
+        theme_path, expected_change, ExtensionBrowserTest::browser()));
+    const extensions::Extension* new_theme =
+        ThemeServiceFactory::GetThemeForProfile(
+            ExtensionBrowserTest::browser()->profile());
+    ASSERT_NE(static_cast<extensions::Extension*>(NULL), new_theme);
+    ASSERT_EQ(new_theme->name(), theme_name);
   }
 
  private:
@@ -331,7 +337,7 @@
 
 IN_PROC_BROWSER_TEST_F(InstantExtendedTest, ExtendedModeIsOn) {
   ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
-  EXPECT_TRUE(instant()->extended_enabled_);
+  EXPECT_TRUE(chrome::IsInstantExtendedAPIEnabled());
 }
 
 IN_PROC_BROWSER_TEST_F(InstantExtendedTest, NTPIsPreloaded) {
@@ -842,73 +848,6 @@
   EXPECT_LT(0, on_esc_key_press_event_calls_);
 }
 
-// Test that renderer initiated navigations to an instant URL from a non
-// Instant page do not end up in an Instant process if they are bounced to the
-// browser.
-IN_PROC_BROWSER_TEST_F(
-    InstantExtendedTest,
-    DISABLED_RendererInitiatedNavigationNotInInstantProcess) {
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(browser()->profile());
-  ASSERT_NE(static_cast<InstantService*>(NULL), instant_service);
-
-  // Setup Instant.
-  ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
-  FocusOmniboxAndWaitForInstantNTPSupport();
-
-  EXPECT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-
-  // Don't use https server for the non instant URL so that the browser uses
-  // different RenderViews.
-  GURL non_instant_url = test_server()->GetURL("files/simple.html");
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(),
-      non_instant_url,
-      CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_FALSE(instant_service->IsInstantProcess(
-      contents->GetRenderProcessHost()->GetID()));
-  EXPECT_EQ(non_instant_url, contents->GetURL());
-
-  int old_render_view_id = contents->GetRenderViewHost()->GetRoutingID();
-  int old_render_process_id = contents->GetRenderProcessHost()->GetID();
-
-  std::string instant_url_with_query = instant_url().spec() + "q=3";
-  std::string add_link_script = base::StringPrintf(
-      "var a = document.createElement('a');"
-      "a.id = 'toClick';"
-      "a.href = '%s';"
-      "document.body.appendChild(a);",
-      instant_url_with_query.c_str());
-  EXPECT_TRUE(content::ExecuteScript(contents, add_link_script));
-
-  // Ensure that navigations are bounced to the browser.
-  contents->GetMutableRendererPrefs()->browser_handles_all_top_level_requests =
-      true;
-  contents->GetRenderViewHost()->SyncRendererPrefs();
-
-  content::WindowedNotificationObserver observer(
-        content::NOTIFICATION_NAV_ENTRY_COMMITTED,
-        content::NotificationService::AllSources());
-  EXPECT_TRUE(content::ExecuteScript(
-      contents, "document.getElementById('toClick').click();"));
-  observer.Wait();
-
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-  contents = browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_FALSE(instant_service->IsInstantProcess(
-      contents->GetRenderProcessHost()->GetID()));
-  EXPECT_EQ(GURL(instant_url_with_query), contents->GetURL());
-  int new_render_view_id = contents->GetRenderViewHost()->GetRoutingID();
-  int new_render_process_id = contents->GetRenderProcessHost()->GetID();
-
-  EXPECT_TRUE(old_render_process_id != new_render_process_id ||
-              old_render_view_id != new_render_view_id);
-}
-
 IN_PROC_BROWSER_TEST_F(InstantExtendedTest, AcceptingURLSearchDoesNotNavigate) {
   // Get a committed Instant tab, which will be in the Instant process and thus
   // support chrome::GetSearchTerms().
@@ -1345,8 +1284,15 @@
   EXPECT_EQ(1, on_theme_changed_calls);
 }
 
+
+// Flaky on Linux: http://crbug.com/265971
+#if defined(OS_LINUX)
+#define MAYBE_SendThemeBackgroundChangedEvent DISABLED_SendThemeBackgroundChangedEvent
+#else
+#define MAYBE_SendThemeBackgroundChangedEvent SendThemeBackgroundChangedEvent
+#endif
 IN_PROC_BROWSER_TEST_F(InstantPolicyTest,
-                       SendThemeBackgroundChangedEvent) {
+                       MAYBE_SendThemeBackgroundChangedEvent) {
   InstallThemeSource();
   ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
   FocusOmniboxAndWaitForInstantNTPSupport();
@@ -1554,14 +1500,9 @@
   EXPECT_TRUE(chrome::IsInstantNTP(active_tab));
 }
 
-// Flaky on Windows and Mac try bots.
-#if defined(OS_CHROMEOS)
-#define MAYBE_DispatchMVChangeEventWhileNavigatingBackToNTP DispatchMVChangeEventWhileNavigatingBackToNTP
-#else
-#define MAYBE_DispatchMVChangeEventWhileNavigatingBackToNTP DISABLED_DispatchMVChangeEventWhileNavigatingBackToNTP
-#endif
+// Flaky: crbug.com/267119
 IN_PROC_BROWSER_TEST_F(InstantExtendedTest,
-                       MAYBE_DispatchMVChangeEventWhileNavigatingBackToNTP) {
+                       DISABLED_DispatchMVChangeEventWhileNavigatingBackToNTP) {
   // Setup Instant.
   ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
   FocusOmniboxAndWaitForInstantNTPSupport();
@@ -1603,7 +1544,9 @@
   EXPECT_EQ(1, on_most_visited_change_calls_);
 }
 
-IN_PROC_BROWSER_TEST_F(InstantExtendedTest, OnDefaultSearchProviderChanged) {
+// Flaky: crbug.com/267096
+IN_PROC_BROWSER_TEST_F(InstantExtendedTest,
+                       DISABLED_OnDefaultSearchProviderChanged) {
   InstantService* instant_service =
       InstantServiceFactory::GetForProfile(browser()->profile());
   ASSERT_NE(static_cast<InstantService*>(NULL), instant_service);
@@ -1715,94 +1658,3 @@
   // Make sure the URL remains the same.
   EXPECT_EQ(ntp_url, ntp_contents->GetURL());
 }
-
-IN_PROC_BROWSER_TEST_F(InstantExtendedTest, ProcessIsolation) {
-  // Prior to setup, Instant has an ntp with a failed "google.com" load in
-  // it, which is rendered in the dedicated Instant renderer process.
-  //
-  // TODO(sreeram): Fix this up when we stop doing crazy things on init.
-  InstantService* instant_service =
-        InstantServiceFactory::GetForProfile(browser()->profile());
-  ASSERT_NE(static_cast<InstantService*>(NULL), instant_service);
-#if !defined(OS_MACOSX)
-  // The failed "google.com" load is deleted, which sometimes leads to the
-  // process shutting down on Mac.
-  EXPECT_EQ(1, instant_service->GetInstantProcessCount());
-#endif
-
-  // Setup Instant.
-  ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
-  FocusOmniboxAndWaitForInstantNTPSupport();
-
-  // The registered Instant render process should still exist.
-  EXPECT_EQ(1, instant_service->GetInstantProcessCount());
-  // And the Instant ntp should live inside it.
-  content::WebContents* ntp_contents =
-      instant_service->ntp_prerenderer()->ntp()->contents();
-  EXPECT_TRUE(instant_service->IsInstantProcess(
-      ntp_contents->GetRenderProcessHost()->GetID()));
-
-  // Navigating to the NTP should use the Instant render process.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(),
-      GURL(chrome::kChromeUINewTabURL),
-      CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_NONE);
-  content::WebContents* active_tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_TRUE(instant_service->IsInstantProcess(
-      active_tab->GetRenderProcessHost()->GetID()));
-
-  // Navigating elsewhere should not use the Instant render process.
-  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIAboutURL));
-  EXPECT_FALSE(instant_service->IsInstantProcess(
-      active_tab->GetRenderProcessHost()->GetID()));
-}
-
-// Test that renderer initiated navigations to an Instant URL from an
-// Instant process end up in an Instant process.
-IN_PROC_BROWSER_TEST_F(InstantExtendedTest,
-                       RendererInitiatedNavigationInInstantProcess) {
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(browser()->profile());
-  ASSERT_NE(static_cast<InstantService*>(NULL), instant_service);
-
-  // Setup Instant.
-  ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
-  FocusOmniboxAndWaitForInstantNTPSupport();
-
-  EXPECT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(),
-      instant_url(),
-      CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_TRUE(instant_service->IsInstantProcess(
-      contents->GetRenderProcessHost()->GetID()));
-
-  std::string instant_url_with_query = instant_url().spec() + "q=3";
-  std::string add_link_script = base::StringPrintf(
-      "var a = document.createElement('a');"
-      "a.id = 'toClick';"
-      "a.href = '%s';"
-      "document.body.appendChild(a);",
-      instant_url_with_query.c_str());
-  EXPECT_TRUE(content::ExecuteScript(contents, add_link_script));
-
-  content::WindowedNotificationObserver observer(
-        content::NOTIFICATION_NAV_ENTRY_COMMITTED,
-        content::NotificationService::AllSources());
-  EXPECT_TRUE(content::ExecuteScript(
-      contents, "document.getElementById('toClick').click();"));
-  observer.Wait();
-
-  EXPECT_EQ(1, browser()->tab_strip_model()->count());
-  contents = browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_TRUE(instant_service->IsInstantProcess(
-      contents->GetRenderProcessHost()->GetID()));
-  EXPECT_EQ(GURL(instant_url_with_query), contents->GetURL());
-}
diff --git a/chrome/browser/ui/search/instant_ipc_sender.cc b/chrome/browser/ui/search/instant_ipc_sender.cc
index a457534..62387e6 100644
--- a/chrome/browser/ui/search/instant_ipc_sender.cc
+++ b/chrome/browser/ui/search/instant_ipc_sender.cc
@@ -79,6 +79,10 @@
         routing_id(), bounds.x(), bounds.width()));
   }
 
+  virtual void ToggleVoiceSearch() OVERRIDE {
+    Send(new ChromeViewMsg_SearchBoxToggleVoiceSearch(routing_id()));
+  }
+
   DISALLOW_COPY_AND_ASSIGN(IncognitoInstantIPCSenderImpl);
 };
 
diff --git a/chrome/browser/ui/search/instant_loader.cc b/chrome/browser/ui/search/instant_loader.cc
index d9e7b49..4daf19a 100644
--- a/chrome/browser/ui/search/instant_loader.cc
+++ b/chrome/browser/ui/search/instant_loader.cc
@@ -47,7 +47,7 @@
                          const base::Closure& on_stale_callback) {
   content::WebContents::CreateParams create_params(profile);
   create_params.site_instance = content::SiteInstance::CreateForURL(
-      profile, chrome::GetPrivilegedURLForInstant(instant_url, profile));
+      profile, instant_url);
   SetContents(scoped_ptr<content::WebContents>(
       content::WebContents::Create(create_params)));
   instant_url_ = instant_url;
diff --git a/chrome/browser/ui/search/instant_ntp_prerenderer.cc b/chrome/browser/ui/search/instant_ntp_prerenderer.cc
index 7b33657..2edfc11 100644
--- a/chrome/browser/ui/search/instant_ntp_prerenderer.cc
+++ b/chrome/browser/ui/search/instant_ntp_prerenderer.cc
@@ -46,8 +46,7 @@
 
 InstantNTPPrerenderer::InstantNTPPrerenderer(Profile* profile,
                                              PrefService* prefs)
-    : profile_(profile),
-      extended_enabled_(chrome::IsInstantExtendedAPIEnabled()) {
+    : profile_(profile) {
   DCHECK(profile);
 
   // In unit tests, prefs may be NULL.
@@ -75,7 +74,7 @@
 }
 
 scoped_ptr<content::WebContents> InstantNTPPrerenderer::ReleaseNTPContents() {
-  if (!extended_enabled() || !profile_ || profile_->IsOffTheRecord() ||
+  if (!profile_ || profile_->IsOffTheRecord() ||
       !chrome::ShouldShowInstantNTP())
     return scoped_ptr<content::WebContents>();
 
@@ -169,19 +168,12 @@
   return ntp_.get();
 }
 
-bool InstantNTPPrerenderer::extended_enabled() const {
-  return extended_enabled_;
-}
-
 void InstantNTPPrerenderer::OnNetworkChanged(
     net::NetworkChangeNotifier::ConnectionType type) {
   // Not interested in events conveying change to offline.
   if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
     return;
 
-  if (!extended_enabled())
-    return;
-
   if (!ntp() || ntp()->IsLocal())
     ResetNTP(GetInstantURL());
 }
@@ -248,7 +240,7 @@
 void InstantNTPPrerenderer::OnDefaultSearchProviderChanged(
     const std::string& pref_name) {
   DCHECK_EQ(pref_name, std::string(prefs::kDefaultSearchProviderID));
-  if (!extended_enabled() || !ntp())
+  if (!ntp())
     return;
 
   ResetNTP(GetInstantURL());
@@ -264,9 +256,6 @@
 }
 
 void InstantNTPPrerenderer::ReloadStaleNTP() {
-  if (!extended_enabled())
-    return;
-
   ResetNTP(GetInstantURL());
 }
 
@@ -290,7 +279,7 @@
 
   // Already a local page. Not calling IsLocal() because we want to distinguish
   // between the Google-specific and generic local NTP.
-  if (extended_enabled() && ntp()->instant_url() == GetLocalInstantURL())
+  if (ntp()->instant_url() == GetLocalInstantURL())
     return false;
 
   if (PageIsCurrent())
diff --git a/chrome/browser/ui/search/instant_ntp_prerenderer.h b/chrome/browser/ui/search/instant_ntp_prerenderer.h
index 8786c1e..8fbaf83 100644
--- a/chrome/browser/ui/search/instant_ntp_prerenderer.h
+++ b/chrome/browser/ui/search/instant_ntp_prerenderer.h
@@ -75,7 +75,6 @@
 
   // Accessors are made protected for testing purposes.
   virtual InstantNTP* ntp() const;
-  virtual bool extended_enabled() const;
 
   Profile* profile() const {
     return profile_;
@@ -155,9 +154,6 @@
 
   Profile* profile_;
 
-  // Whether the extended API is enabled.
-  const bool extended_enabled_;
-
   // Preloaded InstantNTP.
   scoped_ptr<InstantNTP> ntp_;
 
diff --git a/chrome/browser/ui/search/instant_ntp_prerenderer_unittest.cc b/chrome/browser/ui/search/instant_ntp_prerenderer_unittest.cc
index 0535657..917e01a 100644
--- a/chrome/browser/ui/search/instant_ntp_prerenderer_unittest.cc
+++ b/chrome/browser/ui/search/instant_ntp_prerenderer_unittest.cc
@@ -65,7 +65,6 @@
   explicit TestableInstantNTPPrerenderer(TestingProfile* profile)
       : InstantNTPPrerenderer(profile, NULL),
         test_instant_url_("http://test_url"),
-        test_extended_enabled_(true),
         override_javascript_enabled_(true),
         test_javascript_enabled_(true),
         test_in_startup_(false),
@@ -80,10 +79,6 @@
     return "http://local_instant_url";
   }
 
-  virtual bool extended_enabled() const OVERRIDE {
-    return test_extended_enabled_;
-  }
-
   virtual InstantNTP* ntp() const OVERRIDE {
     return test_ntp_;
   }
@@ -103,10 +98,6 @@
     test_instant_url_ = instant_url;
   }
 
-  void set_extended_enabled(bool extended_enabled) {
-    test_extended_enabled_ = extended_enabled;
-  }
-
   void set_ntp(InstantNTP* ntp) {
     test_ntp_ = ntp;
   }
@@ -126,7 +117,6 @@
 
 private:
   std::string test_instant_url_;
-  bool test_extended_enabled_;
   bool override_javascript_enabled_;
   bool test_javascript_enabled_;
   bool test_in_startup_;
@@ -138,6 +128,7 @@
   InstantNTPPrerendererTest()
       : instant_ntp_prerenderer_(new TestableInstantNTPPrerenderer(&profile_)) {
     base::StatisticsRecorder::Initialize();
+    chrome::EnableInstantExtendedAPIForTesting();
   }
 
   TestableInstantNTPPrerenderer* instant_ntp_prerenderer() {
@@ -161,7 +152,6 @@
   ntp->set_is_local(false);
   instant_ntp_prerenderer()->set_ntp(ntp.get());
   instant_ntp_prerenderer()->set_javascript_enabled(true);
-  instant_ntp_prerenderer()->set_extended_enabled(true);
   instant_ntp_prerenderer()->set_instant_url(instant_url);
   ntp->set_instant_url(instant_url);
   ntp->set_supports_instant(false);
@@ -179,7 +169,6 @@
   ntp->set_is_local(false);
   instant_ntp_prerenderer()->set_ntp(ntp.get());
   instant_ntp_prerenderer()->set_javascript_enabled(true);
-  instant_ntp_prerenderer()->set_extended_enabled(true);
   instant_ntp_prerenderer()->set_instant_url(instant_url);
   ntp->set_instant_url(instant_url);
   ntp->set_supports_instant(false);
@@ -196,7 +185,6 @@
   ntp->set_is_local(false);
   instant_ntp_prerenderer()->set_ntp(ntp.get());
   instant_ntp_prerenderer()->set_javascript_enabled(true);
-  instant_ntp_prerenderer()->set_extended_enabled(true);
   instant_ntp_prerenderer()->set_instant_url("http://bogus_url");
   ntp->set_instant_url(instant_url);
   ntp->set_supports_instant(true);
@@ -213,7 +201,6 @@
   ntp->set_is_local(false);
   instant_ntp_prerenderer()->set_ntp(ntp.get());
   instant_ntp_prerenderer()->set_javascript_enabled(true);
-  instant_ntp_prerenderer()->set_extended_enabled(true);
   instant_ntp_prerenderer()->set_instant_url(instant_url);
   ntp->set_instant_url(instant_url);
   ntp->set_supports_instant(true);
@@ -230,7 +217,6 @@
   ntp->set_is_local(false);
   instant_ntp_prerenderer()->set_ntp(ntp.get());
   instant_ntp_prerenderer()->set_javascript_enabled(true);
-  instant_ntp_prerenderer()->set_extended_enabled(true);
   instant_ntp_prerenderer()->set_instant_url(instant_url);
   ntp->set_instant_url("http://local_instant_url");
   ntp->set_supports_instant(true);
@@ -247,7 +233,6 @@
   ntp->set_is_local(false);
   instant_ntp_prerenderer()->set_ntp(ntp.get());
   instant_ntp_prerenderer()->set_javascript_enabled(false);
-  instant_ntp_prerenderer()->set_extended_enabled(true);
   instant_ntp_prerenderer()->set_instant_url(instant_url);
   ntp->set_instant_url("http://local_instant_url");
   ntp->set_supports_instant(true);
diff --git a/chrome/browser/ui/search/instant_page_unittest.cc b/chrome/browser/ui/search/instant_page_unittest.cc
index 2b1ec86..3a537c4 100644
--- a/chrome/browser/ui/search/instant_page_unittest.cc
+++ b/chrome/browser/ui/search/instant_page_unittest.cc
@@ -332,6 +332,8 @@
   EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxSubmit::ID));
   page->sender()->SetOmniboxBounds(gfx::Rect());
   EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxMarginChange::ID));
+  page->sender()->ToggleVoiceSearch();
+  EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxToggleVoiceSearch::ID));
 
   // Incognito pages should not get any others.
   page->sender()->SetFontInformation(string16(), 0);
@@ -350,7 +352,4 @@
   page->sender()->SendMostVisitedItems(std::vector<InstantMostVisitedItem>());
   EXPECT_FALSE(MessageWasSent(
       ChromeViewMsg_SearchBoxMostVisitedItemsChanged::ID));
-
-  page->sender()->ToggleVoiceSearch();
-  EXPECT_FALSE(MessageWasSent(ChromeViewMsg_SearchBoxToggleVoiceSearch::ID));
 }
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index d811aff..da42ae6 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -30,7 +30,8 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_F(LocalNTPTest, LocalNTPJavascriptTest) {
+// Flaky: crbug.com/267117
+IN_PROC_BROWSER_TEST_F(LocalNTPTest, DISABLED_LocalNTPJavascriptTest) {
   ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
   FocusOmniboxAndWaitForInstantNTPSupport();
 
diff --git a/chrome/browser/ui/singleton_tabs.cc b/chrome/browser/ui/singleton_tabs.cc
index 10648de..608c299 100644
--- a/chrome/browser/ui/singleton_tabs.cc
+++ b/chrome/browser/ui/singleton_tabs.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/singleton_tabs.h"
 
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/search.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -43,12 +44,13 @@
 
 void ShowSingletonTabOverwritingNTP(Browser* browser,
                                     const NavigateParams& params) {
+  DCHECK(browser);
   NavigateParams local_params(params);
   content::WebContents* contents =
       browser->tab_strip_model()->GetActiveWebContents();
   if (contents) {
     const GURL& contents_url = contents->GetURL();
-    if ((contents_url == GURL(kChromeUINewTabURL) ||
+    if ((contents_url == GURL(kChromeUINewTabURL) || IsInstantNTP(contents) ||
          contents_url == GURL(content::kAboutBlankURL)) &&
         GetIndexOfSingletonTab(&local_params) < 0) {
       local_params.disposition = CURRENT_TAB;
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index f34e7e4..d21a223 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/profiles/profile_impl.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/sessions/session_restore.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_iterator.h"
@@ -29,12 +30,12 @@
 #include "chrome/browser/ui/host_desktop.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -57,10 +58,6 @@
 #include "policy/policy_constants.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 using testing::_;
 using testing::AnyNumber;
 using testing::Return;
@@ -90,11 +87,6 @@
 class StartupBrowserCreatorTest : public ExtensionBrowserTest {
  protected:
   virtual bool SetUpUserDataDirectory() OVERRIDE {
-    // Make sure the first run sentinel file exists before running these tests,
-    // since some of them customize the session startup pref whose value can
-    // be different than the default during the first run.
-    // TODO(bauerb): set the first run flag instead of creating a sentinel file.
-    first_run::CreateSentinel();
     return ExtensionBrowserTest::SetUpUserDataDirectory();
   }
 
@@ -459,14 +451,14 @@
 IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest, SyncPromoNoWelcomePage) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
   // Trick this test into thinking the promo has been shown for this profile; so
   // that it will show it again (otherwise it skips showing it since
   // --no-first-run is specified in browser tests).
-  SyncPromoUI::DidShowSyncPromoAtStartup(browser()->profile());
+  signin::DidShowPromoAtStartup(browser()->profile());
 
   // Do a simple non-process-startup browser launch.
   CommandLine dummy(CommandLine::NO_PROGRAM);
@@ -482,7 +474,7 @@
   TabStripModel* tab_strip = new_browser->tab_strip_model();
   EXPECT_EQ(1, tab_strip->count());
 
-  if (SyncPromoUI::ShouldShowSyncPromoAtStartup(browser()->profile(), true)) {
+  if (signin::ShouldShowPromoAtStartup(browser()->profile(), true)) {
     EXPECT_EQ("signin", tab_strip->GetWebContentsAt(0)->GetURL().host());
   } else {
     EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
@@ -493,14 +485,14 @@
 IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest, SyncPromoWithWelcomePage) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
   // Trick this test into thinking the promo has been shown for this profile; so
   // that it will show it again (otherwise it skips showing it since
   // --no-first-run is specified in browser tests).
-  SyncPromoUI::DidShowSyncPromoAtStartup(browser()->profile());
+  signin::DidShowPromoAtStartup(browser()->profile());
   first_run::SetShouldShowWelcomePage();
 
   // Do a simple non-process-startup browser launch.
@@ -517,7 +509,7 @@
   TabStripModel* tab_strip = new_browser->tab_strip_model();
   EXPECT_EQ(2, tab_strip->count());
 
-  if (SyncPromoUI::ShouldShowSyncPromoAtStartup(browser()->profile(), true)) {
+  if (signin::ShouldShowPromoAtStartup(browser()->profile(), true)) {
     EXPECT_EQ("signin", tab_strip->GetWebContentsAt(0)->GetURL().host());
   } else {
     EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
@@ -530,7 +522,7 @@
 IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest, SyncPromoWithFirstRunTabs) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -540,7 +532,7 @@
   // Trick this test into thinking the promo has been shown for this profile; so
   // that it will show it again (otherwise it skips showing it since
   // --no-first-run is specified in browser tests).
-  SyncPromoUI::DidShowSyncPromoAtStartup(browser()->profile());
+  signin::DidShowPromoAtStartup(browser()->profile());
 
   // The welcome page should not be shown, even if
   // first_run::ShouldShowWelcomePage() says so, when there are already
@@ -559,7 +551,7 @@
   ASSERT_TRUE(new_browser);
 
   TabStripModel* tab_strip = new_browser->tab_strip_model();
-  if (SyncPromoUI::ShouldShowSyncPromoAtStartup(browser()->profile(), true)) {
+  if (signin::ShouldShowPromoAtStartup(browser()->profile(), true)) {
     EXPECT_EQ(2, tab_strip->count());
     EXPECT_EQ("signin", tab_strip->GetWebContentsAt(0)->GetURL().host());
     EXPECT_EQ("title1.html",
@@ -577,7 +569,7 @@
                        SyncPromoWithFirstRunTabsIncludingWelcomePage) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -588,7 +580,7 @@
   // Trick this test into thinking the promo has been shown for this profile; so
   // that it will show it again (otherwise it skips showing it since
   // --no-first-run is specified in browser tests).
-  SyncPromoUI::DidShowSyncPromoAtStartup(browser()->profile());
+  signin::DidShowPromoAtStartup(browser()->profile());
 
   // Do a simple non-process-startup browser launch.
   CommandLine dummy(CommandLine::NO_PROGRAM);
@@ -602,7 +594,7 @@
   ASSERT_TRUE(new_browser);
 
   TabStripModel* tab_strip = new_browser->tab_strip_model();
-  if (SyncPromoUI::ShouldShowSyncPromoAtStartup(browser()->profile(), true)) {
+  if (signin::ShouldShowPromoAtStartup(browser()->profile(), true)) {
     EXPECT_EQ(3, tab_strip->count());
     EXPECT_EQ("signin", tab_strip->GetWebContentsAt(0)->GetURL().host());
     EXPECT_EQ("title1.html",
@@ -622,7 +614,7 @@
 IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest, StartupURLsForTwoProfiles) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -730,7 +722,7 @@
 IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest, UpdateWithTwoProfiles) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -795,7 +787,7 @@
                        ProfilesWithoutPagesNotLaunched) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -900,7 +892,7 @@
 IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest, ProfilesLaunchedAfterCrash) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -1210,9 +1202,8 @@
   // }
   StartupBrowserCreator browser_creator;
   browser_creator.AddFirstRunTab(test_server()->GetURL("files/title1.html"));
-  browser_creator.AddFirstRunTab(SyncPromoUI::GetSyncPromoURL(
-      SyncPromoUI::SOURCE_START_PAGE,
-      false));
+  browser_creator.AddFirstRunTab(signin::GetPromoURL(signin::SOURCE_START_PAGE,
+                                                     false));
   browser()->profile()->GetPrefs()->SetBoolean(
       prefs::kSyncPromoShowOnFirstRunAllowed, true);
 
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
index 5f7041b..82e7d35 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -48,6 +48,7 @@
 #include "chrome/browser/sessions/session_service.h"
 #include "chrome/browser/sessions/session_service_factory.h"
 #include "chrome/browser/shell_integration.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/app_list/app_list_service.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -65,7 +66,6 @@
 #include "chrome/browser/ui/startup/obsolete_os_infobar_delegate.h"
 #include "chrome/browser/ui/startup/session_crashed_infobar_delegate.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/tabs/pinned_tab_codec.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
@@ -933,11 +933,11 @@
       startup_urls->push_back(internals::GetWelcomePageURL());
   }
 
-  if (SyncPromoUI::ShouldShowSyncPromoAtStartup(profile_, is_first_run_)) {
-    SyncPromoUI::DidShowSyncPromoAtStartup(profile_);
+  if (signin::ShouldShowPromoAtStartup(profile_, is_first_run_)) {
+    signin::DidShowPromoAtStartup(profile_);
 
-    const GURL sync_promo_url = SyncPromoUI::GetSyncPromoURL(
-        SyncPromoUI::SOURCE_START_PAGE, false);
+    const GURL sync_promo_url = signin::GetPromoURL(signin::SOURCE_START_PAGE,
+                                                    false);
 
     // No need to add if the sync promo is already in the startup list.
     bool add_promo = true;
diff --git a/chrome/browser/ui/sync/one_click_signin_bubble_delegate.h b/chrome/browser/ui/sync/one_click_signin_bubble_delegate.h
new file mode 100644
index 0000000..4b5f440
--- /dev/null
+++ b/chrome/browser/ui/sync/one_click_signin_bubble_delegate.h
@@ -0,0 +1,15 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_BUBBLE_DELEGATE_H_
+#define CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_BUBBLE_DELEGATE_H_
+
+class OneClickSigninBubbleDelegate {
+ public:
+  virtual ~OneClickSigninBubbleDelegate() {}
+  virtual void OnLearnMoreLinkClicked(bool is_dialog) = 0;
+  virtual void OnAdvancedLinkClicked() = 0;
+};
+
+#endif  // CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_BUBBLE_DELEGATE_H_
diff --git a/chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.cc b/chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.cc
new file mode 100644
index 0000000..988891d
--- /dev/null
+++ b/chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.cc
@@ -0,0 +1,31 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.h"
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/common/url_constants.h"
+
+OneClickSigninBubbleLinksDelegate::OneClickSigninBubbleLinksDelegate(
+    Browser* browser) : browser_(browser) {}
+
+OneClickSigninBubbleLinksDelegate::~OneClickSigninBubbleLinksDelegate() {}
+
+void OneClickSigninBubbleLinksDelegate::OnLearnMoreLinkClicked(
+    bool is_dialog) {
+  chrome::NavigateParams params(browser_,
+                                GURL(chrome::kChromeSyncLearnMoreURL),
+                                content::PAGE_TRANSITION_LINK);
+  params.disposition = is_dialog ? NEW_WINDOW : NEW_FOREGROUND_TAB;
+  chrome::Navigate(&params);
+}
+
+void OneClickSigninBubbleLinksDelegate::OnAdvancedLinkClicked() {
+  chrome::NavigateParams params(browser_,
+                                GURL(chrome::kChromeUISettingsURL),
+                                content::PAGE_TRANSITION_LINK);
+  params.disposition = CURRENT_TAB;
+  chrome::Navigate(&params);
+}
diff --git a/chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.h b/chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.h
new file mode 100644
index 0000000..e306709
--- /dev/null
+++ b/chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.h
@@ -0,0 +1,31 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_BUBBLE_LINKS_DELEGATE_H_
+#define CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_BUBBLE_LINKS_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/ui/sync/one_click_signin_bubble_delegate.h"
+
+class Browser;
+
+class OneClickSigninBubbleLinksDelegate : public OneClickSigninBubbleDelegate {
+ public:
+  // |browser| must outlive the delegate.
+  explicit OneClickSigninBubbleLinksDelegate(Browser* browser);
+  virtual ~OneClickSigninBubbleLinksDelegate();
+
+ private:
+  // OneClickSigninBubbleDelegate:
+  virtual void OnLearnMoreLinkClicked(bool is_dialog) OVERRIDE;
+  virtual void OnAdvancedLinkClicked() OVERRIDE;
+
+  // Browser in which the links should be opened.
+  Browser* browser_;
+
+  DISALLOW_COPY_AND_ASSIGN(OneClickSigninBubbleLinksDelegate);
+};
+
+#endif  // CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_BUBBLE_LINKS_DELEGATE_H_
diff --git a/chrome/browser/ui/sync/one_click_signin_bubble_links_delegate_browsertest.cc b/chrome/browser/ui/sync/one_click_signin_bubble_links_delegate_browsertest.cc
new file mode 100644
index 0000000..4ffa307
--- /dev/null
+++ b/chrome/browser/ui/sync/one_click_signin_bubble_links_delegate_browsertest.cc
@@ -0,0 +1,39 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.h"
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+
+typedef InProcessBrowserTest OneClickSigninBubbleLinksDelegateBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleLinksDelegateBrowserTest,
+                       AdvancedLink) {
+  scoped_ptr<OneClickSigninBubbleDelegate> delegate_;
+  delegate_.reset(new OneClickSigninBubbleLinksDelegate(browser()));
+
+  int starting_tab_count = browser()->tab_strip_model()->count();
+
+  // The current tab should be replaced.
+  delegate_->OnAdvancedLinkClicked();
+
+  int tab_count = browser()->tab_strip_model()->count();
+  EXPECT_EQ(starting_tab_count, tab_count);
+}
+
+IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleLinksDelegateBrowserTest,
+                       LearnMoreLink) {
+  scoped_ptr<OneClickSigninBubbleDelegate> delegate_;
+  delegate_.reset(new OneClickSigninBubbleLinksDelegate(browser()));
+
+  int starting_tab_count = browser()->tab_strip_model()->count();
+
+  // A new tab should be opened.
+  delegate_->OnLearnMoreLinkClicked(false);
+
+  int tab_count = browser()->tab_strip_model()->count();
+  EXPECT_EQ(starting_tab_count + 1, tab_count);
+}
diff --git a/chrome/browser/ui/sync/one_click_signin_helper.cc b/chrome/browser/ui/sync/one_click_signin_helper.cc
index 5573022..ec2540a 100644
--- a/chrome/browser/ui/sync/one_click_signin_helper.cc
+++ b/chrome/browser/ui/sync/one_click_signin_helper.cc
@@ -13,6 +13,7 @@
 #include "base/callback_forward.h"
 #include "base/callback_helpers.h"
 #include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram.h"
 #include "base/prefs/pref_service.h"
@@ -94,7 +95,8 @@
                 const std::string& password,
                 bool force_same_tab_navigation,
                 bool untrusted_confirmation_required,
-                SyncPromoUI::Source source);
+                signin::Source source,
+                OneClickSigninSyncStarter::Callback callback);
 
   Profile* profile;
   Browser* browser;
@@ -104,7 +106,8 @@
   std::string password;
   bool force_same_tab_navigation;
   OneClickSigninSyncStarter::ConfirmationRequired confirmation_required;
-  SyncPromoUI::Source source;
+  signin::Source source;
+  OneClickSigninSyncStarter::Callback callback;
 };
 
 StartSyncArgs::StartSyncArgs(Profile* profile,
@@ -115,7 +118,8 @@
                              const std::string& password,
                              bool force_same_tab_navigation,
                              bool untrusted_confirmation_required,
-                             SyncPromoUI::Source source)
+                             signin::Source source,
+                             OneClickSigninSyncStarter::Callback callback)
     : profile(profile),
       browser(browser),
       auto_accept(auto_accept),
@@ -123,11 +127,12 @@
       email(email),
       password(password),
       force_same_tab_navigation(force_same_tab_navigation),
-      source(source) {
+      source(source),
+      callback(callback) {
   if (untrusted_confirmation_required) {
     confirmation_required = OneClickSigninSyncStarter::CONFIRM_UNTRUSTED_SIGNIN;
-  } else if (source == SyncPromoUI::SOURCE_SETTINGS ||
-             source == SyncPromoUI::SOURCE_WEBSTORE_INSTALL) {
+  } else if (source == signin::SOURCE_SETTINGS ||
+             source == signin::SOURCE_WEBSTORE_INSTALL) {
     // Do not display a status confirmation for webstore installs or re-auth.
     confirmation_required = OneClickSigninSyncStarter::NO_CONFIRMATION;
   } else {
@@ -147,47 +152,47 @@
   updater->AppendIfNotPresent(new base::StringValue(email));
 }
 
-void LogHistogramValue(SyncPromoUI::Source source, int action) {
+void LogHistogramValue(signin::Source source, int action) {
   switch (source) {
-    case SyncPromoUI::SOURCE_START_PAGE:
+    case signin::SOURCE_START_PAGE:
       UMA_HISTOGRAM_ENUMERATION("Signin.StartPageActions", action,
                                 one_click_signin::HISTOGRAM_MAX);
       break;
-    case SyncPromoUI::SOURCE_NTP_LINK:
+    case signin::SOURCE_NTP_LINK:
       UMA_HISTOGRAM_ENUMERATION("Signin.NTPLinkActions", action,
                                 one_click_signin::HISTOGRAM_MAX);
       break;
-    case SyncPromoUI::SOURCE_MENU:
+    case signin::SOURCE_MENU:
       UMA_HISTOGRAM_ENUMERATION("Signin.MenuActions", action,
                                 one_click_signin::HISTOGRAM_MAX);
       break;
-    case SyncPromoUI::SOURCE_SETTINGS:
+    case signin::SOURCE_SETTINGS:
       UMA_HISTOGRAM_ENUMERATION("Signin.SettingsActions", action,
                                 one_click_signin::HISTOGRAM_MAX);
       break;
-    case SyncPromoUI::SOURCE_EXTENSION_INSTALL_BUBBLE:
+    case signin::SOURCE_EXTENSION_INSTALL_BUBBLE:
       UMA_HISTOGRAM_ENUMERATION("Signin.ExtensionInstallBubbleActions", action,
                                 one_click_signin::HISTOGRAM_MAX);
       break;
-    case SyncPromoUI::SOURCE_WEBSTORE_INSTALL:
+    case signin::SOURCE_WEBSTORE_INSTALL:
       UMA_HISTOGRAM_ENUMERATION("Signin.WebstoreInstallActions", action,
                                 one_click_signin::HISTOGRAM_MAX);
       break;
-    case SyncPromoUI::SOURCE_APP_LAUNCHER:
+    case signin::SOURCE_APP_LAUNCHER:
       UMA_HISTOGRAM_ENUMERATION("Signin.AppLauncherActions", action,
                                 one_click_signin::HISTOGRAM_MAX);
       break;
-    case SyncPromoUI::SOURCE_APPS_PAGE_LINK:
+    case signin::SOURCE_APPS_PAGE_LINK:
       UMA_HISTOGRAM_ENUMERATION("Signin.AppsPageLinkActions", action,
                                 one_click_signin::HISTOGRAM_MAX);
       break;
-    case SyncPromoUI::SOURCE_BOOKMARK_BUBBLE:
+    case signin::SOURCE_BOOKMARK_BUBBLE:
       UMA_HISTOGRAM_ENUMERATION("Signin.BookmarkBubbleActions", action,
                                 one_click_signin::HISTOGRAM_MAX);
       break;
     default:
       // This switch statement needs to be updated when the enum Source changes.
-      COMPILE_ASSERT(SyncPromoUI::SOURCE_UNKNOWN == 9,
+      COMPILE_ASSERT(signin::SOURCE_UNKNOWN == 9,
                      kSourceEnumHasChangedButNotThisSwitchStatement);
       NOTREACHED();
       return;
@@ -204,10 +209,10 @@
 }
 
 void RedirectToNtpOrAppsPage(content::WebContents* contents,
-                             SyncPromoUI::Source source) {
+                             signin::Source source) {
   VLOG(1) << "RedirectToNtpOrAppsPage";
   // Redirect to NTP/Apps page and display a confirmation bubble
-  GURL url(source == SyncPromoUI::SOURCE_APPS_PAGE_LINK ?
+  GURL url(source == signin::SOURCE_APPS_PAGE_LINK ?
            chrome::kChromeUIAppsURL : chrome::kChromeUINewTabURL);
   content::OpenURLParams params(url,
                                 content::Referrer(),
@@ -220,9 +225,9 @@
 // If the |source| is not settings page/webstore, redirects to
 // the NTP/Apps page.
 void RedirectToNtpOrAppsPageIfNecessary(content::WebContents* contents,
-                                        SyncPromoUI::Source source) {
-  if (source != SyncPromoUI::SOURCE_SETTINGS &&
-      source != SyncPromoUI::SOURCE_WEBSTORE_INSTALL) {
+                                        signin::Source source) {
+  if (source != signin::SOURCE_SETTINGS &&
+      source != signin::SOURCE_WEBSTORE_INSTALL) {
     RedirectToNtpOrAppsPage(contents, source);
   }
 }
@@ -240,7 +245,8 @@
                                 args.email, args.password, start_mode,
                                 args.force_same_tab_navigation,
                                 args.confirmation_required,
-                                args.source);
+                                args.source,
+                                args.callback);
 
   int action = one_click_signin::HISTOGRAM_MAX;
   switch (args.auto_accept) {
@@ -292,7 +298,7 @@
 // of the known sign in access point (first run, NTP, Apps page, menu, settings)
 // or its an implicit sign in via another Google property.  In the former case,
 // "service" is also checked to make sure its "chromiumsync".
-SyncPromoUI::Source GetSigninSource(const GURL& url, GURL* continue_url) {
+signin::Source GetSigninSource(const GURL& url, GURL* continue_url) {
   std::string value;
   net::GetValueForKeyInQuery(url, "service", &value);
   bool possibly_an_explicit_signin = value == "chromiumsync";
@@ -302,10 +308,10 @@
   // of layers of indirection.  Peel those layers away.  The final destination
   // can also be "IsGaiaSignonRealm" so stop if we get to the end (but be sure
   // we always extract at least one "continue" value).
-  GURL local_continue_url = SyncPromoUI::GetNextPageURLForSyncPromoURL(url);
+  GURL local_continue_url = signin::GetNextPageURLForPromoURL(url);
   while (gaia::IsGaiaSignonRealm(local_continue_url.GetOrigin())) {
     GURL next_continue_url =
-        SyncPromoUI::GetNextPageURLForSyncPromoURL(local_continue_url);
+        signin::GetNextPageURLForPromoURL(local_continue_url);
     if (!next_continue_url.is_valid())
       break;
     local_continue_url = next_continue_url;
@@ -317,8 +323,8 @@
   }
 
   return possibly_an_explicit_signin ?
-      SyncPromoUI::GetSourceForSyncPromoURL(local_continue_url) :
-      SyncPromoUI::SOURCE_UNKNOWN;
+      signin::GetSourceForPromoURL(local_continue_url) :
+      signin::SOURCE_UNKNOWN;
 }
 
 // Returns true if |url| is a valid URL that can occur during the sign in
@@ -355,8 +361,7 @@
 // or the one click sign in to chrome page.
 // NOTE: This should only be used for logging purposes since it relies on hard
 // coded URLs that could change.
-bool AreWeShowingSignin(GURL url, SyncPromoUI::Source source,
-                        std::string email) {
+bool AreWeShowingSignin(GURL url, signin::Source source, std::string email) {
   GURL::Replacements replacements;
   replacements.ClearQuery();
   GURL clean_login_url =
@@ -364,7 +369,7 @@
           replacements);
 
   return (url.ReplaceComponents(replacements) == clean_login_url &&
-          source != SyncPromoUI::SOURCE_UNKNOWN) ||
+          source != signin::SOURCE_UNKNOWN) ||
       (IsValidGaiaSigninRedirectOrResponseURL(url) &&
        url.spec().find("ChromeLoginPrompt") != std::string::npos &&
        !email.empty());
@@ -516,7 +521,7 @@
   if (history_index_to_remove_ < nc->GetLastCommittedEntryIndex()) {
     content::NavigationEntry* entry =
         nc->GetEntryAtIndex(history_index_to_remove_);
-    if (SyncPromoUI::IsContinueUrlForWebBasedSigninFlow(entry->GetURL()) &&
+    if (signin::IsContinueUrlForWebBasedSigninFlow(entry->GetURL()) &&
         nc->RemoveEntryAtIndex(history_index_to_remove_)) {
       delete this;  // Success.
     }
@@ -556,11 +561,12 @@
     : content::WebContentsObserver(web_contents),
       showing_signin_(false),
       auto_accept_(AUTO_ACCEPT_NONE),
-      source_(SyncPromoUI::SOURCE_UNKNOWN),
+      source_(signin::SOURCE_UNKNOWN),
       switched_to_advanced_(false),
       untrusted_navigations_since_signin_visit_(0),
       untrusted_confirmation_required_(false),
-      do_not_clear_pending_email_(false) {
+      do_not_clear_pending_email_(false),
+      weak_pointer_factory_(this) {
 }
 
 OneClickSigninHelper::~OneClickSigninHelper() {
@@ -797,7 +803,7 @@
 
   // Parse Google-Chrome-SignIn.
   AutoAccept auto_accept = AUTO_ACCEPT_NONE;
-  SyncPromoUI::Source source = SyncPromoUI::SOURCE_UNKNOWN;
+  signin::Source source = signin::SOURCE_UNKNOWN;
   GURL continue_url;
   std::vector<std::string> tokens;
   base::SplitString(google_chrome_signin_value, ',', &tokens);
@@ -815,7 +821,7 @@
   // If this is an explicit sign in (i.e., first run, NTP, Apps page, menu,
   // settings) then force the auto accept type to explicit.
   source = GetSigninSource(request->url(), &continue_url);
-  if (source != SyncPromoUI::SOURCE_UNKNOWN)
+  if (source != signin::SOURCE_UNKNOWN)
     auto_accept = AUTO_ACCEPT_EXPLICIT;
 
   if (auto_accept != AUTO_ACCEPT_NONE) {
@@ -851,7 +857,7 @@
     const std::string& session_index,
     const std::string& email,
     AutoAccept auto_accept,
-    SyncPromoUI::Source source,
+    signin::Source source,
     const GURL& continue_url,
     int child_id,
     int route_id) {
@@ -872,8 +878,8 @@
   if (auto_accept != AUTO_ACCEPT_NONE)
     helper->auto_accept_ = auto_accept;
 
-  if (source != SyncPromoUI::SOURCE_UNKNOWN &&
-      helper->source_ == SyncPromoUI::SOURCE_UNKNOWN) {
+  if (source != signin::SOURCE_UNKNOWN &&
+      helper->source_ == signin::SOURCE_UNKNOWN) {
     helper->source_ = source;
   }
 
@@ -930,7 +936,7 @@
 void OneClickSigninHelper::RemoveSigninRedirectURLHistoryItem(
     content::WebContents* web_contents) {
   // Only actually remove the item if it's the blank.html continue url.
-  if (SyncPromoUI::IsContinueUrlForWebBasedSigninFlow(
+  if (signin::IsContinueUrlForWebBasedSigninFlow(
           web_contents->GetLastCommittedURL())) {
     new CurrentHistoryCleaner(web_contents);  // will self-destruct when done
   }
@@ -954,11 +960,10 @@
   VLOG(1) << "OneClickSigninHelper::RedirectToSignin";
 
   // Extract the existing sounce=X value.  Default to "2" if missing.
-  SyncPromoUI::Source source =
-      SyncPromoUI::GetSourceForSyncPromoURL(continue_url_);
-  if (source == SyncPromoUI::SOURCE_UNKNOWN)
-    source = SyncPromoUI::SOURCE_MENU;
-  GURL page = SyncPromoUI::GetSyncPromoURL(source, false);
+  signin::Source source = signin::GetSourceForPromoURL(continue_url_);
+  if (source == signin::SOURCE_UNKNOWN)
+    source = signin::SOURCE_MENU;
+  GURL page = signin::GetPromoURL(source, false);
 
   content::WebContents* contents = web_contents();
   contents->GetController().LoadURL(page,
@@ -973,7 +978,7 @@
   email_.clear();
   password_.clear();
   auto_accept_ = AUTO_ACCEPT_NONE;
-  source_ = SyncPromoUI::SOURCE_UNKNOWN;
+  source_ = signin::SOURCE_UNKNOWN;
   switched_to_advanced_ = false;
   continue_url_ = GURL();
   untrusted_navigations_since_signin_visit_ = 0;
@@ -1029,10 +1034,8 @@
   // clear the internal state.  This is needed to detect navigations in the
   // middle of the sign in process that may redirect back to the sign in
   // process (see crbug.com/181163 for details).
-  const GURL continue_url =
-      SyncPromoUI::GetNextPageURLForSyncPromoURL(
-          SyncPromoUI::GetSyncPromoURL(SyncPromoUI::SOURCE_START_PAGE,
-                                       false));
+  const GURL continue_url = signin::GetNextPageURLForPromoURL(
+      signin::GetPromoURL(signin::SOURCE_START_PAGE, false));
   GURL::Replacements replacements;
   replacements.ClearQuery();
 
@@ -1092,7 +1095,7 @@
 
   if (AreWeShowingSignin(url, source_, email_)) {
     if (!showing_signin_) {
-      if (source_ == SyncPromoUI::SOURCE_UNKNOWN)
+      if (source_ == signin::SOURCE_UNKNOWN)
         LogOneClickHistogramValue(one_click_signin::HISTOGRAM_SHOWN);
       else
         LogHistogramValue(source_, one_click_signin::HISTOGRAM_SHOWN);
@@ -1122,7 +1125,7 @@
       RedirectToSignin();
     std::string unused_value;
     if (net::GetValueForKeyInQuery(url, "ntp", &unused_value)) {
-      SyncPromoUI::SetUserSkippedSyncPromo(profile);
+      signin::SetUserSkippedPromo(profile);
       RedirectToNtpOrAppsPage(web_contents(), source_);
     }
 
@@ -1157,7 +1160,7 @@
   // may itself lead to a redirect, which means this function will never see
   // the continue URL go by.
   if (auto_accept_ == AUTO_ACCEPT_EXPLICIT) {
-    DCHECK(source_ != SyncPromoUI::SOURCE_UNKNOWN);
+    DCHECK(source_ != signin::SOURCE_UNKNOWN);
     if (!continue_url_match) {
       VLOG(1) << "OneClickSigninHelper::DidStopLoading: invalid url='"
               << url.spec()
@@ -1175,12 +1178,11 @@
     // If the source was changed to SOURCE_SETTINGS, we want
     // OneClickSigninSyncStarter to reuse the current tab to display the
     // advanced configuration.
-    SyncPromoUI::Source source =
-        SyncPromoUI::GetSourceForSyncPromoURL(url);
+    signin::Source source = signin::GetSourceForPromoURL(url);
     if (source != source_) {
       source_ = source;
-      force_same_tab_navigation = source == SyncPromoUI::SOURCE_SETTINGS;
-      switched_to_advanced_ = source == SyncPromoUI::SOURCE_SETTINGS;
+      force_same_tab_navigation = source == signin::SOURCE_SETTINGS;
+      switched_to_advanced_ = source == signin::SOURCE_SETTINGS;
     }
   }
 
@@ -1204,7 +1206,8 @@
       StartSync(StartSyncArgs(profile, browser, auto_accept_,
                               session_index_, email_, password_,
                               false /* force_same_tab_navigation */,
-                              true /* confirmation_required */, source_),
+                              true /* confirmation_required */, source_,
+                              CreateSyncStarterCallback()),
                 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS);
       break;
     case AUTO_ACCEPT_CONFIGURE:
@@ -1216,12 +1219,13 @@
       StartSync(
           StartSyncArgs(profile, browser, auto_accept_, session_index_, email_,
                         password_, false /* force_same_tab_navigation */,
-                        true /* confirmation_required */, source_),
+                        true /* confirmation_required */, source_,
+                        CreateSyncStarterCallback()),
           OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST);
       break;
     case AUTO_ACCEPT_EXPLICIT: {
-      SyncPromoUI::Source original_source =
-          SyncPromoUI::GetSourceForSyncPromoURL(original_continue_url_);
+      signin::Source original_source =
+          signin::GetSourceForPromoURL(original_continue_url_);
       if (switched_to_advanced_) {
         LogHistogramValue(original_source,
                           one_click_signin::HISTOGRAM_WITH_ADVANCED);
@@ -1239,7 +1243,7 @@
       // - If sign in was initiated from the settings page due to a re-auth,
       //   simply navigate back to the settings page.
       OneClickSigninSyncStarter::StartSyncMode start_mode =
-          source_ == SyncPromoUI::SOURCE_SETTINGS ?
+          source_ == signin::SOURCE_SETTINGS ?
               SigninGlobalError::GetForProfile(profile)->HasMenuItem() ?
                   OneClickSigninSyncStarter::SHOW_SETTINGS_WITHOUT_CONFIGURE :
                   OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST :
@@ -1263,14 +1267,16 @@
                 StartSyncArgs(profile, browser, auto_accept_,
                               session_index_, email_, password_,
                               force_same_tab_navigation,
-                              false /* confirmation_required */, source_),
+                              false /* confirmation_required */, source_,
+                              CreateSyncStarterCallback()),
                 contents,
                 start_mode));
       } else {
         StartSync(
             StartSyncArgs(profile, browser, auto_accept_, session_index_,
                           email_, password_, force_same_tab_navigation,
-                          untrusted_confirmation_required_, source_),
+                          untrusted_confirmation_required_, source_,
+                          CreateSyncStarterCallback()),
             start_mode);
 
         // If this explicit sign in is not from settings page/webstore, show
@@ -1284,9 +1290,9 @@
       // requested a gaia sign in, so that when sign in and sync setup are
       // successful, we can redirect to the correct URL, or auto-close the gaia
       // sign in tab.
-      if (original_source == SyncPromoUI::SOURCE_SETTINGS ||
-          (original_source == SyncPromoUI::SOURCE_WEBSTORE_INSTALL &&
-           source_ == SyncPromoUI::SOURCE_SETTINGS)) {
+      if (original_source == signin::SOURCE_SETTINGS ||
+          (original_source == signin::SOURCE_WEBSTORE_INSTALL &&
+           source_ == signin::SOURCE_SETTINGS)) {
         ProfileSyncService* sync_service =
             ProfileSyncServiceFactory::GetForProfile(profile);
         if (sync_service)
@@ -1322,7 +1328,7 @@
   // |original_continue_url_| contains the |auto_close| parameter. Otherwise,
   // wait for sync setup to complete and then navigate to
   // |original_continue_url_|.
-  if (SyncPromoUI::IsAutoCloseEnabledInURL(original_continue_url_)) {
+  if (signin::IsAutoCloseEnabledInURL(original_continue_url_)) {
     // Close the gaia sign in tab via a task to make sure we aren't in the
     // middle of any webui handler code.
     base::MessageLoop::current()->PostTask(
@@ -1346,3 +1352,29 @@
   original_continue_url_ = GURL();
   sync_service->RemoveObserver(this);
 }
+
+OneClickSigninSyncStarter::Callback
+    OneClickSigninHelper::CreateSyncStarterCallback() {
+  // The callback will only be invoked if this object is still alive when sync
+  // setup is completed. This is correct because this object is only deleted
+  // when the web contents that potentially shows a blank page is deleted.
+  return base::Bind(&OneClickSigninHelper::SyncSetupCompletedCallback,
+                    weak_pointer_factory_.GetWeakPtr());
+}
+
+void OneClickSigninHelper::SyncSetupCompletedCallback(
+    OneClickSigninSyncStarter::SyncSetupResult result) {
+  if (result == OneClickSigninSyncStarter::SYNC_SETUP_FAILURE &&
+      web_contents()) {
+    GURL current_url = web_contents()->GetVisibleURL();
+
+    // If the web contents is showing a blank page and not about to be closed,
+    // redirect to the NTP or apps page.
+    if (signin::IsContinueUrlForWebBasedSigninFlow(current_url) &&
+        !signin::IsAutoCloseEnabledInURL(original_continue_url_)) {
+      RedirectToNtpOrAppsPage(
+          web_contents(),
+          signin::GetSourceForPromoURL(original_continue_url_));
+    }
+  }
+}
diff --git a/chrome/browser/ui/sync/one_click_signin_helper.h b/chrome/browser/ui/sync/one_click_signin_helper.h
index 2eb6223..79d9086 100644
--- a/chrome/browser/ui/sync/one_click_signin_helper.h
+++ b/chrome/browser/ui/sync/one_click_signin_helper.h
@@ -8,8 +8,10 @@
 #include <string>
 
 #include "base/gtest_prod_util.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/sync/profile_sync_service_observer.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
+#include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -187,7 +189,7 @@
   static void ShowInfoBarUIThread(const std::string& session_index,
                                   const std::string& email,
                                   AutoAccept auto_accept,
-                                  SyncPromoUI::Source source,
+                                  signin::Source source,
                                   const GURL& continue_url,
                                   int child_id,
                                   int route_id);
@@ -221,6 +223,12 @@
   // ProfileSyncServiceObserver.
   virtual void OnStateChanged() OVERRIDE;
 
+  OneClickSigninSyncStarter::Callback CreateSyncStarterCallback();
+
+  // Callback invoked when OneClickSigninSyncStarter completes sync setup.
+  void SyncSetupCompletedCallback(
+      OneClickSigninSyncStarter::SyncSetupResult result);
+
   // Tracks if we are in the process of showing the signin or one click
   // interstitial page. It's set to true the first time we load one of those
   // pages and set to false when transient state is cleaned.
@@ -232,7 +240,7 @@
   std::string email_;
   std::string password_;
   AutoAccept auto_accept_;
-  SyncPromoUI::Source source_;
+  signin::Source source_;
   bool switched_to_advanced_;
   GURL continue_url_;
   // The orignal continue URL after sync setup is complete.
@@ -255,6 +263,8 @@
   // pending e-mail.
   bool do_not_clear_pending_email_;
 
+  base::WeakPtrFactory<OneClickSigninHelper> weak_pointer_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(OneClickSigninHelper);
 };
 
diff --git a/chrome/browser/ui/sync/one_click_signin_helper_unittest.cc b/chrome/browser/ui/sync/one_click_signin_helper_unittest.cc
index a962faa..ebdc2d8 100644
--- a/chrome/browser/ui/sync/one_click_signin_helper_unittest.cc
+++ b/chrome/browser/ui/sync/one_click_signin_helper_unittest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/signin/signin_names_io_thread.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/profile_sync_service_mock.h"
 #include "chrome/browser/sync/test_profile_sync_service.h"
@@ -240,13 +241,13 @@
 }
 
 void OneClickSigninHelperTest::SetUp() {
-  SyncPromoUI::ForceWebBasedSigninFlowForTesting(true);
+  signin::ForceWebBasedSigninFlowForTesting(true);
   content::RenderViewHostTestHarness::SetUp();
   SetTrustedSigninProcessID(process()->GetID());
 }
 
 void OneClickSigninHelperTest::TearDown() {
-  SyncPromoUI::ForceWebBasedSigninFlowForTesting(false);
+  signin::ForceWebBasedSigninFlowForTesting(false);
   content::RenderViewHostTestHarness::TearDown();
 }
 
@@ -631,7 +632,7 @@
 
   OneClickSigninHelper::ShowInfoBarUIThread(
       "session_index", "email", OneClickSigninHelper::AUTO_ACCEPT_ACCEPTED,
-      SyncPromoUI::SOURCE_UNKNOWN, GURL(), process()->GetID(),
+      signin::SOURCE_UNKNOWN, GURL(), process()->GetID(),
       rvh()->GetRoutingID());
 }
 
@@ -656,7 +657,7 @@
   OneClickSigninHelper::ShowInfoBarUIThread(
       "session_index", "user@gmail.com",
       OneClickSigninHelper::AUTO_ACCEPT_EXPLICIT,
-      SyncPromoUI::SOURCE_WEBSTORE_INSTALL,
+      signin::SOURCE_WEBSTORE_INSTALL,
       continueUrl, process()->GetID(), rvh()->GetRoutingID());
 
   SubmitGAIAPassword(helper);
@@ -707,8 +708,8 @@
 
 TEST_F(OneClickSigninHelperIOTest, CanOfferOnIOThreadReferrer) {
   scoped_ptr<TestProfileIOData> io_data(CreateTestProfileIOData(false));
-  std::string continue_url(SyncPromoUI::GetSyncPromoURL(
-      SyncPromoUI::SOURCE_START_PAGE, false).spec());
+  std::string continue_url(signin::GetPromoURL(
+      signin::SOURCE_START_PAGE, false).spec());
 
   EXPECT_EQ(OneClickSigninHelper::CAN_OFFER,
             OneClickSigninHelper::CanOfferOnIOThreadImpl(
diff --git a/chrome/browser/ui/sync/one_click_signin_sync_starter.cc b/chrome/browser/ui/sync/one_click_signin_sync_starter.cc
index 298f887..03ccb51 100644
--- a/chrome/browser/ui/sync/one_click_signin_sync_starter.cc
+++ b/chrome/browser/ui/sync/one_click_signin_sync_starter.cc
@@ -50,11 +50,13 @@
     StartSyncMode start_mode,
     bool force_same_tab_navigation,
     ConfirmationRequired confirmation_required,
-    SyncPromoUI::Source source)
+    signin::Source source,
+    Callback sync_setup_completed_callback)
     : start_mode_(start_mode),
       force_same_tab_navigation_(force_same_tab_navigation),
       confirmation_required_(confirmation_required),
       source_(source),
+      sync_setup_completed_callback_(sync_setup_completed_callback),
       weak_pointer_factory_(this) {
   DCHECK(profile);
   BrowserList::AddObserver(this);
@@ -314,16 +316,6 @@
 void OneClickSigninSyncStarter::UntrustedSigninConfirmed(
     StartSyncMode response) {
   if (response == UNDO_SYNC) {
-    // If this was not an interstitial signin, (i.e. it was a SAML signin)
-    // then the browser page is now blank and should redirect to the NTP.
-    if (source_ != SyncPromoUI::SOURCE_UNKNOWN) {
-      EnsureBrowser();
-      chrome::NavigateParams params(browser_, GURL(chrome::kChromeUINewTabURL),
-                                    content::PAGE_TRANSITION_AUTO_TOPLEVEL);
-      params.disposition = CURRENT_TAB;
-      params.window_action = chrome::NavigateParams::SHOW_WINDOW;
-      chrome::Navigate(&params);
-    }
     CancelSigninAndDelete();  // This statement frees this object.
   } else {
     // If the user clicked the "Advanced" link in the confirmation dialog, then
@@ -337,6 +329,9 @@
 
 void OneClickSigninSyncStarter::SigninFailed(
     const GoogleServiceAuthError& error) {
+  if (!sync_setup_completed_callback_.is_null())
+    sync_setup_completed_callback_.Run(SYNC_SETUP_FAILURE);
+
   FinishProfileSyncServiceSetup();
   if (confirmation_required_ == CONFIRM_AFTER_SIGNIN) {
     switch (error.state()) {
@@ -357,6 +352,9 @@
 }
 
 void OneClickSigninSyncStarter::SigninSuccess() {
+  if (!sync_setup_completed_callback_.is_null())
+    sync_setup_completed_callback_.Run(SYNC_SETUP_SUCCESS);
+
   switch (start_mode_) {
     case SYNC_WITH_DEFAULT_SETTINGS: {
       // Just kick off the sync machine, no need to configure it first.
diff --git a/chrome/browser/ui/sync/one_click_signin_sync_starter.h b/chrome/browser/ui/sync/one_click_signin_sync_starter.h
index 0bbe183..3fc359e 100644
--- a/chrome/browser/ui/sync/one_click_signin_sync_starter.h
+++ b/chrome/browser/ui/sync/one_click_signin_sync_starter.h
@@ -7,13 +7,16 @@
 
 #include <string>
 
+#include "base/callback_forward.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/signin/signin_tracker.h"
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "chrome/browser/ui/host_desktop.h"
 #include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 
 class Browser;
 class ProfileSyncService;
@@ -60,10 +63,20 @@
     CONFIRM_AFTER_SIGNIN
   };
 
+  // Result of the sync setup.
+  enum SyncSetupResult {
+    SYNC_SETUP_SUCCESS,
+    SYNC_SETUP_FAILURE
+  };
+
+  typedef base::Callback<void(SyncSetupResult)> Callback;
+
   // |profile| must not be NULL, however |browser| can be. When using the
   // OneClickSigninSyncStarter from a browser, provide both.
   // If |display_confirmation| is true, the user will be prompted to confirm the
   // signin before signin completes.
+  // |callback| is always executed before OneClickSigninSyncStarter is deleted.
+  // It can be empty.
   OneClickSigninSyncStarter(Profile* profile,
                             Browser* browser,
                             const std::string& session_index,
@@ -72,12 +85,21 @@
                             StartSyncMode start_mode,
                             bool force_same_tab_navigation,
                             ConfirmationRequired display_confirmation,
-                            SyncPromoUI::Source source);
+                            signin::Source source,
+                            Callback callback);
 
   // chrome::BrowserListObserver override.
   virtual void OnBrowserRemoved(Browser* browser) OVERRIDE;
 
  private:
+  friend class OneClickSigninSyncStarterTest;
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninSyncStarterTest,
+                           CallbackSigninFailed);
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninSyncStarterTest,
+                           CallbackSigninSucceeded);
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninSyncStarterTest,
+                           CallbackNull);
+
   virtual ~OneClickSigninSyncStarter();
 
   // Initializes the internals of the OneClickSigninSyncStarter object. Can also
@@ -175,7 +197,11 @@
   chrome::HostDesktopType desktop_type_;
   bool force_same_tab_navigation_;
   ConfirmationRequired confirmation_required_;
-  SyncPromoUI::Source source_;
+  signin::Source source_;
+
+  // Callback executed when sync setup succeeds or fails.
+  Callback sync_setup_completed_callback_;
+
   base::WeakPtrFactory<OneClickSigninSyncStarter> weak_pointer_factory_;
 
 #if defined(ENABLE_CONFIGURATION_POLICY)
diff --git a/chrome/browser/ui/sync/one_click_signin_sync_starter_unittest.cc b/chrome/browser/ui/sync/one_click_signin_sync_starter_unittest.cc
new file mode 100644
index 0000000..2d12642
--- /dev/null
+++ b/chrome/browser/ui/sync/one_click_signin_sync_starter_unittest.cc
@@ -0,0 +1,107 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/signin/fake_signin_manager.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const char* kTestingUsername = "fake_username";
+}  // namespace
+
+class OneClickSigninSyncStarterTest : public testing::Test {
+ public:
+  OneClickSigninSyncStarterTest()
+      : sync_starter_(NULL),
+        failed_count_(0),
+        succeeded_count_(0) {}
+
+  // testing::Test:
+  virtual void SetUp() OVERRIDE {
+    testing::Test::SetUp();
+    profile_.reset(new TestingProfile());
+
+    // Disable sync to simplify the creation of a OneClickSigninSyncStarter.
+    CommandLine::ForCurrentProcess()->AppendSwitch(switches::kDisableSync);
+
+    // Create the sign in manager required by OneClickSigninSyncStarter.
+    SigninManagerBase* signin_manager =
+        static_cast<FakeSigninManager*>(
+            SigninManagerFactory::GetInstance()->SetTestingFactoryAndUse(
+                profile_.get(),
+                &OneClickSigninSyncStarterTest::BuildSigninManager));
+    signin_manager->Initialize(profile_.get(), NULL);
+    signin_manager->SetAuthenticatedUsername(kTestingUsername);
+  }
+
+  void Callback(OneClickSigninSyncStarter::SyncSetupResult result) {
+    if (result == OneClickSigninSyncStarter::SYNC_SETUP_SUCCESS)
+      ++succeeded_count_;
+    else
+      ++failed_count_;
+  }
+
+ protected:
+  void CreateSyncStarter(OneClickSigninSyncStarter::Callback callback) {
+    sync_starter_ = new OneClickSigninSyncStarter(
+      profile_.get(),
+      NULL,
+      std::string(),
+      kTestingUsername,
+      std::string(),
+      OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS,
+      false,
+      OneClickSigninSyncStarter::NO_CONFIRMATION,
+      signin::SOURCE_UNKNOWN,
+      callback
+    );
+  }
+
+  scoped_ptr<TestingProfile> profile_;
+
+  // Deletes itself when SigninFailed() or SigninSuccess() is called.
+  OneClickSigninSyncStarter* sync_starter_;
+
+  // Number of times that the callback is called with SYNC_SETUP_FAILURE.
+  int failed_count_;
+
+  // Number of times that the callback is called with SYNC_SETUP_SUCCESS.
+  int succeeded_count_;
+
+ private:
+  static BrowserContextKeyedService* BuildSigninManager(
+      content::BrowserContext* profile) {
+    return new FakeSigninManager(static_cast<Profile*>(profile));
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(OneClickSigninSyncStarterTest);
+};
+
+// Verifies that the callback is invoked when syn setup fails.
+TEST_F(OneClickSigninSyncStarterTest, CallbackSigninFailed) {
+  CreateSyncStarter(base::Bind(&OneClickSigninSyncStarterTest::Callback,
+                               base::Unretained(this)));
+  sync_starter_->SigninFailed(GoogleServiceAuthError(
+      GoogleServiceAuthError::REQUEST_CANCELED));
+  EXPECT_EQ(1, failed_count_);
+  EXPECT_EQ(0, succeeded_count_);
+}
+
+// Verifies that there is no crash when the callback is NULL.
+TEST_F(OneClickSigninSyncStarterTest, CallbackNull) {
+  CreateSyncStarter(OneClickSigninSyncStarter::Callback());
+  sync_starter_->SigninFailed(GoogleServiceAuthError(
+      GoogleServiceAuthError::REQUEST_CANCELED));
+  EXPECT_EQ(0, failed_count_);
+  EXPECT_EQ(0, succeeded_count_);
+}
diff --git a/chrome/browser/ui/sync/sync_promo_ui.cc b/chrome/browser/ui/sync/sync_promo_ui.cc
index 15a597c..9b5c491 100644
--- a/chrome/browser/ui/sync/sync_promo_ui.cc
+++ b/chrome/browser/ui/sync/sync_promo_ui.cc
@@ -4,287 +4,18 @@
 
 #include "chrome/browser/ui/sync/sync_promo_ui.h"
 
-#include "base/command_line.h"
-#include "base/prefs/pref_service.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/first_run/first_run.h"
-#include "chrome/browser/google/google_util.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_info_cache.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/signin/signin_manager.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
-#include "chrome/browser/sync/profile_sync_service.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/ui/webui/options/core_options_handler.h"
-#include "chrome/browser/ui/webui/sync_promo/sync_promo_trial.h"
-#include "chrome/browser/ui/webui/theme_source.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/net/url_util.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
-#include "components/user_prefs/pref_registry_syncable.h"
-#include "content/public/browser/url_data_source.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "google_apis/gaia/gaia_urls.h"
-#include "grit/browser_resources.h"
-#include "grit/generated_resources.h"
-#include "grit/theme_resources.h"
-#include "net/base/escape.h"
-#include "net/base/network_change_notifier.h"
-#include "net/base/url_util.h"
-#include "ui/base/l10n/l10n_util.h"
+#include "chrome/browser/signin/signin_promo.h"
 
-using content::WebContents;
-
-namespace {
-
-const char kStringsJsFile[] = "strings.js";
-const char kSyncPromoJsFile[] = "sync_promo.js";
-
-const char kSyncPromoQueryKeyAutoClose[] = "auto_close";
-const char kSyncPromoQueryKeyContinue[] = "continue";
-const char kSyncPromoQueryKeySource[] = "source";
-
-// Gaia cannot support about:blank as a continue URL, so using a hosted blank
-// page instead.
-const char kSyncLandingUrlPrefix[] =
-    "https://www.google.com/intl/%s/chrome/blank.html";
-
-// The maximum number of times we want to show the sync promo at startup.
-const int kSyncPromoShowAtStartupMaximum = 10;
-
-// Forces the web based signin flow when set.
-bool g_force_web_based_signin_flow = false;
-
-// Checks we want to show the sync promo for the given brand.
-bool AllowPromoAtStartupForCurrentBrand() {
-  std::string brand;
-  google_util::GetBrand(&brand);
-
-  if (brand.empty())
-    return true;
-
-  if (google_util::IsInternetCafeBrandCode(brand))
-    return false;
-
-  // Enable for both organic and distribution.
-  return true;
-}
-
-}  // namespace
-
-// static
-bool SyncPromoUI::HasShownPromoAtStartup(Profile* profile) {
-  return profile->GetPrefs()->HasPrefPath(prefs::kSyncPromoStartupCount);
-}
-
-// static
 bool SyncPromoUI::ShouldShowSyncPromo(Profile* profile) {
-#if defined(OS_CHROMEOS)
-  // There's no need to show the sync promo on cros since cros users are logged
-  // into sync already.
-  return false;
-#else
-
-  // Don't bother if we don't have any kind of network connection.
-  if (net::NetworkChangeNotifier::IsOffline())
+  // Don't show sync promo if the sign in promo should not be shown.
+  if (!signin::ShouldShowPromo(profile)) {
     return false;
-
-  // Don't show for managed profiles.
-  if (profile->GetPrefs()->GetBoolean(prefs::kProfileIsManaged))
-    return false;
-
-  // Display the signin promo if the user is not signed in.
-  SigninManager* signin = SigninManagerFactory::GetForProfile(
-      profile->GetOriginalProfile());
-  return !signin->AuthInProgress() && signin->IsSigninAllowed() &&
-      signin->GetAuthenticatedUsername().empty();
-#endif
-}
-
-// static
-void SyncPromoUI::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterIntegerPref(
-      prefs::kSyncPromoStartupCount,
-      0,
-      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
-  registry->RegisterBooleanPref(
-      prefs::kSyncPromoUserSkipped,
-      false,
-      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
-  registry->RegisterBooleanPref(
-      prefs::kSyncPromoShowOnFirstRunAllowed,
-      true,
-      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
-  registry->RegisterBooleanPref(
-      prefs::kSyncPromoShowNTPBubble,
-      false,
-      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
-  registry->RegisterStringPref(
-      prefs::kSyncPromoErrorMessage,
-      std::string(),
-      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
-}
-
-// static
-bool SyncPromoUI::ShouldShowSyncPromoAtStartup(Profile* profile,
-                                               bool is_new_profile) {
-  DCHECK(profile);
-
-  // Don't show if the profile is an incognito.
-  if (profile->IsOffTheRecord())
-    return false;
-
-  if (!ShouldShowSyncPromo(profile))
-    return false;
-
-  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
-  if (command_line.HasSwitch(switches::kNoFirstRun))
-    is_new_profile = false;
-
-  if (!is_new_profile) {
-    if (!HasShownPromoAtStartup(profile))
-      return false;
   }
 
-  if (HasUserSkippedSyncPromo(profile))
+  // Don't show if sync is disabled by policy.
+  if (!profile->IsSyncAccessible())
     return false;
 
-  // For Chinese users skip the sync promo.
-  if (g_browser_process->GetApplicationLocale() == "zh-CN")
-    return false;
-
-  PrefService* prefs = profile->GetPrefs();
-  int show_count = prefs->GetInteger(prefs::kSyncPromoStartupCount);
-  if (show_count >= kSyncPromoShowAtStartupMaximum)
-    return false;
-
-  // This pref can be set in the master preferences file to allow or disallow
-  // showing the sync promo at startup.
-  if (prefs->HasPrefPath(prefs::kSyncPromoShowOnFirstRunAllowed))
-    return prefs->GetBoolean(prefs::kSyncPromoShowOnFirstRunAllowed);
-
-  // For now don't show the promo for some brands.
-  if (!AllowPromoAtStartupForCurrentBrand())
-    return false;
-
-  // Default to show the promo for Google Chrome builds.
-#if defined(GOOGLE_CHROME_BUILD)
   return true;
-#else
-  return false;
-#endif
-}
-
-void SyncPromoUI::DidShowSyncPromoAtStartup(Profile* profile) {
-  int show_count = profile->GetPrefs()->GetInteger(
-      prefs::kSyncPromoStartupCount);
-  show_count++;
-  profile->GetPrefs()->SetInteger(prefs::kSyncPromoStartupCount, show_count);
-}
-
-bool SyncPromoUI::HasUserSkippedSyncPromo(Profile* profile) {
-  return profile->GetPrefs()->GetBoolean(prefs::kSyncPromoUserSkipped);
-}
-
-void SyncPromoUI::SetUserSkippedSyncPromo(Profile* profile) {
-  profile->GetPrefs()->SetBoolean(prefs::kSyncPromoUserSkipped, true);
-}
-
-// static
-std::string SyncPromoUI::GetSyncLandingURL(const char* option, int value) {
-  const std::string& locale = g_browser_process->GetApplicationLocale();
-  std::string url = base::StringPrintf(kSyncLandingUrlPrefix, locale.c_str());
-  base::StringAppendF(&url, "?%s=%d", option, value);
-  return url;
-}
-
-// static
-GURL SyncPromoUI::GetSyncPromoURL(Source source, bool auto_close) {
-  DCHECK_NE(SOURCE_UNKNOWN, source);
-
-  std::string url_string;
-
-  // Build a Gaia-based URL that can be used to sign the user into chrome.
-  // There are required request parameters:
-  //
-  //  - tell Gaia which service the user is signing into.  In this case,
-  //    a chrome sign in uses the service "chromiumsync"
-  //  - provide a continue URL.  This is the URL that Gaia will redirect to
-  //    once the sign is complete.
-  //
-  // The continue URL includes a source parameter that can be extracted using
-  // the function GetSourceForSyncPromoURL() below.  This is used to know
-  // which of the chrome sign in access points was used to sign the user in.
-  // It is also parsed for the |auto_close| flag, which indicates that the tab
-  // must be closed after sync setup is successful.
-  // See OneClickSigninHelper for details.
-  url_string = GaiaUrls::GetInstance()->service_login_url();
-  url_string.append("?service=chromiumsync&sarp=1");
-
-  std::string continue_url = GetSyncLandingURL(
-      kSyncPromoQueryKeySource, static_cast<int>(source));
-  if (auto_close)
-    base::StringAppendF(&continue_url, "&%s=1", kSyncPromoQueryKeyAutoClose);
-
-  base::StringAppendF(&url_string, "&%s=%s", kSyncPromoQueryKeyContinue,
-                      net::EscapeQueryParamValue(
-                          continue_url, false).c_str());
-
-  return GURL(url_string);
-}
-
-// static
-GURL SyncPromoUI::GetNextPageURLForSyncPromoURL(const GURL& url) {
-  std::string value;
-  if (net::GetValueForKeyInQuery(url, kSyncPromoQueryKeyContinue, &value))
-    return GURL(value);
-
-  return GURL();
-}
-
-// static
-SyncPromoUI::Source SyncPromoUI::GetSourceForSyncPromoURL(const GURL& url) {
-  std::string value;
-  if (net::GetValueForKeyInQuery(url, kSyncPromoQueryKeySource, &value)) {
-    int source = 0;
-    if (base::StringToInt(value, &source) && source >= SOURCE_START_PAGE &&
-        source < SOURCE_UNKNOWN) {
-      return static_cast<Source>(source);
-    }
-  }
-  return SOURCE_UNKNOWN;
-}
-
-// static
-bool SyncPromoUI::IsAutoCloseEnabledInURL(const GURL& url) {
-  std::string value;
-  if (net::GetValueForKeyInQuery(url, kSyncPromoQueryKeyAutoClose, &value)) {
-    int enabled = 0;
-    if (base::StringToInt(value, &enabled) && enabled == 1)
-      return true;
-  }
-  return false;
-}
-
-// static
-bool SyncPromoUI::IsContinueUrlForWebBasedSigninFlow(const GURL& url) {
-  GURL::Replacements replacements;
-  replacements.ClearQuery();
-  const std::string& locale = g_browser_process->GetApplicationLocale();
-  return url.ReplaceComponents(replacements) ==
-      GURL(base::StringPrintf(kSyncLandingUrlPrefix, locale.c_str()));
-}
-
-// static
-void SyncPromoUI::ForceWebBasedSigninFlowForTesting(bool force) {
-  g_force_web_based_signin_flow = force;
 }
diff --git a/chrome/browser/ui/sync/sync_promo_ui.h b/chrome/browser/ui/sync/sync_promo_ui.h
index 46fa449..1af94bc 100644
--- a/chrome/browser/ui/sync/sync_promo_ui.h
+++ b/chrome/browser/ui/sync/sync_promo_ui.h
@@ -5,87 +5,15 @@
 #ifndef CHROME_BROWSER_UI_SYNC_SYNC_PROMO_UI_H_
 #define CHROME_BROWSER_UI_SYNC_SYNC_PROMO_UI_H_
 
-#include <string>
-
-#include "base/basictypes.h"
-
-class GURL;
 class Profile;
 
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
-// Static helper functions useful for chrome sign in.
+// Static helper function useful for sync promos.
 class SyncPromoUI {
  public:
-  // Please keep this in sync with enums in sync_promo_trial.cc.
-  enum Source {
-    SOURCE_START_PAGE = 0, // This must be first.
-    SOURCE_NTP_LINK,
-    SOURCE_MENU,
-    SOURCE_SETTINGS,
-    SOURCE_EXTENSION_INSTALL_BUBBLE,
-    SOURCE_WEBSTORE_INSTALL,
-    SOURCE_APP_LAUNCHER,
-    SOURCE_APPS_PAGE_LINK,
-    SOURCE_BOOKMARK_BUBBLE,
-    SOURCE_UNKNOWN, // This must be last.
-  };
-
   // Returns true if the sync promo should be visible.
-  // |profile| is the profile of the tab the promo would be shown on.
+  // |profile| is the profile for which the promo would be displayed.
   static bool ShouldShowSyncPromo(Profile* profile);
 
-  // Returns true if we should show the sync promo at startup.
-  static bool ShouldShowSyncPromoAtStartup(Profile* profile,
-                                           bool is_new_profile);
-
-  // Called when the sync promo has been shown so that we can keep track
-  // of the number of times we've displayed it.
-  static void DidShowSyncPromoAtStartup(Profile* profile);
-
-  // Returns true if a user has seen the sync promo at startup previously.
-  static bool HasShownPromoAtStartup(Profile* profile);
-
-  // Returns true if the user has previously skipped the sync promo.
-  static bool HasUserSkippedSyncPromo(Profile* profile);
-
-  // Registers the fact that the user has skipped the sync promo.
-  static void SetUserSkippedSyncPromo(Profile* profile);
-
-  // Registers the preferences the Sync Promo UI needs.
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
-  // Gets the sync landing page URL.
-  static std::string GetSyncLandingURL(const char* option, int value);
-
-  // Returns the sync promo URL wth the given arguments in the query.
-  // |source| identifies from where the sync promo is being called, and is used
-  // to record sync promo UMA stats in the context of the source.
-  // |auto_close| whether to close the sync promo automatically when done.
-  static GURL GetSyncPromoURL(Source source, bool auto_close);
-
-  // Gets the next page URL from the query portion of the sync promo URL.
-  static GURL GetNextPageURLForSyncPromoURL(const GURL& url);
-
-  // Gets the source from the query portion of the sync promo URL.
-  // The source identifies from where the sync promo was opened.
-  static Source GetSourceForSyncPromoURL(const GURL& url);
-
-  // Returns true if the auto_close parameter in the given URL is set to true.
-  static bool IsAutoCloseEnabledInURL(const GURL& url);
-
-  // Returns true if the given URL is the standard continue URL used with the
-  // sync promo when the web-based flow is enabled.  The query parameters
-  // of the URL are ignored for this comparison.
-  static bool IsContinueUrlForWebBasedSigninFlow(const GURL& url);
-
-  // Forces UseWebBasedSigninFlow() to return true when set; used in tests only.
-  static void ForceWebBasedSigninFlowForTesting(bool force);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SyncPromoUI);
 };
 
 #endif  // CHROME_BROWSER_UI_SYNC_SYNC_PROMO_UI_H_
diff --git a/chrome/browser/ui/sync/sync_promo_ui_unittest.cc b/chrome/browser/ui/sync/sync_promo_ui_unittest.cc
new file mode 100644
index 0000000..1a9d8d6
--- /dev/null
+++ b/chrome/browser/ui/sync/sync_promo_ui_unittest.cc
@@ -0,0 +1,70 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/ui/sync/sync_promo_ui.h"
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/signin/fake_signin_manager.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class SyncPromoUITest : public testing::Test {
+ public:
+  SyncPromoUITest() {}
+
+  // testing::Test:
+  virtual void SetUp() OVERRIDE {
+    testing::Test::SetUp();
+    profile_.reset(new TestingProfile());
+  }
+
+ protected:
+  void CreateSigninManager(const std::string& username) {
+    SigninManagerBase* signin_manager =
+        static_cast<FakeSigninManagerBase*>(
+            SigninManagerFactory::GetInstance()->SetTestingFactoryAndUse(
+                profile_.get(),
+                &FakeSigninManagerBase::Build));
+    signin_manager->Initialize(profile_.get(), NULL);
+
+    if (!username.empty()) {
+      ASSERT_TRUE(signin_manager);
+      signin_manager->SetAuthenticatedUsername(username);
+    }
+  }
+
+  void DisableSync() {
+    CommandLine::ForCurrentProcess()->AppendSwitch(switches::kDisableSync);
+  }
+
+  scoped_ptr<TestingProfile> profile_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SyncPromoUITest);
+};
+
+// Verifies that ShouldShowSyncPromo returns false if sync is disabled by
+// policy.
+TEST_F(SyncPromoUITest, ShouldShowSyncPromoSyncDisabled) {
+  CreateSigninManager("");
+  DisableSync();
+  EXPECT_FALSE(SyncPromoUI::ShouldShowSyncPromo(profile_.get()));
+}
+
+// Verifies that ShouldShowSyncPromo returns true if all conditions to
+// show the promo are met.
+TEST_F(SyncPromoUITest, ShouldShowSyncPromoSyncEnabled) {
+  CreateSigninManager("");
+#if defined(OS_CHROMEOS)
+  // No sync promo on CrOS.
+  EXPECT_FALSE(SyncPromoUI::ShouldShowSyncPromo(profile_.get()));
+#else
+  EXPECT_TRUE(SyncPromoUI::ShouldShowSyncPromo(profile_.get()));
+#endif
+}
diff --git a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
index 327df0e..33748be 100644
--- a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
+++ b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
@@ -16,7 +16,6 @@
 
 #if defined(ENABLE_MANAGED_USERS)
 #include "chrome/browser/managed_mode/managed_mode_navigation_observer.h"
-#include "chrome/browser/managed_mode/managed_user_service.h"
 #endif
 
 using content::NavigationEntry;
@@ -76,11 +75,7 @@
 }
 
 bool TabContentsSyncedTabDelegate::ProfileIsManaged() const {
-#if defined(ENABLE_MANAGED_USERS)
-  return ManagedUserService::ProfileIsManaged(profile());
-#else
-  return false;
-#endif
+  return profile()->IsManaged();
 }
 
 const std::vector<const content::NavigationEntry*>*
@@ -106,10 +101,10 @@
 
 bool TabContentsSyncedTabDelegate::HasWebContents() const { return true; }
 
-int64 TabContentsSyncedTabDelegate::GetSyncId() const {
+int TabContentsSyncedTabDelegate::GetSyncId() const {
   return sync_session_id_;
 }
 
-void TabContentsSyncedTabDelegate::SetSyncId(int64 sync_id) {
+void TabContentsSyncedTabDelegate::SetSyncId(int sync_id) {
   sync_session_id_ = sync_id;
 }
diff --git a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h
index 68486a5..6f98ae4 100644
--- a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h
+++ b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h
@@ -37,15 +37,15 @@
       GetBlockedNavigations() const OVERRIDE;
   virtual bool IsPinned() const OVERRIDE;
   virtual bool HasWebContents() const OVERRIDE;
-  virtual int64 GetSyncId() const OVERRIDE;
-  virtual void SetSyncId(int64 sync_id) OVERRIDE;
+  virtual int GetSyncId() const OVERRIDE;
+  virtual void SetSyncId(int sync_id) OVERRIDE;
 
  private:
   explicit TabContentsSyncedTabDelegate(content::WebContents* web_contents);
   friend class content::WebContentsUserData<TabContentsSyncedTabDelegate>;
 
   content::WebContents* web_contents_;
-  int64 sync_session_id_;
+  int sync_session_id_;
 
   DISALLOW_COPY_AND_ASSIGN(TabContentsSyncedTabDelegate);
 };
diff --git a/chrome/browser/ui/tab_modal_confirm_dialog.h b/chrome/browser/ui/tab_modal_confirm_dialog.h
index 838e705..6bd54ac 100644
--- a/chrome/browser/ui/tab_modal_confirm_dialog.h
+++ b/chrome/browser/ui/tab_modal_confirm_dialog.h
@@ -12,7 +12,7 @@
 }
 
 // Base class for the tab modal confirm dialog.
-class TabModalConfirmDialog : public TabModalConfirmDialogOperationsDelegate {
+class TabModalConfirmDialog : public TabModalConfirmDialogCloseDelegate {
  public:
   // Platform specific factory function. This function will automatically show
   // the dialog.
@@ -24,11 +24,9 @@
   // Cancels the dialog.
   virtual void CancelTabModalDialog() = 0;
 
-  // TabModalConfirmDialogOperationsDelegate:
+  // TabModalConfirmDialogCloseDelegate:
   // Closes the dialog.
   virtual void CloseDialog() = 0;
-  // Prevents the dialog from closing on WebContents load start.
-  virtual void SetPreventCloseOnLoadStart(bool prevent) = 0;
 
  protected:
   virtual ~TabModalConfirmDialog() {}
diff --git a/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.cc b/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.cc
index 1ced79b..148b816 100644
--- a/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.cc
+++ b/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.cc
@@ -11,7 +11,10 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/page_navigator.h"
+#include "content/public/browser/web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/window_open_disposition.h"
 
 MockTabModalConfirmDialogDelegate::MockTabModalConfirmDialogDelegate(
     content::WebContents* web_contents,
@@ -41,11 +44,17 @@
     delegate_->OnCanceled();
 }
 
+void MockTabModalConfirmDialogDelegate::OnClosed() {
+  if (delegate_)
+    delegate_->OnClosed();
+}
+
 TabModalConfirmDialogTest::TabModalConfirmDialogTest()
     : delegate_(NULL),
       dialog_(NULL),
       accepted_count_(0),
-      canceled_count_(0) {
+      canceled_count_(0),
+      closed_count_(0) {
 }
 
 void TabModalConfirmDialogTest::SetUpOnMainThread() {
@@ -68,22 +77,56 @@
   ++canceled_count_;
 }
 
+void TabModalConfirmDialogTest::OnClosed() {
+  ++closed_count_;
+}
+
 IN_PROC_BROWSER_TEST_F(TabModalConfirmDialogTest, Accept) {
   dialog_->AcceptTabModalDialog();
   EXPECT_EQ(1, accepted_count_);
   EXPECT_EQ(0, canceled_count_);
+  EXPECT_EQ(0, closed_count_);
 }
 
 IN_PROC_BROWSER_TEST_F(TabModalConfirmDialogTest, Cancel) {
   dialog_->CancelTabModalDialog();
   EXPECT_EQ(0, accepted_count_);
   EXPECT_EQ(1, canceled_count_);
+  EXPECT_EQ(0, closed_count_);
 }
 
 IN_PROC_BROWSER_TEST_F(TabModalConfirmDialogTest, CancelSelf) {
   delegate_->Cancel();
   EXPECT_EQ(0, accepted_count_);
   EXPECT_EQ(1, canceled_count_);
+  EXPECT_EQ(0, closed_count_);
+}
+
+IN_PROC_BROWSER_TEST_F(TabModalConfirmDialogTest, Close) {
+  dialog_->CloseDialog();
+  EXPECT_EQ(0, accepted_count_);
+  EXPECT_EQ(0, canceled_count_);
+  EXPECT_EQ(1, closed_count_);
+}
+
+IN_PROC_BROWSER_TEST_F(TabModalConfirmDialogTest, CloseSelf) {
+  delegate_->Close();
+  EXPECT_EQ(0, accepted_count_);
+  EXPECT_EQ(0, canceled_count_);
+  EXPECT_EQ(1, closed_count_);
+}
+
+IN_PROC_BROWSER_TEST_F(TabModalConfirmDialogTest, Navigate) {
+  content::OpenURLParams params(GURL("about:blank"),
+                                content::Referrer(),
+                                CURRENT_TAB,
+                                content::PAGE_TRANSITION_LINK,
+                                false);
+  browser()->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
+
+  EXPECT_EQ(0, accepted_count_);
+  EXPECT_EQ(0, canceled_count_);
+  EXPECT_EQ(1, closed_count_);
 }
 
 IN_PROC_BROWSER_TEST_F(TabModalConfirmDialogTest, Quit) {
@@ -91,5 +134,6 @@
                                               base::Bind(&chrome::AttemptExit));
   content::RunMessageLoop();
   EXPECT_EQ(0, accepted_count_);
-  EXPECT_EQ(1, canceled_count_);
+  EXPECT_EQ(0, canceled_count_);
+  EXPECT_EQ(1, closed_count_);
 }
diff --git a/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.h b/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.h
index 0f22b37..a45e3d1 100644
--- a/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.h
+++ b/chrome/browser/ui/tab_modal_confirm_dialog_browsertest.h
@@ -17,6 +17,7 @@
    public:
     virtual void OnAccepted() = 0;
     virtual void OnCanceled() = 0;
+    virtual void OnClosed() = 0;
    protected:
     virtual ~Delegate() {}
   };
@@ -30,6 +31,7 @@
 
   virtual void OnAccepted() OVERRIDE;
   virtual void OnCanceled() OVERRIDE;
+  virtual void OnClosed() OVERRIDE;
 
  private:
   Delegate* delegate_;
@@ -49,6 +51,7 @@
   // MockTabModalConfirmDialogDelegate::Delegate:
   virtual void OnAccepted() OVERRIDE;
   virtual void OnCanceled() OVERRIDE;
+  virtual void OnClosed() OVERRIDE;
 
  protected:
   // Owned by |dialog_|.
@@ -59,6 +62,7 @@
 
   int accepted_count_;
   int canceled_count_;
+  int closed_count_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TabModalConfirmDialogTest);
diff --git a/chrome/browser/ui/tab_modal_confirm_dialog_delegate.cc b/chrome/browser/ui/tab_modal_confirm_dialog_delegate.cc
index c8e1a58..9e49df0 100644
--- a/chrome/browser/ui/tab_modal_confirm_dialog_delegate.cc
+++ b/chrome/browser/ui/tab_modal_confirm_dialog_delegate.cc
@@ -16,9 +16,11 @@
 
 TabModalConfirmDialogDelegate::TabModalConfirmDialogDelegate(
     WebContents* web_contents)
-    : operations_delegate_(NULL),
+    : close_delegate_(NULL),
       closing_(false) {
   NavigationController* controller = &web_contents->GetController();
+  registrar_.Add(this, content::NOTIFICATION_LOAD_START,
+                 content::Source<NavigationController>(controller));
   registrar_.Add(this, chrome::NOTIFICATION_TAB_CLOSING,
                  content::Source<NavigationController>(controller));
 }
@@ -26,7 +28,7 @@
 TabModalConfirmDialogDelegate::~TabModalConfirmDialogDelegate() {
   // If we end up here, the window has been closed, so make sure we don't close
   // it again.
-  operations_delegate_ = NULL;
+  close_delegate_ = NULL;
   // Make sure everything is cleaned up.
   Cancel();
 }
@@ -34,8 +36,7 @@
 void TabModalConfirmDialogDelegate::Cancel() {
   if (closing_)
     return;
-  // Make sure we won't do anything when |Cancel()| or |Accept()| is called
-  // again.
+  // Make sure we won't do anything when another action occurs.
   closing_ = true;
   OnCanceled();
   CloseDialog();
@@ -44,8 +45,7 @@
 void TabModalConfirmDialogDelegate::Accept() {
   if (closing_)
     return;
-  // Make sure we won't do anything when |Cancel()| or |Accept()| is called
-  // again.
+  // Make sure we won't do anything when another action occurs.
   closing_ = true;
   OnAccepted();
   CloseDialog();
@@ -65,14 +65,25 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  // Close the dialog if the tab is closed.
-  if (type == chrome::NOTIFICATION_TAB_CLOSING) {
-    Cancel();
+  // Close the dialog if we load a page (because the action might not apply to
+  // the same page anymore) or if the tab is closed.
+  if (type == content::NOTIFICATION_LOAD_START ||
+      type == chrome::NOTIFICATION_TAB_CLOSING) {
+    Close();
   } else {
     NOTREACHED();
   }
 }
 
+void TabModalConfirmDialogDelegate::Close() {
+  if (closing_)
+    return;
+  // Make sure we won't do anything when another action occurs.
+  closing_ = true;
+  OnClosed();
+  CloseDialog();
+}
+
 gfx::Image* TabModalConfirmDialogDelegate::GetIcon() {
   return NULL;
 }
@@ -107,7 +118,10 @@
     WindowOpenDisposition disposition) {
 }
 
+void TabModalConfirmDialogDelegate::OnClosed() {
+}
+
 void TabModalConfirmDialogDelegate::CloseDialog() {
-  if (operations_delegate_)
-    operations_delegate_->CloseDialog();
+  if (close_delegate_)
+    close_delegate_->CloseDialog();
 }
diff --git a/chrome/browser/ui/tab_modal_confirm_dialog_delegate.h b/chrome/browser/ui/tab_modal_confirm_dialog_delegate.h
index 4f20b07..1db4c4b 100644
--- a/chrome/browser/ui/tab_modal_confirm_dialog_delegate.h
+++ b/chrome/browser/ui/tab_modal_confirm_dialog_delegate.h
@@ -20,18 +20,15 @@
 class Image;
 }
 
-// Operations to be performed on the dialog by the
-// TabModalConfirmDialogDelegate.
-class TabModalConfirmDialogOperationsDelegate {
+class TabModalConfirmDialogCloseDelegate {
  public:
-  TabModalConfirmDialogOperationsDelegate() {}
-  virtual ~TabModalConfirmDialogOperationsDelegate() {}
+  TabModalConfirmDialogCloseDelegate() {}
+  virtual ~TabModalConfirmDialogCloseDelegate() {}
 
   virtual void CloseDialog() = 0;
-  virtual void SetPreventCloseOnLoadStart(bool prevent) = 0;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(TabModalConfirmDialogOperationsDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TabModalConfirmDialogCloseDelegate);
 };
 
 // This class acts as the delegate for a simple tab-modal dialog confirming
@@ -41,26 +38,36 @@
   explicit TabModalConfirmDialogDelegate(content::WebContents* web_contents);
   virtual ~TabModalConfirmDialogDelegate();
 
-  void set_operations_delegate(
-      TabModalConfirmDialogOperationsDelegate* operations_delegate) {
-    operations_delegate_ = operations_delegate;
+  void set_close_delegate(TabModalConfirmDialogCloseDelegate* close_delegate) {
+    close_delegate_ = close_delegate;
   }
 
-  // Accepts the confirmation prompt and calls |OnAccepted|.
+  // Accepts the confirmation prompt and calls |OnAccepted| if no other call
+  // to |Accept|, |Cancel|, |LinkClicked| or |Close| has been made before.
   // This method is safe to call even from an |OnAccepted| or |OnCanceled|
   // callback.
   void Accept();
 
-  // Cancels the confirmation prompt and calls |OnCanceled|.
+  // Cancels the confirmation prompt and calls |OnCanceled| if no other call
+  // to |Accept|, |Cancel|, |LinkClicked| or |Close| has been made before.
   // This method is safe to call even from an |OnAccepted| or |OnCanceled|
   // callback.
   void Cancel();
 
   // Called when the link (if any) is clicked. Calls |OnLinkClicked| and closes
-  // the dialog. The |disposition| specifies how the resulting document should
-  // be loaded (based on the event flags present when the link was clicked).
+  // the dialog if no other call to |Accept|, |Cancel|, |LinkClicked| or
+  // |Close| has been made before. The |disposition| specifies how the
+  // resulting document should be loaded (based on the event flags present when
+  // the link was clicked).
   void LinkClicked(WindowOpenDisposition disposition);
 
+  // Called when the dialog is closed without selecting an option, e.g. by
+  // pressing the close button on the dialog, using a window manager gesture,
+  // closing the parent tab or navigating in the parent tab.
+  // Calls |OnClosed| and closes the dialog if no other call to |Accept|,
+  // |Cancel|, |LinkClicked| or |Close| has been made before.
+  void Close();
+
   // The title of the dialog. Note that the title is not shown on all platforms.
   virtual string16 GetTitle() = 0;
   virtual string16 GetMessage() = 0;
@@ -85,12 +92,12 @@
   virtual const char* GetCancelButtonIcon();
 
  protected:
-  TabModalConfirmDialogOperationsDelegate* operations_delegate() {
-    return operations_delegate_;
+  TabModalConfirmDialogCloseDelegate* close_delegate() {
+    return close_delegate_;
   }
 
   // content::NotificationObserver implementation.
-  // Watch for a closed tab and dismiss the dialog if it occurs.
+  // Watch for a new load or a closed tab and dismiss the dialog if they occur.
   virtual void Observe(int type,
                        const content::NotificationSource& source,
                        const content::NotificationDetails& details) OVERRIDE;
@@ -98,10 +105,9 @@
   content::NotificationRegistrar registrar_;
 
  private:
-  // It is guaranteed that exactly one of |OnAccepted|, |OnCanceled| or
-  // |OnLinkClicked| is eventually called. These method are private to
-  // enforce this guarantee. Access to them is controlled by |Accept|,
-  // |Cancel| and |LinkClicked|.
+  // It is guaranteed that exactly one of the |On...| methods is eventually
+  // called. These method are private to enforce this guarantee. Access to them
+  // is  controlled by |Accept|, |Cancel|, |LinkClicked| and |Close|.
 
   // Called when the user accepts or cancels the dialog, respectively.
   virtual void OnAccepted();
@@ -110,10 +116,13 @@
   // Called when the user clicks on the link (if any).
   virtual void OnLinkClicked(WindowOpenDisposition disposition);
 
+  // Called when the dialog is closed.
+  virtual void OnClosed();
+
   // Close the dialog.
   void CloseDialog();
 
-  TabModalConfirmDialogOperationsDelegate* operations_delegate_;
+  TabModalConfirmDialogCloseDelegate* close_delegate_;
   // True iff we are in the process of closing, to avoid running callbacks
   // multiple times.
   bool closing_;
diff --git a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
index 3fe29a4..1fa9d5c 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
@@ -76,7 +76,8 @@
   std::vector<WindowInfo> windows;
 };
 
-RecentTabsBuilderTestHelper::RecentTabsBuilderTestHelper() {
+RecentTabsBuilderTestHelper::RecentTabsBuilderTestHelper()
+    : max_tab_node_id_(0) {
   start_time_ = base::Time::Now();
 }
 
@@ -262,6 +263,7 @@
   SessionID::id_type tab_id = GetTabID(session_index, window_index, tab_index);
 
   tab_base->set_session_tag(ToSessionTag(session_id));
+  tab_base->set_tab_node_id(++max_tab_node_id_);
   sync_pb::SessionTab* tab = tab_base->mutable_tab();
   tab->set_window_id(window_id);
   tab->set_tab_id(tab_id);
diff --git a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h
index 63f4761..ac7492b 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h
+++ b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h
@@ -71,6 +71,8 @@
   std::vector<SessionInfo> sessions_;
   base::Time start_time_;
 
+  int max_tab_node_id_;
+
   DISALLOW_COPY_AND_ASSIGN(RecentTabsBuilderTestHelper);
 };
 
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
index 072c5df..f58ba89 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/prefs/scoped_user_pref_update.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/search.h"
 #include "chrome/browser/sessions/session_restore.h"
 #include "chrome/browser/sessions/tab_restore_service.h"
 #include "chrome/browser/sessions/tab_restore_service_delegate.h"
@@ -26,7 +27,6 @@
 #include "chrome/common/favicon/favicon_types.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/time_format.h"
-#include "chrome/common/url_constants.h"
 #include "grit/browser_resources.h"
 #include "grit/generated_resources.h"
 #include "grit/theme_resources.h"
@@ -417,8 +417,8 @@
           continue;
         const sessions::SerializedNavigationEntry& current_navigation =
             tab->navigations.at(tab->normalized_navigation_index());
-        if (current_navigation.virtual_url() ==
-            GURL(chrome::kChromeUINewTabURL)) {
+        if (chrome::IsNTPURL(current_navigation.virtual_url(),
+                             browser_->profile())) {
           continue;
         }
         tabs_in_session.push_back(tab);
diff --git a/chrome/browser/ui/views/app_list/app_list_controller_win.cc b/chrome/browser/ui/views/app_list/app_list_controller_win.cc
index 6028f1a..b87598a 100644
--- a/chrome/browser/ui/views/app_list/app_list_controller_win.cc
+++ b/chrome/browser/ui/views/app_list/app_list_controller_win.cc
@@ -454,7 +454,11 @@
 
   gfx::NativeWindow parent_hwnd =
       view->GetWidget()->GetTopLevelWidget()->GetNativeWindow();
-  chrome::ShowCreateChromeAppShortcutsDialog(parent_hwnd, profile, extension);
+  OnShowExtensionPrompt();
+  chrome::ShowCreateChromeAppShortcutsDialog(
+      parent_hwnd, profile, extension,
+      base::Bind(&AppListControllerDelegateWin::OnCloseExtensionPrompt,
+                 base::Unretained(this)));
 }
 
 void AppListControllerDelegateWin::CreateNewWindow(Profile* profile,
diff --git a/chrome/browser/ui/views/autofill/autofill_credit_card_bubble_views.cc b/chrome/browser/ui/views/autofill/autofill_credit_card_bubble_views.cc
index 4853230..fb2f238 100644
--- a/chrome/browser/ui/views/autofill/autofill_credit_card_bubble_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_credit_card_bubble_views.cc
@@ -28,8 +28,6 @@
   // TODO(dbeam): investigate why this steals focus from the web contents.
   views::BubbleDelegateView::CreateBubble(this);
 
-  GetBubbleFrameView()->SetTitle(controller_->BubbleTitle());
-
   GetWidget()->Show();
   SizeToContents();
 }
@@ -42,6 +40,10 @@
   return GetWidget() && GetWidget()->IsClosed();
 }
 
+string16 AutofillCreditCardBubbleViews::GetWindowTitle() const {
+  return controller_->BubbleTitle();
+}
+
 void AutofillCreditCardBubbleViews::Init() {
   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0,
                                         views::kRelatedControlVerticalSpacing));
diff --git a/chrome/browser/ui/views/autofill/autofill_credit_card_bubble_views.h b/chrome/browser/ui/views/autofill/autofill_credit_card_bubble_views.h
index c5d0fb6..9d8e8f1 100644
--- a/chrome/browser/ui/views/autofill/autofill_credit_card_bubble_views.h
+++ b/chrome/browser/ui/views/autofill/autofill_credit_card_bubble_views.h
@@ -30,6 +30,7 @@
   virtual bool IsHiding() const OVERRIDE;
 
   // views::BubbleDelegateView:
+  virtual string16 GetWindowTitle() const OVERRIDE;
   virtual void Init() OVERRIDE;
   virtual gfx::Size GetPreferredSize() OVERRIDE;
 
diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
index e9bf061..b580df5 100644
--- a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/autofill/autofill_dialog_controller.h"
 #include "chrome/browser/ui/autofill/autofill_dialog_sign_in_delegate.h"
+#include "chrome/browser/ui/views/autofill/decorated_textfield.h"
 #include "chrome/browser/ui/views/constrained_window_views.h"
 #include "components/autofill/content/browser/wallet/wallet_service_url.h"
 #include "components/autofill/core/browser/autofill_type.h"
@@ -69,12 +70,6 @@
 // Horizontal padding between text and other elements (in pixels).
 const int kAroundTextPadding = 4;
 
-// Padding around icons inside DecoratedTextfields.
-const int kTextfieldIconPadding = 3;
-
-// Size of the triangular mark that indicates an invalid textfield (in pixels).
-const int kDogEarSize = 10;
-
 // The space between the edges of a notification bar and the text within (in
 // pixels).
 const int kNotificationPadding = 17;
@@ -116,7 +111,6 @@
 // places.
 const SkColor kGreyTextColor = SkColorSetRGB(102, 102, 102);
 
-const char kDecoratedTextfieldClassName[] = "autofill/DecoratedTextfield";
 const char kNotificationAreaClassName[] = "autofill/NotificationArea";
 const char kOverlayViewClassName[] = "autofill/OverlayView";
 
@@ -124,17 +118,27 @@
 typedef ui::MultiAnimation::Parts Parts;
 
 // Draws an arrow at the top of |canvas| pointing to |tip_x|.
-void DrawArrow(gfx::Canvas* canvas, int tip_x, const SkColor& color) {
+void DrawArrow(gfx::Canvas* canvas,
+               int tip_x,
+               const SkColor& fill_color,
+               const SkColor& stroke_color) {
   const int arrow_half_width = kArrowWidth / 2.0f;
 
   SkPath arrow;
-  arrow.moveTo(tip_x, 0);
-  arrow.rLineTo(arrow_half_width, kArrowHeight);
-  arrow.rLineTo(-kArrowWidth, 0);
-  arrow.close();
-  SkPaint paint;
-  paint.setColor(color);
-  canvas->DrawPath(arrow, paint);
+  arrow.moveTo(tip_x - arrow_half_width, kArrowHeight);
+  arrow.lineTo(tip_x, 0);
+  arrow.lineTo(tip_x + arrow_half_width, kArrowHeight);
+
+  SkPaint fill_paint;
+  fill_paint.setColor(fill_color);
+  canvas->DrawPath(arrow, fill_paint);
+
+  if (stroke_color != SK_ColorTRANSPARENT) {
+    SkPaint stroke_paint;
+    stroke_paint.setColor(stroke_color);
+    stroke_paint.setStyle(SkPaint::kStroke_Style);
+    canvas->DrawPath(arrow, stroke_paint);
+  }
 }
 
 // This class handles layout for the first row of a SuggestionView.
@@ -193,7 +197,7 @@
  public:
   explicit ErrorBubbleContents(const base::string16& message)
       : color_(kWarningColor) {
-    set_border(views::Border::CreateEmptyBorder(kArrowHeight, 0, 0, 0));
+    set_border(views::Border::CreateEmptyBorder(kArrowHeight - 3, 0, 0, 0));
 
     views::Label* label = new views::Label();
     label->SetText(message);
@@ -210,7 +214,7 @@
 
   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
     views::View::OnPaint(canvas);
-    DrawArrow(canvas, width() / 2.0f, color_);
+    DrawArrow(canvas, width() / 2.0f, color_, SK_ColorTRANSPARENT);
   }
 
  private:
@@ -362,6 +366,8 @@
 
     set_background(
        views::Background::CreateSolidBackground(data.GetBackgroundColor()));
+    set_border(views::Border::CreateSolidSidedBorder(1, 0, 1, 0,
+                                                     data.GetBorderColor()));
   }
 
   virtual ~NotificationView() {}
@@ -482,92 +488,6 @@
   return bubble_bounds;
 }
 
-// AutofillDialogViews::DecoratedTextfield -------------------------------------
-
-AutofillDialogViews::DecoratedTextfield::DecoratedTextfield(
-    const base::string16& default_value,
-    const base::string16& placeholder,
-    views::TextfieldController* controller)
-    : border_(new views::FocusableBorder()),
-      invalid_(false) {
-  set_background(
-      views::Background::CreateSolidBackground(GetBackgroundColor()));
-
-  set_border(border_);
-  // Removes the border from |native_wrapper_|.
-  RemoveBorder();
-
-  set_placeholder_text(placeholder);
-  SetText(default_value);
-  SetController(controller);
-  SetHorizontalMargins(0, 0);
-}
-
-AutofillDialogViews::DecoratedTextfield::~DecoratedTextfield() {}
-
-void AutofillDialogViews::DecoratedTextfield::SetInvalid(bool invalid) {
-  invalid_ = invalid;
-  if (invalid)
-    border_->SetColor(kWarningColor);
-  else
-    border_->UseDefaultColor();
-  SchedulePaint();
-}
-
-void AutofillDialogViews::DecoratedTextfield::SetIcon(const gfx::Image& icon) {
-  int icon_space = icon.IsEmpty() ? 0 :
-                                    icon.Width() + 2 * kTextfieldIconPadding;
-  int left = base::i18n::IsRTL() ? icon_space : 0;
-  int right = base::i18n::IsRTL() ? 0 : icon_space;
-  SetHorizontalMargins(left, right);
-  icon_ = icon;
-
-  PreferredSizeChanged();
-  SchedulePaint();
-}
-
-const char* AutofillDialogViews::DecoratedTextfield::GetClassName() const {
-  return kDecoratedTextfieldClassName;
-}
-
-void AutofillDialogViews::DecoratedTextfield::PaintChildren(
-    gfx::Canvas* canvas) {}
-
-void AutofillDialogViews::DecoratedTextfield::OnPaint(gfx::Canvas* canvas) {
-  // Draw the border and background.
-  border_->set_has_focus(HasFocus());
-  views::View::OnPaint(canvas);
-
-  // Then the textfield.
-  views::View::PaintChildren(canvas);
-
-  // Then the icon.
-  if (!icon_.IsEmpty()) {
-    gfx::Rect bounds = GetContentsBounds();
-    int x = base::i18n::IsRTL() ?
-        kTextfieldIconPadding :
-        bounds.right() - icon_.Width() - kTextfieldIconPadding;
-    canvas->DrawImageInt(icon_.AsImageSkia(), x,
-                         bounds.y() + (bounds.height() - icon_.Height()) / 2);
-  }
-
-  // Then the invalid indicator.
-  if (invalid_) {
-    if (base::i18n::IsRTL()) {
-      canvas->Translate(gfx::Vector2d(width(), 0));
-      canvas->Scale(-1, 1);
-    }
-
-    SkPath dog_ear;
-    dog_ear.moveTo(width() - kDogEarSize, 0);
-    dog_ear.lineTo(width(), 0);
-    dog_ear.lineTo(width(), kDogEarSize);
-    dog_ear.close();
-    canvas->ClipPath(dog_ear);
-    canvas->DrawColor(kWarningColor);
-  }
-}
-
 // AutofillDialogViews::AccountChooser -----------------------------------------
 
 AutofillDialogViews::AccountChooser::AccountChooser(
@@ -862,7 +782,8 @@
     : controller_(controller),
       checkbox_(NULL) {
   // Reserve vertical space for the arrow (regardless of whether one exists).
-  set_border(views::Border::CreateEmptyBorder(kArrowHeight, 0, 0, 0));
+  // The -1 accounts for the border.
+  set_border(views::Border::CreateEmptyBorder(kArrowHeight - 1, 0, 0, 0));
 
   views::BoxLayout* box_layout =
       new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
@@ -907,14 +828,19 @@
   return kNotificationAreaClassName;
 }
 
+void AutofillDialogViews::NotificationArea::PaintChildren(
+    gfx::Canvas* canvas) {}
+
 void AutofillDialogViews::NotificationArea::OnPaint(gfx::Canvas* canvas) {
   views::View::OnPaint(canvas);
+  views::View::PaintChildren(canvas);
 
   if (HasArrow()) {
     DrawArrow(
         canvas,
         GetMirroredXInView(width() - arrow_centering_anchor_->width() / 2.0f),
-        notifications_[0].GetBackgroundColor());
+        notifications_[0].GetBackgroundColor(),
+        notifications_[0].GetBorderColor());
   }
 }
 
@@ -954,6 +880,7 @@
                                               kDetailSectionVerticalPadding,
                                               kDialogEdgePadding));
 
+  // TODO(estade): this label should be semi-bold.
   views::Label* label_view = new views::Label(label);
   label_view->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
@@ -978,12 +905,8 @@
   label_bar_layout->StartRow(0, kColumnSetId);
   label_bar_layout->AddView(label_view);
   label_bar_layout->AddView(proxy_button);
-  // TODO(estade): Make this the correct color, also sometimes hide the border.
-  label_bar->set_border(
-      views::Border::CreateSolidSidedBorder(0, 0, 1, 0, SK_ColorLTGRAY));
 
-  // TODO(estade): do something about this '7'.
-  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 7));
+  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
   AddChildView(label_bar);
   AddChildView(controls);
 }
@@ -1073,6 +996,10 @@
           new DecoratedTextfield(base::string16(),
                                  base::string16(),
                                  autofill_dialog)) {
+  // TODO(estade): Make this the correct color.
+  set_border(
+      views::Border::CreateSolidSidedBorder(1, 0, 0, 0, SK_ColorLTGRAY));
+
   // Label and icon.
   label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   label_container_->AddChildView(icon_);
@@ -1088,7 +1015,8 @@
   label_line_2_->SetMultiLine(true);
   AddChildView(label_line_2_);
 
-  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
+  // TODO(estade): do something about this '2'.
+  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 2, 0));
 }
 
 AutofillDialogViews::SuggestionView::~SuggestionView() {}
@@ -1228,8 +1156,6 @@
       web_contents_modal_dialog_manager->delegate()->
           GetWebContentsModalDialogHost());
   web_contents_modal_dialog_manager->ShowDialog(window_->GetNativeView());
-  web_contents_modal_dialog_manager->SetPreventCloseOnLoadStart(
-      window_->GetNativeView(), true);
   focus_manager_ = window_->GetFocusManager();
   focus_manager_->AddFocusChangeListener(this);
 
@@ -1966,7 +1892,7 @@
 
     float expand = input.expand_weight;
     column_set->AddColumn(views::GridLayout::FILL,
-                          views::GridLayout::BASELINE,
+                          views::GridLayout::FILL,
                           expand ? expand : 1.0,
                           views::GridLayout::USE_PREF,
                           0,
@@ -1976,7 +1902,7 @@
     // view's preferred width. Thus the width of the column completely depends
     // on |expand|.
     layout->AddView(view_to_add.release(), 1, 1,
-                    views::GridLayout::FILL, views::GridLayout::BASELINE,
+                    views::GridLayout::FILL, views::GridLayout::FILL,
                     1, 0);
   }
 
@@ -2294,7 +2220,7 @@
       return group;
 
     views::View* decorated =
-        view->GetAncestorWithClassName(kDecoratedTextfieldClassName);
+        view->GetAncestorWithClassName(DecoratedTextfield::kViewClassName);
 
     // Textfields need to check a second case, since they can be
     // suggested inputs instead of directly editable inputs. Those are
diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.h b/chrome/browser/ui/views/autofill/autofill_dialog_views.h
index 9fe4d8f..d076667 100644
--- a/chrome/browser/ui/views/autofill/autofill_dialog_views.h
+++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.h
@@ -21,7 +21,6 @@
 #include "ui/views/controls/progress_bar.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/styled_label_listener.h"
-#include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/controls/textfield/textfield_controller.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/widget/widget_observer.h"
@@ -39,7 +38,6 @@
 class BubbleBorder;
 class Checkbox;
 class Combobox;
-class FocusableBorder;
 class FocusManager;
 class ImageButton;
 class ImageView;
@@ -61,6 +59,7 @@
 namespace autofill {
 
 class AutofillDialogSignInDelegate;
+class DecoratedTextfield;
 struct DetailInput;
 
 // Views toolkit implementation of the Autofill dialog that handles the
@@ -198,42 +197,6 @@
     DISALLOW_COPY_AND_ASSIGN(ErrorBubble);
   };
 
-  // A class which holds a textfield and draws extra stuff on top, like
-  // invalid content indications.
-  class DecoratedTextfield : public views::Textfield {
-   public:
-    DecoratedTextfield(const base::string16& default_value,
-                       const base::string16& placeholder,
-                       views::TextfieldController* controller);
-    virtual ~DecoratedTextfield();
-
-    // Sets whether to indicate the textfield has invalid content.
-    void SetInvalid(bool invalid);
-    bool invalid() const { return invalid_; }
-
-    // Sets the icon to be displayed inside the textfield at the end of the
-    // text.
-    void SetIcon(const gfx::Image& icon);
-
-    // views::View implementation.
-    virtual const char* GetClassName() const OVERRIDE;
-    virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE;
-    virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
-
-   private:
-    // We draw the border.
-    views::FocusableBorder* border_;  // Weak.
-
-    // The icon that goes at the right side of the textfield.
-    gfx::Image icon_;
-
-    // Whether the text contents are "invalid" (i.e. should special markers be
-    // shown to indicate invalidness).
-    bool invalid_;
-
-    DISALLOW_COPY_AND_ASSIGN(DecoratedTextfield);
-  };
-
   // A View which displays the currently selected account and lets the user
   // switch accounts.
   class AccountChooser : public views::View,
@@ -342,6 +305,7 @@
     // views::View implementation.
     virtual gfx::Size GetPreferredSize() OVERRIDE;
     virtual const char* GetClassName() const OVERRIDE;
+    virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE;
     virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
 
     // views::ButtonListener implementation:
diff --git a/chrome/browser/ui/views/autofill/decorated_textfield.cc b/chrome/browser/ui/views/autofill/decorated_textfield.cc
new file mode 100644
index 0000000..139c5d8
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/decorated_textfield.cc
@@ -0,0 +1,124 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/ui/views/autofill/decorated_textfield.h"
+
+#include "chrome/browser/ui/autofill/autofill_dialog_types.h"
+#include "ui/gfx/canvas.h"
+#include "ui/views/background.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/focusable_border.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
+
+namespace {
+
+// Padding around icons inside DecoratedTextfields.
+const int kTextfieldIconPadding = 3;
+
+// Size of the triangular mark that indicates an invalid textfield (in pixels).
+const int kDogEarSize = 10;
+
+}  // namespace
+
+namespace autofill {
+
+// static
+const char DecoratedTextfield::kViewClassName[] = "autofill/DecoratedTextfield";
+
+const int DecoratedTextfield::kMagicInsetNumber = 6;
+
+DecoratedTextfield::DecoratedTextfield(
+    const base::string16& default_value,
+    const base::string16& placeholder,
+    views::TextfieldController* controller)
+    : border_(new views::FocusableBorder()),
+      invalid_(false) {
+  set_background(
+      views::Background::CreateSolidBackground(GetBackgroundColor()));
+
+  set_border(border_);
+  // Removes the border from |native_wrapper_|.
+  RemoveBorder();
+
+  set_placeholder_text(placeholder);
+  SetText(default_value);
+  SetController(controller);
+  SetHorizontalMargins(0, 0);
+}
+
+DecoratedTextfield::~DecoratedTextfield() {}
+
+void DecoratedTextfield::SetInvalid(bool invalid) {
+  invalid_ = invalid;
+  if (invalid)
+    border_->SetColor(kWarningColor);
+  else
+    border_->UseDefaultColor();
+  SchedulePaint();
+}
+
+void DecoratedTextfield::SetIcon(const gfx::Image& icon) {
+  int icon_space = icon.IsEmpty() ? 0 :
+                                    icon.Width() + 2 * kTextfieldIconPadding;
+  // Extra indent inside of textfield before text starts, in px.
+  const int kTextIndent = 6;
+  int left = base::i18n::IsRTL() ? icon_space : kTextIndent;
+  int right = base::i18n::IsRTL() ? kTextIndent : icon_space;
+  SetHorizontalMargins(left, right);
+  icon_ = icon;
+
+  PreferredSizeChanged();
+  SchedulePaint();
+}
+
+const char* DecoratedTextfield::GetClassName() const {
+  return kViewClassName;
+}
+
+void DecoratedTextfield::PaintChildren(gfx::Canvas* canvas) {}
+
+void DecoratedTextfield::OnPaint(gfx::Canvas* canvas) {
+  // Draw the border and background.
+  border_->set_has_focus(HasFocus());
+  views::View::OnPaint(canvas);
+
+  // Then the textfield.
+  views::View::PaintChildren(canvas);
+
+  // Then the icon.
+  if (!icon_.IsEmpty()) {
+    gfx::Rect bounds = GetContentsBounds();
+    int x = base::i18n::IsRTL() ?
+        kTextfieldIconPadding :
+        bounds.right() - icon_.Width() - kTextfieldIconPadding;
+    canvas->DrawImageInt(icon_.AsImageSkia(), x,
+                         bounds.y() + (bounds.height() - icon_.Height()) / 2);
+  }
+
+  // Then the invalid indicator.
+  if (invalid_) {
+    if (base::i18n::IsRTL()) {
+      canvas->Translate(gfx::Vector2d(width(), 0));
+      canvas->Scale(-1, 1);
+    }
+
+    SkPath dog_ear;
+    dog_ear.moveTo(width() - kDogEarSize, 0);
+    dog_ear.lineTo(width(), 0);
+    dog_ear.lineTo(width(), kDogEarSize);
+    dog_ear.close();
+    canvas->ClipPath(dog_ear);
+    canvas->DrawColor(kWarningColor);
+  }
+}
+
+gfx::Size DecoratedTextfield::GetPreferredSize() {
+  int w = views::Textfield::GetPreferredSize().width();
+  views::LabelButton button(NULL, string16());
+  button.SetStyle(views::Button::STYLE_BUTTON);
+  int h = button.GetPreferredSize().height();
+  return gfx::Size(w, h - kMagicInsetNumber);
+}
+
+} // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/decorated_textfield.h b/chrome/browser/ui/views/autofill/decorated_textfield.h
new file mode 100644
index 0000000..b16c4ea
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/decorated_textfield.h
@@ -0,0 +1,68 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_DECORATED_TEXTFIELD_H_
+#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_DECORATED_TEXTFIELD_H_
+
+#include "base/strings/string16.h"
+#include "ui/gfx/image/image.h"
+#include "ui/views/controls/textfield/textfield.h"
+
+namespace views {
+class FocusableBorder;
+class TextfieldController;
+}
+
+namespace autofill {
+
+// A class which holds a textfield and draws extra stuff on top, like
+// invalid content indications.
+class DecoratedTextfield : public views::Textfield {
+ public:
+  static const char kViewClassName[];
+
+  DecoratedTextfield(const base::string16& default_value,
+                     const base::string16& placeholder,
+                     views::TextfieldController* controller);
+  virtual ~DecoratedTextfield();
+
+  // Sets whether to indicate the textfield has invalid content.
+  void SetInvalid(bool invalid);
+  bool invalid() const { return invalid_; }
+
+  // Sets the icon to be displayed inside the textfield at the end of the
+  // text.
+  void SetIcon(const gfx::Image& icon);
+
+  // views::View implementation.
+  virtual const char* GetClassName() const OVERRIDE;
+  virtual gfx::Size GetPreferredSize() OVERRIDE;
+  virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE;
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(DecoratedTextfieldTest, HeightMatchesButton);
+
+  // This number corresponds to the number of pixels in the images that
+  // are used to draw a views button which are above or below the actual border.
+  // This number is encoded in the button assets themselves, so there's no other
+  // way to get it than to hardcode it here.
+  static const int kMagicInsetNumber;
+
+  // We draw the border.
+  views::FocusableBorder* border_;  // Weak.
+
+  // The icon that goes at the right side of the textfield.
+  gfx::Image icon_;
+
+  // Whether the text contents are "invalid" (i.e. should special markers be
+  // shown to indicate invalidness).
+  bool invalid_;
+
+  DISALLOW_COPY_AND_ASSIGN(DecoratedTextfield);
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_VIEWS_AUTOFILL_DECORATED_TEXTFIELD_H_
diff --git a/chrome/browser/ui/views/autofill/decorated_textfield_unittest.cc b/chrome/browser/ui/views/autofill/decorated_textfield_unittest.cc
new file mode 100644
index 0000000..e8b2fe8
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/decorated_textfield_unittest.cc
@@ -0,0 +1,23 @@
+// Copyright 2013 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.
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/views/autofill/decorated_textfield.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/controls/button/label_button.h"
+
+namespace autofill {
+
+TEST(DecoratedTextfieldTest, HeightMatchesButton) {
+  DecoratedTextfield textfield(ASCIIToUTF16("default"),
+                               ASCIIToUTF16("placeholder"),
+                               NULL);
+  views::LabelButton button(NULL, ASCIIToUTF16("anyoldtext"));;
+  button.SetStyle(views::Button::STYLE_BUTTON);
+  EXPECT_EQ(button.GetPreferredSize().height() -
+                DecoratedTextfield::kMagicInsetNumber,
+            textfield.GetPreferredSize().height());
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/views/avatar_menu_button_browsertest.cc b/chrome/browser/ui/views/avatar_menu_button_browsertest.cc
index c5b7185..c097c71 100644
--- a/chrome/browser/ui/views/avatar_menu_button_browsertest.cc
+++ b/chrome/browser/ui/views/avatar_menu_button_browsertest.cc
@@ -20,16 +20,13 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "content/public/test/test_utils.h"
 #include "grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/views/controls/button/label_button.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 class AvatarMenuButtonTest : public InProcessBrowserTest,
                              public testing::WithParamInterface<bool> {
  public:
@@ -111,7 +108,7 @@
 IN_PROC_BROWSER_TEST_P(AvatarMenuButtonTest, HideOnSecondClick) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
index 86b9060..3422e7a 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h"
 
-#include "base/command_line.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -17,7 +16,6 @@
 #include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view_observer.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_sync_promo_view.h"
-#include "chrome/common/chrome_switches.h"
 #include "content/public/browser/user_metrics.h"
 #include "grit/generated_resources.h"
 #include "grit/theme_resources.h"
@@ -249,9 +247,7 @@
       0,
       views::kUnrelatedControlVerticalSpacing - kControlBorderWidth);
 
-  const CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableBookmarkSyncPromo) &&
-      SyncPromoUI::ShouldShowSyncPromo(profile_)) {
+  if (SyncPromoUI::ShouldShowSyncPromo(profile_)) {
     // The column layout used for the sync promo.
     cs = layout->AddColumnSet(SYNC_PROMO_COLUMN_SET_ID);
     cs->AddColumn(GridLayout::FILL,
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
index 1809017..9a2e5a2 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <string>
 
-#include "base/command_line.h"
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/bookmarks/bookmark_utils.h"
@@ -14,7 +13,6 @@
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/ui/bookmarks/bookmark_bubble_delegate.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 
@@ -30,9 +28,6 @@
   virtual void SetUp() OVERRIDE {
     BrowserWithTestWindowTest::SetUp();
 
-    CommandLine* command_line = CommandLine::ForCurrentProcess();
-    command_line->AppendSwitch(switches::kEnableBookmarkSyncPromo);
-
     profile()->CreateBookmarkModel(true);
     ui_test_utils::WaitForBookmarkModelToLoad(profile());
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
index 44bc5e2..f987e04 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -229,11 +229,6 @@
   title_tf_->RequestFocus();
 }
 
-void BookmarkEditorView::Close() {
-  DCHECK(GetWidget());
-  GetWidget()->Close();
-}
-
 void BookmarkEditorView::ShowContextMenuForView(
     views::View* source,
     const gfx::Point& point,
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
index 21f553d..d9003ed 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.h
@@ -116,9 +116,6 @@
   // closed the BookmarkEditorView is deleted.
   void Show(gfx::NativeWindow parent);
 
-  // Closes the dialog.
-  void Close();
-
   // views::ContextMenuController:
   virtual void ShowContextMenuForView(views::View* source,
                                       const gfx::Point& point,
diff --git a/chrome/browser/ui/views/browser_dialogs.h b/chrome/browser/ui/views/browser_dialogs.h
index 30d12a5..4260d61 100644
--- a/chrome/browser/ui/views/browser_dialogs.h
+++ b/chrome/browser/ui/views/browser_dialogs.h
@@ -59,9 +59,11 @@
                       Profile* profile);
 
 // Shows the create chrome app shortcut dialog box.
-void ShowCreateChromeAppShortcutsDialog(gfx::NativeWindow parent_window,
-                                        Profile* profile,
-                                        const extensions::Extension* app);
+void ShowCreateChromeAppShortcutsDialog(
+    gfx::NativeWindow parent_window,
+    Profile* profile,
+    const extensions::Extension* app,
+    const base::Closure& close_callback);
 
 }  // namespace chrome
 
diff --git a/chrome/browser/ui/views/create_application_shortcut_view.cc b/chrome/browser/ui/views/create_application_shortcut_view.cc
index 756db33..14f0b41 100644
--- a/chrome/browser/ui/views/create_application_shortcut_view.cc
+++ b/chrome/browser/ui/views/create_application_shortcut_view.cc
@@ -234,11 +234,13 @@
       parent_window)->Show();
 }
 
-void ShowCreateChromeAppShortcutsDialog(gfx::NativeWindow parent_window,
-                                        Profile* profile,
-                                        const extensions::Extension* app) {
+void ShowCreateChromeAppShortcutsDialog(
+    gfx::NativeWindow parent_window,
+    Profile* profile,
+    const extensions::Extension* app,
+    const base::Closure& close_callback) {
   CreateBrowserModalDialogViews(
-      new CreateChromeApplicationShortcutView(profile, app),
+      new CreateChromeApplicationShortcutView(profile, app, close_callback),
       parent_window)->Show();
 }
 
@@ -504,10 +506,12 @@
 
 CreateChromeApplicationShortcutView::CreateChromeApplicationShortcutView(
     Profile* profile,
-    const extensions::Extension* app) :
-      CreateApplicationShortcutView(profile),
-      app_(app),
-      weak_ptr_factory_(this) {
+    const extensions::Extension* app,
+    const base::Closure& close_callback)
+        : CreateApplicationShortcutView(profile),
+          app_(app),
+          close_callback_(close_callback),
+          weak_ptr_factory_(this) {
   // Required by InitControls().
   shortcut_info_.title = UTF8ToUTF16(app->name());
   shortcut_info_.description = UTF8ToUTF16(app->description());
@@ -526,6 +530,16 @@
 
 CreateChromeApplicationShortcutView::~CreateChromeApplicationShortcutView() {}
 
+bool CreateChromeApplicationShortcutView::Accept() {
+  close_callback_.Run();
+  return CreateApplicationShortcutView::Accept();
+}
+
+bool CreateChromeApplicationShortcutView::Cancel() {
+  close_callback_.Run();
+  return CreateApplicationShortcutView::Cancel();
+}
+
 // Called when the app's ShortcutInfo (with icon) is loaded.
 void CreateChromeApplicationShortcutView::OnShortcutInfoLoaded(
     const ShellIntegration::ShortcutInfo& shortcut_info) {
diff --git a/chrome/browser/ui/views/create_application_shortcut_view.h b/chrome/browser/ui/views/create_application_shortcut_view.h
index 06cc08f..74aa877 100644
--- a/chrome/browser/ui/views/create_application_shortcut_view.h
+++ b/chrome/browser/ui/views/create_application_shortcut_view.h
@@ -118,15 +118,20 @@
 class CreateChromeApplicationShortcutView
     : public CreateApplicationShortcutView {
  public:
-  CreateChromeApplicationShortcutView(Profile* profile,
-                                      const extensions::Extension* app);
+  CreateChromeApplicationShortcutView(
+      Profile* profile,
+      const extensions::Extension* app,
+      const base::Closure& close_callback);
   virtual ~CreateChromeApplicationShortcutView();
+  virtual bool Accept() OVERRIDE;
+  virtual bool Cancel() OVERRIDE;
 
  private:
   void OnShortcutInfoLoaded(
       const ShellIntegration::ShortcutInfo& shortcut_info);
 
   const extensions::Extension* app_;
+  base::Closure close_callback_;
 
   base::WeakPtrFactory<CreateChromeApplicationShortcutView> weak_ptr_factory_;
 
diff --git a/chrome/browser/ui/views/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_media_picker_views.cc
new file mode 100644
index 0000000..685c155
--- /dev/null
+++ b/chrome/browser/ui/views/desktop_media_picker_views.cc
@@ -0,0 +1,560 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/media/desktop_media_picker.h"
+
+#include "ash/shell.h"
+#include "base/callback.h"
+#include "chrome/browser/media/desktop_media_picker_model.h"
+#include "content/public/browser/browser_thread.h"
+#include "grit/generated_resources.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/native_theme/native_theme.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/scroll_view.h"
+#include "ui/views/corewm/shadow_types.h"
+#include "ui/views/focus_border.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/layout_constants.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/window/dialog_client_view.h"
+#include "ui/views/window/dialog_delegate.h"
+
+namespace {
+
+const int kThumbnailWidth = 160;
+const int kThumbnailHeight = 100;
+const int kThumbnailMargin = 10;
+const int kLabelHeight = 40;
+const int kListItemWidth = kThumbnailMargin * 2 + kThumbnailWidth;
+const int kListItemHeight =
+    kThumbnailMargin * 2 + kThumbnailHeight + kLabelHeight;
+const int kListColumns = 3;
+const int kTotalListWidth = kListColumns * kListItemWidth;
+
+const int kDesktopMediaSourceViewGroupId = 1;
+
+const char kDesktopMediaSourceViewClassName[] =
+    "DesktopMediaPicker_DesktopMediaSourceView";
+
+class DesktopMediaListView;
+class DesktopMediaPickerDialogView;
+class DesktopMediaPickerViews;
+
+// View used for each item in DesktopMediaListView. Shows a single desktop media
+// source as a thumbnail with the title under it.
+class DesktopMediaSourceView : public views::View {
+ public:
+  DesktopMediaSourceView(DesktopMediaListView* parent,
+                         DesktopMediaPickerModel::SourceId source_id);
+  virtual ~DesktopMediaSourceView();
+
+  // Updates thumbnail and title from |source|.
+  void SetName(const string16& name);
+  void SetThumbnail(const gfx::ImageSkia& thumbnail);
+
+  // Id for the source shown by this View.
+  const DesktopMediaPickerModel::SourceId& source_id() const {
+    return source_id_;
+  }
+
+  // Returns true if the source is selected.
+  bool is_selected() const { return selected_; }
+
+  // Updates selection state of the element. If |selected| is true then also
+  // calls SetSelected(false) for the source view that was selected before that
+  // (if any).
+  void SetSelected(bool selected);
+
+  // views::View interface.
+  virtual const char* GetClassName() const OVERRIDE;
+  virtual void Layout() OVERRIDE;
+  virtual views::View* GetSelectedViewForGroup(int group) OVERRIDE;
+  virtual bool IsGroupFocusTraversable() const OVERRIDE;
+  virtual void OnFocus() OVERRIDE;
+  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
+
+ private:
+  DesktopMediaListView* parent_;
+
+  DesktopMediaPickerModel::SourceId source_id_;
+
+  views::ImageView* image_view_;
+  views::Label* label_;
+
+  bool selected_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopMediaSourceView);
+};
+
+// View that shows list of desktop media sources available from
+// DesktopMediaPickerModel.
+class DesktopMediaListView : public views::View,
+                             public DesktopMediaPickerModel::Observer {
+ public:
+  DesktopMediaListView(DesktopMediaPickerDialogView* parent,
+                       scoped_ptr<DesktopMediaPickerModel> model);
+  virtual ~DesktopMediaListView();
+
+  // Called by DesktopMediaSourceView when selection has changed.
+  void OnSelectionChanged();
+
+  // Returns currently selected source.
+  DesktopMediaSourceView* GetSelection();
+
+  // views::View overrides.
+  virtual gfx::Size GetPreferredSize() OVERRIDE;
+  virtual void Layout() OVERRIDE;
+  virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
+
+ private:
+  // DesktopMediaPickerModel::Observer interface
+  virtual void OnSourceAdded(int index) OVERRIDE;
+  virtual void OnSourceRemoved(int index) OVERRIDE;
+  virtual void OnSourceNameChanged(int index) OVERRIDE;
+  virtual void OnSourceThumbnailChanged(int index) OVERRIDE;
+
+  DesktopMediaPickerDialogView* parent_;
+  scoped_ptr<DesktopMediaPickerModel> model_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopMediaListView);
+};
+
+// Dialog view used for DesktopMediaPickerViews.
+class DesktopMediaPickerDialogView : public views::DialogDelegateView {
+ public:
+  DesktopMediaPickerDialogView(gfx::NativeWindow context,
+                               gfx::NativeWindow parent_window,
+                               DesktopMediaPickerViews* parent,
+                               const string16& app_name,
+                               scoped_ptr<DesktopMediaPickerModel> model);
+  virtual ~DesktopMediaPickerDialogView();
+
+  // Called by parent (DesktopMediaPickerViews) when it's destroyed.
+  void DetachParent();
+
+  // Called by DesktopMediaListView.
+  void OnSelectionChanged();
+
+  // views::View overrides.
+  virtual gfx::Size GetPreferredSize() OVERRIDE;
+  virtual void Layout() OVERRIDE;
+
+  // views::DialogDelegateView overrides.
+  virtual base::string16 GetWindowTitle() const OVERRIDE;
+  virtual bool IsDialogButtonEnabled(ui::DialogButton button) const OVERRIDE;
+  virtual bool Accept() OVERRIDE;
+  virtual void DeleteDelegate() OVERRIDE;
+
+ private:
+  DesktopMediaPickerViews* parent_;
+  string16 app_name_;
+
+  views::Label* label_;
+  views::ScrollView* scroll_view_;
+  DesktopMediaListView* list_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerDialogView);
+};
+
+// Implementation of DesktopMediaPicker for Views.
+class DesktopMediaPickerViews : public DesktopMediaPicker {
+ public:
+  DesktopMediaPickerViews();
+  virtual ~DesktopMediaPickerViews();
+
+  void NotifyDialogResult(DesktopMediaPickerModel::SourceId source);
+
+  // DesktopMediaPicker overrides.
+  virtual void Show(gfx::NativeWindow context,
+                    gfx::NativeWindow parent,
+                    const string16& app_name,
+                    scoped_ptr<DesktopMediaPickerModel> model,
+                    const DoneCallback& done_callback) OVERRIDE;
+
+ private:
+  DoneCallback callback_;
+
+  // The |dialog_| is owned by the corresponding views::Widget instance.
+  // When DesktopMediaPickerViews is destroyed the |dialog_| is destroyed
+  // asynchronously by closing the widget.
+  DesktopMediaPickerDialogView* dialog_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopMediaPickerViews);
+};
+
+DesktopMediaSourceView::DesktopMediaSourceView(
+    DesktopMediaListView* parent,
+    DesktopMediaPickerModel::SourceId source_id)
+    : parent_(parent),
+      source_id_(source_id),
+      image_view_(new views::ImageView()),
+      label_(new views::Label()),
+      selected_(false)  {
+  AddChildView(image_view_);
+  AddChildView(label_);
+  set_focusable(true);
+}
+
+DesktopMediaSourceView::~DesktopMediaSourceView() {}
+
+void DesktopMediaSourceView::SetName(const string16& name) {
+  label_->SetText(name);
+}
+
+void DesktopMediaSourceView::SetThumbnail(const gfx::ImageSkia& thumbnail) {
+  image_view_->SetImage(thumbnail);
+}
+
+void DesktopMediaSourceView::SetSelected(bool selected) {
+  if (selected == selected_)
+    return;
+  selected_ = selected;
+
+  if (selected) {
+    // Unselect all other sources.
+    Views neighbours;
+    parent()->GetViewsInGroup(GetGroup(), &neighbours);
+    for (Views::iterator i(neighbours.begin()); i != neighbours.end(); ++i) {
+      if (*i != this) {
+        DCHECK_EQ((*i)->GetClassName(), kDesktopMediaSourceViewClassName);
+        DesktopMediaSourceView* source_view =
+            static_cast<DesktopMediaSourceView*>(*i);
+        source_view->SetSelected(false);
+      }
+    }
+
+    const SkColor bg_color = GetNativeTheme()->GetSystemColor(
+        ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor);
+    set_background(views::Background::CreateSolidBackground(bg_color));
+
+    parent_->OnSelectionChanged();
+  } else {
+    set_background(NULL);
+  }
+
+  SchedulePaint();
+}
+
+const char* DesktopMediaSourceView::GetClassName() const {
+  return kDesktopMediaSourceViewClassName;
+}
+
+void DesktopMediaSourceView::Layout() {
+  image_view_->SetBounds(kThumbnailMargin, kThumbnailMargin,
+                         kThumbnailWidth, kThumbnailHeight);
+  label_->SetBounds(kThumbnailMargin, kThumbnailHeight + kThumbnailMargin,
+                    kThumbnailWidth, kLabelHeight);
+
+  set_focus_border(views::FocusBorder::CreateDashedFocusBorder(
+      kThumbnailMargin / 2, kThumbnailMargin / 2,
+      kThumbnailMargin / 2, kThumbnailMargin / 2));
+}
+
+views::View* DesktopMediaSourceView::GetSelectedViewForGroup(int group) {
+  Views neighbours;
+  parent()->GetViewsInGroup(group, &neighbours);
+  if (neighbours.empty())
+    return NULL;
+
+  for (Views::iterator i(neighbours.begin()); i != neighbours.end(); ++i) {
+    DCHECK_EQ((*i)->GetClassName(), kDesktopMediaSourceViewClassName);
+    DesktopMediaSourceView* source_view =
+        static_cast<DesktopMediaSourceView*>(*i);
+    if (source_view->selected_)
+      return source_view;
+  }
+  return NULL;
+}
+
+bool DesktopMediaSourceView::IsGroupFocusTraversable() const {
+  return false;
+}
+
+void DesktopMediaSourceView::OnFocus() {
+  View::OnFocus();
+  SetSelected(true);
+  ScrollRectToVisible(gfx::Rect(size()));
+}
+
+bool DesktopMediaSourceView::OnMousePressed(const ui::MouseEvent& event) {
+  RequestFocus();
+  return true;
+}
+
+DesktopMediaListView::DesktopMediaListView(
+    DesktopMediaPickerDialogView* parent,
+    scoped_ptr<DesktopMediaPickerModel> model)
+    : parent_(parent),
+      model_(model.Pass()) {
+  model_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight));
+  model_->StartUpdating(this);
+}
+
+DesktopMediaListView::~DesktopMediaListView() {
+}
+
+void DesktopMediaListView::OnSelectionChanged() {
+  parent_->OnSelectionChanged();
+}
+
+DesktopMediaSourceView* DesktopMediaListView::GetSelection() {
+  for (int i = 0; i < child_count(); ++i) {
+    DesktopMediaSourceView* source_view =
+        static_cast<DesktopMediaSourceView*>(child_at(i));
+    DCHECK_EQ(source_view->GetClassName(), kDesktopMediaSourceViewClassName);
+    if (source_view->is_selected())
+      return source_view;
+  }
+  return NULL;
+}
+
+gfx::Size DesktopMediaListView::GetPreferredSize() {
+  int total_rows = (child_count() + kListColumns - 1) / kListColumns;
+  return gfx::Size(kTotalListWidth, kListItemHeight * total_rows);
+}
+
+void DesktopMediaListView::Layout() {
+  int x = 0;
+  int y = 0;
+
+  for (int i = 0; i < child_count(); ++i) {
+    if (x + kListItemWidth > kTotalListWidth) {
+      x = 0;
+      y += kListItemHeight;
+    }
+
+    View* source_view = child_at(i);
+    source_view->SetBounds(x, y, kListItemWidth, kListItemHeight);
+
+    x += kListItemWidth;
+  }
+
+
+  y += kListItemHeight;
+  SetSize(gfx::Size(kTotalListWidth, y));
+}
+
+bool DesktopMediaListView::OnKeyPressed(const ui::KeyEvent& event) {
+  int position_increment = 0;
+  switch (event.key_code()) {
+    case ui::VKEY_UP:
+      position_increment = -kListColumns;
+      break;
+    case ui::VKEY_DOWN:
+      position_increment = kListColumns;
+      break;
+    case ui::VKEY_LEFT:
+      position_increment = -1;
+      break;
+    case ui::VKEY_RIGHT:
+      position_increment = 1;
+      break;
+    default:
+      return false;
+  }
+
+
+  if (position_increment != 0) {
+    DesktopMediaSourceView* selected = GetSelection();
+    DesktopMediaSourceView* new_selected = NULL;
+
+    if (selected) {
+      int index = GetIndexOf(selected);
+      int new_index = index + position_increment;
+      if (new_index >= child_count())
+        new_index = child_count() - 1;
+      else if (new_index < 0)
+        new_index = 0;
+      if (index != new_index) {
+        new_selected =
+            static_cast<DesktopMediaSourceView*>(child_at(new_index));
+      }
+    } else if (has_children()) {
+      new_selected = static_cast<DesktopMediaSourceView*>(child_at(0));
+    }
+
+    if (new_selected) {
+      GetFocusManager()->SetFocusedView(new_selected);
+    }
+
+    return true;
+  }
+
+  return false;
+}
+
+void DesktopMediaListView::OnSourceAdded(int index) {
+  const DesktopMediaPickerModel::Source& source = model_->source(index);
+  DesktopMediaSourceView* source_view =
+      new DesktopMediaSourceView(this, source.id);
+  source_view->SetName(source.name);
+  source_view->SetGroup(kDesktopMediaSourceViewGroupId);
+  AddChildViewAt(source_view, index);
+  Layout();
+}
+
+void DesktopMediaListView::OnSourceRemoved(int index) {
+  DesktopMediaSourceView* view =
+      static_cast<DesktopMediaSourceView*>(child_at(index));
+  DCHECK(view);
+  DCHECK_EQ(view->GetClassName(), kDesktopMediaSourceViewClassName);
+  bool was_selected = view->is_selected();
+  RemoveChildView(view);
+  delete view;
+
+  if (was_selected)
+    OnSelectionChanged();
+
+  PreferredSizeChanged();
+}
+
+void DesktopMediaListView::OnSourceNameChanged(int index) {
+  const DesktopMediaPickerModel::Source& source = model_->source(index);
+  DesktopMediaSourceView* source_view =
+      static_cast<DesktopMediaSourceView*>(child_at(index));
+  source_view->SetName(source.name);
+}
+
+void DesktopMediaListView::OnSourceThumbnailChanged(int index) {
+  const DesktopMediaPickerModel::Source& source = model_->source(index);
+  DesktopMediaSourceView* source_view =
+      static_cast<DesktopMediaSourceView*>(child_at(index));
+  source_view->SetThumbnail(source.thumbnail);
+}
+
+DesktopMediaPickerDialogView::DesktopMediaPickerDialogView(
+    gfx::NativeWindow context,
+    gfx::NativeWindow parent_window,
+    DesktopMediaPickerViews* parent,
+    const string16& app_name,
+    scoped_ptr<DesktopMediaPickerModel> model)
+    : parent_(parent),
+      app_name_(app_name),
+      label_(new views::Label()),
+      scroll_view_(views::ScrollView::CreateScrollViewWithBorder()),
+      list_view_(new DesktopMediaListView(this, model.Pass())) {
+  label_->SetText(
+      l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TEXT, app_name_));
+  label_->SetMultiLine(true);
+  label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  AddChildView(label_);
+
+  scroll_view_->SetContents(list_view_);
+  AddChildView(scroll_view_);
+
+  DialogDelegate::CreateDialogWidget(this, context, parent_window);
+  GetWidget()->Show();
+}
+
+DesktopMediaPickerDialogView::~DesktopMediaPickerDialogView() {}
+
+void DesktopMediaPickerDialogView::DetachParent() {
+  parent_ = NULL;
+}
+
+gfx::Size DesktopMediaPickerDialogView::GetPreferredSize() {
+  return gfx::Size(600, 500);
+}
+
+void DesktopMediaPickerDialogView::Layout() {
+  gfx::Rect rect = GetLocalBounds();
+  rect.Inset(views::kPanelHorizMargin, views::kPanelVertMargin);
+
+  gfx::Rect label_rect(rect.x(), rect.y(), rect.width(),
+                       label_->GetHeightForWidth(rect.width()));
+  label_->SetBoundsRect(label_rect);
+
+  int scroll_view_top = label_rect.bottom() + views::kPanelVerticalSpacing;
+  scroll_view_->SetBounds(
+      rect.x(), scroll_view_top,
+      rect.width(), rect.height() - scroll_view_top);
+}
+
+base::string16 DesktopMediaPickerDialogView::GetWindowTitle() const {
+  return l10n_util::GetStringFUTF16(IDS_DESKTOP_MEDIA_PICKER_TITLE, app_name_);
+}
+
+bool DesktopMediaPickerDialogView::IsDialogButtonEnabled(
+    ui::DialogButton button) const {
+  if (button == ui::DIALOG_BUTTON_OK)
+    return list_view_->GetSelection() != NULL;
+  return true;
+}
+
+bool DesktopMediaPickerDialogView::Accept() {
+  DesktopMediaSourceView* selection = list_view_->GetSelection();
+
+  // Ok button should only be enabled when a source is selected.
+  DCHECK(selection);
+
+  DesktopMediaPickerModel::SourceId source;
+  if (selection)
+    source = selection->source_id();
+
+  if (parent_)
+    parent_->NotifyDialogResult(source);
+
+  // Return true to close the window.
+  return true;
+}
+
+void DesktopMediaPickerDialogView::DeleteDelegate() {
+  // If the dialog is being closed then notify the parent about it.
+  if (parent_) {
+    parent_->NotifyDialogResult(
+        DesktopMediaPickerModel::SourceId(content::MEDIA_NO_SERVICE, 0));
+  }
+  delete this;
+}
+
+void DesktopMediaPickerDialogView::OnSelectionChanged() {
+  GetDialogClientView()->UpdateDialogButtons();
+}
+
+DesktopMediaPickerViews::DesktopMediaPickerViews()
+    : dialog_(NULL) {
+}
+
+DesktopMediaPickerViews::~DesktopMediaPickerViews() {
+  if (dialog_) {
+    dialog_->DetachParent();
+    dialog_->GetWidget()->Close();
+  }
+}
+
+void DesktopMediaPickerViews::Show(gfx::NativeWindow context,
+                                   gfx::NativeWindow parent,
+                                   const string16& app_name,
+                                   scoped_ptr<DesktopMediaPickerModel> model,
+                                   const DoneCallback& done_callback) {
+  callback_ = done_callback;
+  dialog_ = new DesktopMediaPickerDialogView(
+      context, parent, this, app_name, model.Pass());
+}
+
+void DesktopMediaPickerViews::NotifyDialogResult(
+    DesktopMediaPickerModel::SourceId source) {
+  // Once this method is called the |dialog_| will close and destroy itself.
+  dialog_->DetachParent();
+  dialog_ = NULL;
+
+  DCHECK(!callback_.is_null());
+
+  // Notify the |callback_| asynchronously because it may need to destroy
+  // DesktopMediaPicker.
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI, FROM_HERE,
+      base::Bind(callback_, source));
+  callback_.Reset();
+}
+
+}  // namespace
+
+// static
+scoped_ptr<DesktopMediaPicker> DesktopMediaPicker::Create() {
+  return scoped_ptr<DesktopMediaPicker>(new DesktopMediaPickerViews());
+}
diff --git a/chrome/browser/ui/views/extensions/extension_dialog.cc b/chrome/browser/ui/views/extensions/extension_dialog.cc
index 8ceff2f..2dac704 100644
--- a/chrome/browser/ui/views/extensions/extension_dialog.cc
+++ b/chrome/browser/ui/views/extensions/extension_dialog.cc
@@ -27,8 +27,7 @@
 
 ExtensionDialog::ExtensionDialog(extensions::ExtensionHost* host,
                                  ExtensionDialogObserver* observer)
-    : window_(NULL),
-      extension_host_(host),
+    : extension_host_(host),
       observer_(observer) {
   AddRef();  // Balanced in DeleteDelegate();
 
@@ -99,7 +98,7 @@
                                  int width,
                                  int height) {
   gfx::NativeWindow parent = base_window->GetNativeWindow();
-  window_ = CreateBrowserModalDialogViews(this, parent);
+  views::Widget* window = CreateBrowserModalDialogViews(this, parent);
 
   // Center the window over the browser.
   gfx::Point center = base_window->GetBounds().CenterPoint();
@@ -111,25 +110,17 @@
       GetDisplayNearestPoint(center).bounds();
   gfx::Rect bounds_rect = gfx::Rect(x, y, width, height);
   bounds_rect.AdjustToFit(screen_rect);
-  window_->SetBounds(bounds_rect);
+  window->SetBounds(bounds_rect);
 
-  window_->Show();
+  window->Show();
   // TODO(jamescook): Remove redundant call to Activate()?
-  window_->Activate();
+  window->Activate();
 }
 
 void ExtensionDialog::ObserverDestroyed() {
   observer_ = NULL;
 }
 
-void ExtensionDialog::Close() {
-  if (!window_)
-    return;
-
-  window_->Close();
-  window_ = NULL;
-}
-
 void ExtensionDialog::MaybeFocusRenderView() {
   views::FocusManager* focus_manager = GetWidget()->GetFocusManager();
   DCHECK(focus_manager != NULL);
@@ -225,7 +216,7 @@
       // If we aren't the host of the popup, then disregard the notification.
       if (content::Details<extensions::ExtensionHost>(host()) != details)
         return;
-      Close();
+      GetWidget()->Close();
       break;
     case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
       if (content::Details<extensions::ExtensionHost>(host()) != details)
diff --git a/chrome/browser/ui/views/extensions/extension_dialog.h b/chrome/browser/ui/views/extensions/extension_dialog.h
index 2a9c3d7..1f02a69 100644
--- a/chrome/browser/ui/views/extensions/extension_dialog.h
+++ b/chrome/browser/ui/views/extensions/extension_dialog.h
@@ -54,9 +54,6 @@
   // be sent notifications.
   void ObserverDestroyed();
 
-  // Closes the ExtensionDialog.
-  void Close();
-
   // Focus to the render view if possible.
   void MaybeFocusRenderView();
 
@@ -109,9 +106,6 @@
 
   void InitWindow(ui::BaseWindow* base_window, int width, int height);
 
-  // Window that holds the extension host view.
-  views::Widget* window_;
-
   // Window Title
   string16 window_title_;
 
diff --git a/chrome/browser/ui/views/extensions/extension_installed_bubble.cc b/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
index 1bbf9f4..5ffff39 100644
--- a/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
+++ b/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/extensions/extension_action_manager.h"
 #include "chrome/browser/extensions/extension_install_ui.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/singleton_tabs.h"
@@ -261,8 +262,8 @@
       configure_url = chrome::kChromeUIExtensionsURL;
       configure_url += chrome::kExtensionConfigureCommandsSubPage;
     } else if (source == sign_in_link_) {
-      configure_url = SyncPromoUI::GetSyncPromoURL(
-          SyncPromoUI::SOURCE_EXTENSION_INSTALL_BUBBLE, false).spec();
+      configure_url = signin::GetPromoURL(
+          signin::SOURCE_EXTENSION_INSTALL_BUBBLE, false).spec();
     } else {
       NOTREACHED();
       return;
diff --git a/chrome/browser/ui/views/external_tab_container_win.cc b/chrome/browser/ui/views/external_tab_container_win.cc
index b921b66..806f3b0 100644
--- a/chrome/browser/ui/views/external_tab_container_win.cc
+++ b/chrome/browser/ui/views/external_tab_container_win.cc
@@ -1439,13 +1439,17 @@
 // static
 ExternalTabContainer* ExternalTabContainer::GetContainerForTab(
     content::WebContents* web_contents) {
-  HWND webcontents_view_window = views::HWNDForNativeWindow(
+  HWND window = views::HWNDForNativeWindow(
       web_contents->GetView()->GetNativeView());
-  HWND parent_window = ::GetParent(webcontents_view_window);
-  if (!::IsWindow(parent_window))
+#if !defined(USE_AURA)
+  // In the non-Aura case, it is the parent of the WebContents's view that has
+  // the property set.
+  window = ::GetParent(window);
+  if (!::IsWindow(window))
     return NULL;
+#endif
   return reinterpret_cast<ExternalTabContainerWin*>(
-      ui::ViewProp::GetValue(parent_window, kWindowObjectKey));
+      ui::ViewProp::GetValue(window, kWindowObjectKey));
 }
 
 // static
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index ee5dda2..86ddf41 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -56,15 +56,11 @@
 void BrowserNonClientFrameView::UpdateAvatarInfo() {
   if (browser_view_->ShouldShowAvatar()) {
     if (!avatar_button_) {
-#if defined(ENABLE_MANAGED_USERS)
       Profile* profile = browser_view_->browser()->profile();
-      ManagedUserService* service =
-          ManagedUserServiceFactory::GetForProfile(profile);
-      if (service->ProfileIsManaged() && !avatar_label_) {
+      if (profile->IsManaged() && !avatar_label_) {
         avatar_label_ = new AvatarLabel(browser_view_);
         AddChildView(avatar_label_);
       }
-#endif
       avatar_button_ = new AvatarMenuButton(browser_view_->browser(),
                                             browser_view_->IsOffTheRecord());
       AddChildView(avatar_button_);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 67491f6..67a8e69 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -123,6 +123,12 @@
     views::View* tabstrip) const {
   if (!tabstrip)
     return gfx::Rect();
+
+  // When the tab strip is painted in the immersive fullscreen light bar style,
+  // the caption buttons and the avatar button are not visible. However, their
+  // bounds are still used to compute the tab strip bounds so that the tabs have
+  // the same horizontal position when the tab strip is painted in the immersive
+  // light bar style as when the top-of-window views are revealed.
   TabStripInsets insets(GetTabStripInsets(false));
   return gfx::Rect(insets.left, insets.top,
                    std::max(0, width() - insets.left - insets.right),
@@ -185,21 +191,12 @@
 }
 
 void BrowserNonClientFrameViewAsh::ResetWindowControls() {
-  if (ImmersiveFullscreenConfiguration::UseImmersiveFullscreen()) {
-    // Hide the caption buttons in immersive mode because it's confusing when
-    // the user hovers or clicks in the top-right of the screen and hits one.
-    // Only show them during a reveal.
-    ImmersiveModeController* controller =
-        browser_view()->immersive_mode_controller();
-    if (controller->IsEnabled()) {
-      bool revealed = controller->IsRevealed();
-      size_button_->SetVisible(revealed);
-      close_button_->SetVisible(revealed);
-    } else {
-      size_button_->SetVisible(true);
-      close_button_->SetVisible(true);
-    }
-  }
+  // Hide the caption buttons in immersive fullscreen when the tab light bar
+  // is visible because it's confusing when the user hovers or clicks in the
+  // top-right of the screen and hits one.
+  bool button_visibility = !UseImmersiveLightbarHeaderStyle();
+  size_button_->SetVisible(button_visibility);
+  close_button_->SetVisible(button_visibility);
 
   size_button_->SetState(views::CustomButton::STATE_NORMAL);
   // The close button isn't affected by this constraint.
@@ -221,6 +218,12 @@
 void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) {
   if (!ShouldPaint())
     return;
+
+  if (UseImmersiveLightbarHeaderStyle()) {
+    PaintImmersiveLightbarStyleHeader(canvas);
+    return;
+  }
+
   // The primary header image changes based on window activation state and
   // theme, so we look it up for each paint.
   int theme_frame_image_id = GetThemeFrameImageId();
@@ -392,7 +395,7 @@
     bool force_restored) const {
   if (force_restored)
     return kTabstripTopSpacingTall;
-  if (frame()->IsFullscreen())
+  if (!ShouldPaint() || UseImmersiveLightbarHeaderStyle())
     return 0;
   // Windows with tab strips need a smaller non-client area.
   if (browser_view()->IsTabStripVisible()) {
@@ -408,7 +411,8 @@
 bool BrowserNonClientFrameViewAsh::UseShortHeader() const {
   // Restored browser -> tall header
   // Maximized browser -> short header
-  // Fullscreen browser (header shows with immersive reveal) -> short header
+  // Fullscreen browser, no immersive reveal -> hidden or super short light bar
+  // Fullscreen browser, immersive reveal -> short header
   // Popup&App window -> tall header
   // Panel -> short header
   // Dialogs use short header and are handled via CustomFrameViewAsh.
@@ -424,40 +428,61 @@
   }
 }
 
+bool BrowserNonClientFrameViewAsh::UseImmersiveLightbarHeaderStyle() const {
+  ImmersiveModeController* immersive_controller =
+      browser_view()->immersive_mode_controller();
+  return immersive_controller->IsEnabled() &&
+      !immersive_controller->IsRevealed() &&
+      !immersive_controller->ShouldHideTabIndicators();
+}
+
 void BrowserNonClientFrameViewAsh::LayoutAvatar() {
   DCHECK(avatar_button());
   gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon();
 
-  if (frame()->IsFullscreen()) {
-    ImmersiveModeController* immersive_controller =
-        browser_view()->immersive_mode_controller();
-    // Hide the incognito icon when the top-of-window views are closed in
-    // immersive mode as the tab indicators are too short for the incognito
-    // icon to still be recongizable.
-    if (immersive_controller->IsEnabled() &&
-        !immersive_controller->IsRevealed()) {
-      avatar_button()->SetBoundsRect(gfx::Rect());
-      return;
-    }
-  }
-
   int avatar_bottom = GetTabStripInsets(false).top +
       browser_view()->GetTabStripHeight() - kAvatarBottomSpacing;
   int avatar_restored_y = avatar_bottom - incognito_icon.height();
   int avatar_y = (frame()->IsMaximized() || frame()->IsFullscreen()) ?
       NonClientTopBorderHeight(false) + kContentShadowHeight :
       avatar_restored_y;
+
+  // Hide the incognito icon in immersive fullscreen when the tab light bar is
+  // visible because the header is too short for the icognito icon to be
+  // recognizable.
+  bool avatar_visible = !UseImmersiveLightbarHeaderStyle();
+  int avatar_height = avatar_visible ? avatar_bottom - avatar_y : 0;
+
   gfx::Rect avatar_bounds(kAvatarSideSpacing,
                           avatar_y,
                           incognito_icon.width(),
-                          avatar_bottom - avatar_y);
+                          avatar_height);
   avatar_button()->SetBoundsRect(avatar_bounds);
+  avatar_button()->SetVisible(avatar_visible);
 }
 
 bool BrowserNonClientFrameViewAsh::ShouldPaint() const {
-  // Immersive mode windows are fullscreen, but need to paint during a reveal.
-  return !frame()->IsFullscreen() ||
-      browser_view()->immersive_mode_controller()->IsRevealed();
+  if (!frame()->IsFullscreen())
+    return true;
+
+  // There is nothing to paint for traditional (tab) fullscreen.
+  ImmersiveModeController* immersive_controller =
+      browser_view()->immersive_mode_controller();
+  if (!immersive_controller->IsEnabled())
+    return false;
+
+  // Need to paint during an immersive fullscreen reveal or when the immersive
+  // light bar is visible.
+  return immersive_controller->IsRevealed() ||
+      !immersive_controller->ShouldHideTabIndicators();
+}
+
+void BrowserNonClientFrameViewAsh::PaintImmersiveLightbarStyleHeader(
+    gfx::Canvas* canvas) {
+  // The light bar header is not themed because theming it does not look good.
+  gfx::ImageSkia* frame_image = GetThemeProvider()->GetImageSkiaNamed(
+      IDR_AURA_WINDOW_HEADER_BASE_MINIMAL);
+  canvas->TileImageInt(*frame_image, 0, 0, width(), frame_image->height());
 }
 
 void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas* canvas) {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
index 85d4d0a..5b914ee 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -69,7 +69,10 @@
 
  private:
   FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest, WindowHeader);
-  FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest, ImmersiveMode);
+  FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest,
+                           NonImmersiveFullscreen);
+  FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest,
+                           ImmersiveFullscreen);
 
   // Distance between top of window and client area.
   int NonClientTopBorderHeight(bool force_restored) const;
@@ -77,6 +80,11 @@
   // Returns true if we should use a short header, such as for popup windows.
   bool UseShortHeader() const;
 
+  // Returns true if we should use a super short header with light bars instead
+  // of regular tabs. This header is used in immersive fullscreen when the
+  // top-of-window views are not revealed.
+  bool UseImmersiveLightbarHeaderStyle() const;
+
   // Layout the incognito icon.
   void LayoutAvatar();
 
@@ -84,6 +92,10 @@
   // need their frames painted.
   bool ShouldPaint() const;
 
+  // Paints the header background when the frame is in immersive fullscreen and
+  // tab light bar is visible.
+  void PaintImmersiveLightbarStyleHeader(gfx::Canvas* canvas);
+
   void PaintToolbarBackground(gfx::Canvas* canvas);
 
   // Windows without a toolbar need to draw their own line under the header,
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 97ddb9e..9bf74a2 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -8,6 +8,9 @@
 #include "ash/ash_switches.h"
 #include "base/command_line.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
+#include "chrome/browser/ui/fullscreen/fullscreen_controller_test.h"
 #include "chrome/browser/ui/immersive_fullscreen_configuration.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
@@ -77,7 +80,54 @@
   EXPECT_FALSE(app_frame_view->UseShortHeader());
 }
 
-IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest, ImmersiveMode) {
+// Test that the frame view does not do any painting in non-immersive
+// fullscreen.
+IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest,
+                       NonImmersiveFullscreen) {
+  // We know we're using Views, so static cast.
+  BrowserView* browser_view = static_cast<BrowserView*>(browser()->window());
+  content::WebContents* web_contents = browser_view->GetActiveWebContents();
+  Widget* widget = browser_view->GetWidget();
+  // We know we're using Ash, so static cast.
+  BrowserNonClientFrameViewAsh* frame_view =
+      static_cast<BrowserNonClientFrameViewAsh*>(
+          widget->non_client_view()->frame_view());
+
+  // Frame paints by default.
+  EXPECT_TRUE(frame_view->ShouldPaint());
+
+  // No painting should occur in non-immersive fullscreen. (We enter into tab
+  // fullscreen here because tab fullscreen is non-immersive even when
+  // ImmersiveFullscreenConfiguration::UseImmersiveFullscreen()) returns
+  // true.
+  {
+    // NOTIFICATION_FULLSCREEN_CHANGED is sent asynchronously.
+    scoped_ptr<FullscreenNotificationObserver> waiter(
+        new FullscreenNotificationObserver());
+    browser()->fullscreen_controller()->ToggleFullscreenModeForTab(
+        web_contents, true);
+    waiter->Wait();
+  }
+  EXPECT_FALSE(browser_view->immersive_mode_controller()->IsEnabled());
+  EXPECT_FALSE(frame_view->ShouldPaint());
+
+  // Frame abuts top of window.
+  EXPECT_EQ(0, frame_view->NonClientTopBorderHeight(false));
+
+  // The frame should be painted again when fullscreen is exited and the caption
+  // buttons should be visible.
+  {
+    scoped_ptr<FullscreenNotificationObserver> waiter(
+        new FullscreenNotificationObserver());
+    chrome::ToggleFullscreenMode(browser());
+    waiter->Wait();
+  }
+  EXPECT_TRUE(frame_view->ShouldPaint());
+  EXPECT_TRUE(frame_view->size_button_->visible());
+  EXPECT_TRUE(frame_view->close_button_->visible());
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest, ImmersiveFullscreen) {
   if (!ImmersiveFullscreenConfiguration::UseImmersiveFullscreen())
     return;
 
@@ -93,6 +143,7 @@
       static_cast<ImmersiveModeControllerAsh*>(
           browser_view->immersive_mode_controller());
   immersive_mode_controller->DisableAnimationsForTest();
+  immersive_mode_controller->SetForceHideTabIndicatorsForTest(true);
 
   // Immersive mode starts disabled.
   ASSERT_FALSE(widget->IsFullscreen());
@@ -102,34 +153,56 @@
   EXPECT_TRUE(frame_view->ShouldPaint());
 
   // Going fullscreen enables immersive mode.
-  browser_view->EnterFullscreen(GURL(), FEB_TYPE_NONE);
+  chrome::ToggleFullscreenMode(browser());
   EXPECT_TRUE(immersive_mode_controller->IsEnabled());
 
-  // TODO(jamescook): When adding back the slide-out animation for immersive
-  // mode, this is a good place to test the button visibility.
+  // An immersive reveal shows the buttons and the top of the frame.
+  immersive_mode_controller->StartRevealForTest(true);
+  EXPECT_TRUE(immersive_mode_controller->IsRevealed());
+  EXPECT_TRUE(frame_view->ShouldPaint());
+  EXPECT_TRUE(frame_view->size_button_->visible());
+  EXPECT_TRUE(frame_view->close_button_->visible());
+  EXPECT_TRUE(frame_view->UseShortHeader());
+  EXPECT_FALSE(frame_view->UseImmersiveLightbarHeaderStyle());
+
+  // End the reveal. As the header does not paint a light bar when the
+  // top-of-window views are not revealed, nothing should be painted.
+  immersive_mode_controller->SetMouseHoveredForTest(false);
+  EXPECT_FALSE(immersive_mode_controller->IsRevealed());
+  EXPECT_FALSE(frame_view->ShouldPaint());
 
   // Frame abuts top of window.
   EXPECT_EQ(0, frame_view->NonClientTopBorderHeight(false));
 
-  // An immersive reveal shows the buttons and the top of the frame.
+  // Repeat test but with the tab light bar visible when the top-of-window views
+  // are not revealed.
+  immersive_mode_controller->SetForceHideTabIndicatorsForTest(false);
+
+  // Immersive reveal should have same behavior as before.
   immersive_mode_controller->StartRevealForTest(true);
+  EXPECT_TRUE(immersive_mode_controller->IsRevealed());
+  EXPECT_TRUE(frame_view->ShouldPaint());
   EXPECT_TRUE(frame_view->size_button_->visible());
   EXPECT_TRUE(frame_view->close_button_->visible());
-  EXPECT_TRUE(frame_view->ShouldPaint());
+  EXPECT_TRUE(frame_view->UseShortHeader());
+  EXPECT_FALSE(frame_view->UseImmersiveLightbarHeaderStyle());
 
-  // Ending reveal hides them again.
+  // Ending the reveal should hide the caption buttons and the header should
+  // be in the lightbar style.
   immersive_mode_controller->SetMouseHoveredForTest(false);
-  EXPECT_FALSE(immersive_mode_controller->IsRevealed());
+  EXPECT_TRUE(frame_view->ShouldPaint());
   EXPECT_FALSE(frame_view->size_button_->visible());
   EXPECT_FALSE(frame_view->close_button_->visible());
-  EXPECT_FALSE(frame_view->ShouldPaint());
+  EXPECT_TRUE(frame_view->UseShortHeader());
+  EXPECT_TRUE(frame_view->UseImmersiveLightbarHeaderStyle());
 
   // Exiting fullscreen exits immersive mode.
   browser_view->ExitFullscreen();
   EXPECT_FALSE(immersive_mode_controller->IsEnabled());
 
   // Exiting immersive mode makes controls and frame visible again.
+  EXPECT_TRUE(frame_view->ShouldPaint());
   EXPECT_TRUE(frame_view->size_button_->visible());
   EXPECT_TRUE(frame_view->close_button_->visible());
-  EXPECT_TRUE(frame_view->ShouldPaint());
+  EXPECT_FALSE(frame_view->UseImmersiveLightbarHeaderStyle());
 }
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 5093269..3249868 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -9,6 +9,7 @@
 #include "base/auto_reset.h"
 #include "base/command_line.h"
 #include "base/i18n/rtl.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/metrics/histogram.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/string_number_conversions.h"
@@ -71,6 +72,7 @@
 #include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "chrome/browser/ui/views/fullscreen_exit_bubble_views.h"
 #include "chrome/browser/ui/views/infobars/infobar_container_view.h"
+#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/browser/ui/views/location_bar/location_icon_view.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_views.h"
@@ -112,6 +114,7 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/sys_color_change_listener.h"
+#include "ui/views/controls/button/menu_button.h"
 #include "ui/views/controls/single_split_view.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/controls/webview/webview.h"
@@ -149,6 +152,8 @@
 #endif
 
 #if defined(ENABLE_ONE_CLICK_SIGNIN)
+#include "chrome/browser/ui/sync/one_click_signin_bubble_delegate.h"
+#include "chrome/browser/ui/sync/one_click_signin_bubble_links_delegate.h"
 #include "chrome/browser/ui/views/sync/one_click_signin_bubble_view.h"
 #endif
 
@@ -410,6 +415,9 @@
 #endif
       force_location_bar_focus_(false),
       immersive_mode_controller_(chrome::CreateImmersiveModeController()),
+#if defined(OS_CHROMEOS)
+      scroll_end_effect_controller_(ScrollEndEffectController::Create()),
+#endif
       color_change_listener_(this),
       activate_modal_dialog_factory_(this) {
 }
@@ -1099,8 +1107,18 @@
     const string16& email,
     const string16& error_message,
     const StartSyncCallback& start_sync_callback) {
+  scoped_ptr<OneClickSigninBubbleDelegate> delegate;
+  delegate.reset(new OneClickSigninBubbleLinksDelegate(browser()));
+
+  views::View* anchor_view;
+  if (type == BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE)
+    anchor_view = toolbar_->app_menu();
+  else
+    anchor_view = toolbar_->location_bar();
+
   OneClickSigninBubbleView::ShowBubble(type, email, error_message,
-                                       toolbar_, start_sync_callback);
+                                       delegate.Pass(), anchor_view,
+                                       start_sync_callback);
 }
 #endif
 
@@ -1144,7 +1162,8 @@
 void BrowserView::ShowCreateChromeAppShortcutsDialog(
     Profile* profile,
     const extensions::Extension* app) {
-  chrome::ShowCreateChromeAppShortcutsDialog(GetNativeWindow(), profile, app);
+  chrome::ShowCreateChromeAppShortcutsDialog(
+      GetNativeWindow(), profile, app, base::Closure());
 }
 
 void BrowserView::UserChangedTheme() {
@@ -2576,6 +2595,8 @@
 }
 
 void BrowserView::OverscrollUpdate(int delta_y) {
+  if (scroll_end_effect_controller_)
+    scroll_end_effect_controller_->OverscrollUpdate(delta_y);
 }
 
 void BrowserView::DoCutCopyPaste(void (content::RenderWidgetHost::*method)(),
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 798d564..00dd37b 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -21,6 +21,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/views/frame/browser_frame.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
+#include "chrome/browser/ui/views/frame/scroll_end_effect_controller.h"
 #include "chrome/browser/ui/views/load_complete_listener.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/models/simple_menu_model.h"
@@ -757,6 +758,8 @@
 
   scoped_ptr<ImmersiveModeController> immersive_mode_controller_;
 
+  scoped_ptr<ScrollEndEffectController> scroll_end_effect_controller_;
+
   gfx::ScopedSysColorChangeListener color_change_listener_;
 
   mutable base::WeakPtrFactory<BrowserView> activate_modal_dialog_factory_;
diff --git a/chrome/browser/ui/views/frame/scroll_end_effect_controller.h b/chrome/browser/ui/views/frame/scroll_end_effect_controller.h
new file mode 100644
index 0000000..372ec19
--- /dev/null
+++ b/chrome/browser/ui/views/frame/scroll_end_effect_controller.h
@@ -0,0 +1,30 @@
+ // Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_SCROLL_END_EFFECT_CONTROLLER_H_
+#define CHROME_BROWSER_UI_VIEWS_FRAME_SCROLL_END_EFFECT_CONTROLLER_H_
+
+#include "base/basictypes.h"
+
+// The controller receives the raw y-deltas generated by the overscroll
+// controller and is resonsible for converting them in to the scroll end
+// effect. This effect occurs in the vertical overscroll case and is used to
+// visually indicate to the user that they have reached the end of the content
+// that they are scrolling.
+class ScrollEndEffectController {
+ public:
+  ScrollEndEffectController() {}
+  virtual ~ScrollEndEffectController() {}
+
+  static ScrollEndEffectController* Create();
+
+  // Interface that allows vertical overscroll activies to be communicated to
+  // the controller.
+  virtual void OverscrollUpdate(int delta_y) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScrollEndEffectController);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_SCROLL_END_EFFECT_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.cc b/chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.cc
new file mode 100644
index 0000000..aa06428
--- /dev/null
+++ b/chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.cc
@@ -0,0 +1,19 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.h"
+
+ScrollEndEffectController* ScrollEndEffectController::Create() {
+  return new ScrollEndEffectControllerAsh();
+}
+
+ScrollEndEffectControllerAsh::ScrollEndEffectControllerAsh() {
+}
+
+ScrollEndEffectControllerAsh::~ScrollEndEffectControllerAsh() {
+}
+
+void ScrollEndEffectControllerAsh::OverscrollUpdate(int delta_y) {
+  // TODO(rharrison): Implement initial version of scroll end effect
+}
diff --git a/chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.h b/chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.h
new file mode 100644
index 0000000..9c14e7f
--- /dev/null
+++ b/chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.h
@@ -0,0 +1,23 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_SCROLL_END_EFFECT_CONTROLLER_ASH_H_
+#define CHROME_BROWSER_UI_VIEWS_FRAME_SCROLL_END_EFFECT_CONTROLLER_ASH_H_
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/ui/views/frame/scroll_end_effect_controller.h"
+
+class ScrollEndEffectControllerAsh : public ScrollEndEffectController {
+ public:
+  ScrollEndEffectControllerAsh();
+  virtual ~ScrollEndEffectControllerAsh();
+
+  // ScrollEndEffectController overides:
+  virtual void OverscrollUpdate(int delta_y) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScrollEndEffectControllerAsh);
+ };
+
+#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_SCROLL_END_EFFECT_CONTROLLER_ASH_H_
diff --git a/chrome/browser/ui/views/javascript_app_modal_dialog_views.cc b/chrome/browser/ui/views/javascript_app_modal_dialog_views.cc
index ed4c6a2..ef485a1 100644
--- a/chrome/browser/ui/views/javascript_app_modal_dialog_views.cc
+++ b/chrome/browser/ui/views/javascript_app_modal_dialog_views.cc
@@ -108,7 +108,7 @@
   return true;
 }
 
-void JavaScriptAppModalDialogViews::OnClose() {
+void JavaScriptAppModalDialogViews::OnClosed() {
   parent_->OnClose();
 }
 
diff --git a/chrome/browser/ui/views/javascript_app_modal_dialog_views.h b/chrome/browser/ui/views/javascript_app_modal_dialog_views.h
index 20b3186..bae46d1 100644
--- a/chrome/browser/ui/views/javascript_app_modal_dialog_views.h
+++ b/chrome/browser/ui/views/javascript_app_modal_dialog_views.h
@@ -43,7 +43,7 @@
   virtual ui::ModalType GetModalType() const OVERRIDE;
   virtual views::View* GetContentsView() OVERRIDE;
   virtual views::View* GetInitiallyFocusedView() OVERRIDE;
-  virtual void OnClose() OVERRIDE;
+  virtual void OnClosed() OVERRIDE;
   virtual views::Widget* GetWidget() OVERRIDE;
   virtual const views::Widget* GetWidget() const OVERRIDE;
 
diff --git a/chrome/browser/ui/views/location_bar/ev_bubble_view.cc b/chrome/browser/ui/views/location_bar/ev_bubble_view.cc
index 96c99cd..5ee505c 100644
--- a/chrome/browser/ui/views/location_bar/ev_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/ev_bubble_view.cc
@@ -17,9 +17,9 @@
                            SkColor text_color,
                            SkColor parent_background_color,
                            LocationBarView* location_bar)
-    : IconLabelBubbleView(kBackgroundImages, IDR_OMNIBOX_HTTPS_VALID, font,
-                          font_y_offset, text_color, parent_background_color,
-                          true),
+    : IconLabelBubbleView(kBackgroundImages, NULL, IDR_OMNIBOX_HTTPS_VALID,
+                          font, font_y_offset, text_color,
+                          parent_background_color, true),
       page_info_helper_(this, location_bar) {
 }
 
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index 4d5e3ba..b74463b 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -16,6 +16,7 @@
 
 
 IconLabelBubbleView::IconLabelBubbleView(const int background_images[],
+                                         const int hover_background_images[],
                                          int contained_image,
                                          const gfx::Font& font,
                                          int font_y_offset,
@@ -26,12 +27,22 @@
           views::Painter::CreateImageGridPainter(background_images)),
       image_(new views::ImageView()),
       label_(new views::Label()),
-      is_extension_icon_(false) {
+      is_extension_icon_(false),
+      in_hover_(false) {
   image_->SetImage(
       ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
           contained_image));
+
+  // Disable separate hit testing for |image_|.  This prevents views treating
+  // |image_| as a separate mouse hover region from |this|.
+  image_->set_interactive(false);
   AddChildView(image_);
 
+  if (hover_background_images) {
+    hover_background_painter_.reset(
+        views::Painter::CreateImageGridPainter(hover_background_images));
+  }
+
   label_->set_border(views::Border::CreateEmptyBorder(font_y_offset, 0, 0, 0));
   label_->SetFont(font);
   label_->SetEnabledColor(text_color);
@@ -93,6 +104,18 @@
   return size;
 }
 
+void IconLabelBubbleView::OnMouseEntered(const ui::MouseEvent& event) {
+  in_hover_ = true;
+  if (hover_background_painter_.get())
+    SchedulePaint();
+}
+
+void IconLabelBubbleView::OnMouseExited(const ui::MouseEvent& event) {
+  in_hover_ = false;
+  if (hover_background_painter_.get())
+    SchedulePaint();
+}
+
 // static
 int IconLabelBubbleView::GetBubbleOuterPadding(bool by_icon) {
   return LocationBarView::GetItemPadding() - LocationBarView::kBubblePadding +
@@ -100,7 +123,9 @@
 }
 
 void IconLabelBubbleView::OnPaint(gfx::Canvas* canvas) {
-  background_painter_->Paint(canvas, size());
+  views::Painter* painter = (in_hover_ && hover_background_painter_) ?
+      hover_background_painter_.get() : background_painter_.get();
+  painter->Paint(canvas, size());
 }
 
 int IconLabelBubbleView::GetPreLabelWidth() const {
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
index 7c30e91..fe1efc7 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
@@ -29,7 +29,10 @@
 class IconLabelBubbleView : public views::View {
  public:
   // The label will be positioned |font_y_offset| px. from the top of the view.
+  // |hover_background_images| is an optional set of images to be used in place
+  // of |background_images| during mouse hover.
   IconLabelBubbleView(const int background_images[],
+                      const int hover_background_images[],
                       int contained_image,
                       const gfx::Font& font,
                       int font_y_offset,
@@ -48,6 +51,8 @@
   // views::View:
   virtual gfx::Size GetPreferredSize() OVERRIDE;
   virtual void Layout() OVERRIDE;
+  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
 
   gfx::Size GetSizeForLabelWidth(int width) const;
 
@@ -66,12 +71,14 @@
 
   // For painting the background.
   scoped_ptr<views::Painter> background_painter_;
+  scoped_ptr<views::Painter> hover_background_painter_;
 
   // The contents of the bubble.
   views::ImageView* image_;
   views::Label* label_;
 
   bool is_extension_icon_;
+  bool in_hover_;
 
   DISALLOW_COPY_AND_ASSIGN(IconLabelBubbleView);
 };
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 352f878..72a84ce 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -114,11 +114,12 @@
   return contents ? chrome::FindBrowserWithWebContents(contents) : NULL;
 }
 
-// Given a containing |height| and a base |font|, shrinks the font until it will
-// fit within |height| while having its cap height vertically centered.  Returns
-// the |font_y_offset| needed to produce this centering.
+// Given a containing |height| and a base |font_list|, shrinks the fonts until
+// the primary font will fit within |height| while having its cap height
+// vertically centered.  Returns the |font_y_offset| needed to produce this
+// centering.
 void CalculateFontAndOffsetForHeight(int height,
-                                     gfx::Font* font,
+                                     gfx::FontList* font_list,
                                      int* font_y_offset) {
 #if defined(OS_WIN)
   base::win::ScopedGetDC screen_dc(NULL);
@@ -128,21 +129,23 @@
     // TODO(pkasting): Expand the gfx::Font metrics (and underlying Skia
     // metrics) enough to expose the cap height directly.
 #if defined(OS_WIN)
-    base::win::ScopedSelectObject font_in_dc(screen_dc, font->GetNativeFont());
+    const gfx::Font& font = font_list->GetPrimaryFont();
+    base::win::ScopedSelectObject font_in_dc(screen_dc, font.GetNativeFont());
     TEXTMETRIC tm = {0};
     GetTextMetrics(screen_dc, &tm);
-    int cap_height = font->GetBaseline() - tm.tmInternalLeading;
+    int cap_height = font.GetBaseline() - tm.tmInternalLeading;
     *font_y_offset = ((height - cap_height) / 2) - tm.tmInternalLeading;
 #else
     // Without cap height available, we fall back to centering the full height.
-    *font_y_offset = (height - font->GetHeight()) / 2;
+    *font_y_offset = (height - font_list->GetHeight()) / 2;
 #endif
 
+    const int font_size = font_list->GetFontSize();
     if (((*font_y_offset >= 0) &&
-         ((*font_y_offset + font->GetHeight()) <= height)) ||
-        (font->GetFontSize() <= 1))
+         ((*font_y_offset + font_list->GetHeight()) <= height)) ||
+        (font_size <= 1))
       return;
-    *font = font->DeriveFont(-1);
+    *font_list = font_list->DeriveFontListWithSize(font_size - 1);
   }
 }
 
@@ -234,28 +237,30 @@
   AddChildView(location_icon_view_);
 
   // Determine the main font.
-  gfx::Font font(ui::ResourceBundle::GetSharedInstance().GetFont(
-      ui::ResourceBundle::BaseFont));
-  const int current_font_size = font.GetFontSize();
+  gfx::FontList font_list = ResourceBundle::GetSharedInstance().GetFontList(
+      ResourceBundle::BaseFont);
+  const int current_font_size = font_list.GetFontSize();
   const int desired_font_size = browser_defaults::kOmniboxFontPixelSize;
   if (current_font_size < desired_font_size)
-    font = font.DeriveFont(desired_font_size - current_font_size);
+    font_list = font_list.DeriveFontListWithSize(desired_font_size);
   // Shrink large fonts to make them fit.
   // TODO(pkasting): Stretch the location bar instead in this case.
   int location_height = GetInternalHeight(true);
   int font_y_offset;
-  CalculateFontAndOffsetForHeight(location_height, &font, &font_y_offset);
+  CalculateFontAndOffsetForHeight(location_height, &font_list, &font_y_offset);
+  const gfx::Font& font = font_list.GetPrimaryFont();
 
   // Determine the font for use inside the bubbles.
-  gfx::Font bubble_font(font);
+  gfx::FontList bubble_font_list(font_list);
   int bubble_font_y_offset;
   // The bubble background images have 1 px thick edges, which we don't want to
   // overlap.
   const int kBubbleInteriorVerticalPadding = 1;
   CalculateFontAndOffsetForHeight(
       location_height - ((kBubblePadding + kBubbleInteriorVerticalPadding) * 2),
-      &bubble_font, &bubble_font_y_offset);
+      &bubble_font_list, &bubble_font_y_offset);
   bubble_font_y_offset += kBubbleInteriorVerticalPadding;
+  const gfx::Font& bubble_font = font_list.GetPrimaryFont();
 
   const SkColor background_color =
       GetColor(ToolbarModel::NONE, LocationBarView::BACKGROUND);
@@ -267,7 +272,7 @@
 
   // Initialize the Omnibox view.
   location_entry_.reset(CreateOmniboxView(this, model_, profile_,
-      command_updater_, is_popup_mode_, this, font, font_y_offset));
+      command_updater_, is_popup_mode_, this, font_list, font_y_offset));
   SetLocationEntryFocusable(true);
   location_entry_view_ = location_entry_->AddToView(this);
 
diff --git a/chrome/browser/ui/views/location_bar/page_action_image_view.cc b/chrome/browser/ui/views/location_bar/page_action_image_view.cc
index ddb69b9..cfa3345 100644
--- a/chrome/browser/ui/views/location_bar/page_action_image_view.cc
+++ b/chrome/browser/ui/views/location_bar/page_action_image_view.cc
@@ -135,8 +135,9 @@
       break;
 
     case LocationBarController::ACTION_SHOW_SCRIPT_POPUP:
-      ShowPopupWithURL(ExtensionInfoUI::GetURL(page_action_->extension_id()),
-                       show_action);
+      ShowPopupWithURL(
+          extensions::ExtensionInfoUI::GetURL(page_action_->extension_id()),
+          show_action);
       break;
   }
 }
diff --git a/chrome/browser/ui/views/location_bar/selected_keyword_view.cc b/chrome/browser/ui/views/location_bar/selected_keyword_view.cc
index c7cccd7..ab89b19 100644
--- a/chrome/browser/ui/views/location_bar/selected_keyword_view.cc
+++ b/chrome/browser/ui/views/location_bar/selected_keyword_view.cc
@@ -27,9 +27,9 @@
                                          SkColor text_color,
                                          SkColor parent_background_color,
                                          Profile* profile)
-    : IconLabelBubbleView(kBackgroundImages, IDR_KEYWORD_SEARCH_MAGNIFIER, font,
-                          font_y_offset, text_color, parent_background_color,
-                          false),
+    : IconLabelBubbleView(kBackgroundImages, NULL, IDR_KEYWORD_SEARCH_MAGNIFIER,
+                          font, font_y_offset, text_color,
+                          parent_background_color, false),
       profile_(profile) {
   full_label_.SetFont(font);
   full_label_.SetVisible(false);
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
index d075227..ef4ee23 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
@@ -109,7 +109,7 @@
 
   // The zoom bubble should not be anchored when it is shown in immersive
   // fullscreen and the top-of-window views are not revealed.
-  ZoomBubbleView::ShowBubble(web_contents, false);
+  ZoomBubbleView::ShowBubble(web_contents, true);
   ASSERT_TRUE(ZoomBubbleView::IsShowing());
   const ZoomBubbleView* zoom_bubble = ZoomBubbleView::GetZoomBubbleForTest();
   EXPECT_FALSE(zoom_bubble->anchor_view());
diff --git a/chrome/browser/ui/views/message_center/message_center_widget_delegate.cc b/chrome/browser/ui/views/message_center/message_center_widget_delegate.cc
index d308601..32d31a8 100644
--- a/chrome/browser/ui/views/message_center/message_center_widget_delegate.cc
+++ b/chrome/browser/ui/views/message_center/message_center_widget_delegate.cc
@@ -115,8 +115,8 @@
 
 int MessageCenterWidgetDelegate::GetHeightForWidth(int width) {
   int height = MessageCenterView::GetHeightForWidth(width);
-  return (pos_info_.max_height != 0) ? std::min(height, pos_info_.max_height)
-                                     : height;
+  return (pos_info_.max_height != 0) ?
+    std::min(height, pos_info_.max_height - border_insets_.height()) : height;
 }
 
 bool MessageCenterWidgetDelegate::AcceleratorPressed(
diff --git a/chrome/browser/ui/views/message_center/web_notification_tray.cc b/chrome/browser/ui/views/message_center/web_notification_tray.cc
index 5f25c9d..2d2058a 100644
--- a/chrome/browser/ui/views/message_center/web_notification_tray.cc
+++ b/chrome/browser/ui/views/message_center/web_notification_tray.cc
@@ -35,13 +35,23 @@
 const int kSystemTrayHeight = 16;
 const int kNumberOfSystemTraySprites = 10;
 
-gfx::ImageSkia GetIcon(int unread_count) {
-  bool has_unread = unread_count > 0;
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  if (!has_unread)
-    return *rb.GetImageSkiaNamed(IDR_NOTIFICATION_TRAY_EMPTY);
+// Number of pixels the message center is offset from the mouse.
+const int kMouseOffset = 5;
 
-  return *rb.GetImageSkiaNamed(IDR_NOTIFICATION_TRAY_ATTENTION);
+gfx::ImageSkia* GetIcon(int unread_count, bool is_quiet_mode) {
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  int resource_id = IDR_NOTIFICATION_TRAY_EMPTY;
+
+  if (unread_count) {
+    if (is_quiet_mode)
+      resource_id = IDR_NOTIFICATION_TRAY_DO_NOT_DISTURB_ATTENTION;
+    else
+      resource_id = IDR_NOTIFICATION_TRAY_ATTENTION;
+  } else if (is_quiet_mode) {
+    resource_id = IDR_NOTIFICATION_TRAY_DO_NOT_DISTURB_EMPTY;
+  }
+
+  return rb.GetImageSkiaNamed(resource_id);
 }
 
 }  // namespace
@@ -121,7 +131,7 @@
       should_update_tray_content_(true) {
   message_center_tray_.reset(
       new MessageCenterTray(this, g_browser_process->message_center()));
-  UpdateStatusIcon();
+  OnMessageCenterTrayChanged();
 }
 
 WebNotificationTray::~WebNotificationTray() {
@@ -199,28 +209,28 @@
     return;
   should_update_tray_content_ = false;
 
-  int total_notifications = message_center()->NotificationCount();
-  if (total_notifications == 0) {
-    DestroyStatusIcon();
-    return;
-  }
-
   int unread_notifications = message_center()->UnreadNotificationCount();
-  StatusIcon* status_icon = GetStatusIcon();
-  if (!status_icon)
-    return;
 
-  status_icon->SetImage(GetIcon(unread_notifications));
-
-  string16 product_name(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
+  string16 tool_tip;
   if (unread_notifications > 0) {
     string16 str_unread_count = base::FormatNumber(unread_notifications);
-    status_icon->SetToolTip(l10n_util::GetStringFUTF16(
-        IDS_MESSAGE_CENTER_TOOLTIP_UNREAD, product_name, str_unread_count));
+    tool_tip = l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_TOOLTIP_UNREAD,
+                                          str_unread_count);
   } else {
-    status_icon->SetToolTip(
-        l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_TOOLTIP, product_name));
+    tool_tip = l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_TOOLTIP);
   }
+
+  gfx::ImageSkia* icon_image = GetIcon(
+      unread_notifications,
+      message_center()->IsQuietMode());
+
+  if (status_icon_) {
+    status_icon_->SetImage(*icon_image);
+    status_icon_->SetToolTip(tool_tip);
+    return;
+  }
+
+  CreateStatusIcon(*icon_image, tool_tip);
 }
 
 void WebNotificationTray::SendHideMessageCenter() {
@@ -261,36 +271,40 @@
   pos_info.max_height = work_area.height();
 
   if (work_area.Contains(mouse_click_point_)) {
-    pos_info.max_height -= std::abs(mouse_click_point_.y() - corner.y());
-
     // Message center is in the work area. So position it few pixels above the
     // mouse click point if alignemnt is towards bottom and few pixels below if
     // alignment is towards top.
-    pos_info.inital_anchor_point
-        .set_y(mouse_click_point_.y() +
-               (pos_info.message_center_alignment & ALIGNMENT_BOTTOM ? -5 : 5));
+    pos_info.inital_anchor_point.set_y(
+        mouse_click_point_.y() +
+        (pos_info.message_center_alignment & ALIGNMENT_BOTTOM ? -kMouseOffset
+                                                              : kMouseOffset));
+
+    // Subtract the distance between mouse click point and the closest
+    // (insetted) edge from the max height to show the message center within the
+    // (insetted) work area bounds. Also subtract the offset from the mouse
+    // click point we added earlier.
+    pos_info.max_height -=
+        std::abs(mouse_click_point_.y() - corner.y()) + kMouseOffset;
   }
   return pos_info;
 }
 
-StatusIcon* WebNotificationTray::GetStatusIcon() {
+void WebNotificationTray::CreateStatusIcon(const gfx::ImageSkia& image,
+                                           const string16& tool_tip) {
   if (status_icon_)
-    return status_icon_;
+    return;
 
   StatusTray* status_tray = g_browser_process->status_tray();
   if (!status_tray)
-    return NULL;
+    return;
 
-  StatusIcon* status_icon =
-      status_tray->CreateStatusIcon(StatusTray::NOTIFICATION_TRAY_ICON);
-  if (!status_icon)
-    return NULL;
+  status_icon_ = status_tray->CreateStatusIcon(
+      StatusTray::NOTIFICATION_TRAY_ICON, image, tool_tip);
+  if (!status_icon_)
+    return;
 
-  status_icon_ = status_icon;
   status_icon_->AddObserver(this);
   AddQuietModeMenu(status_icon_);
-
-  return status_icon_;
 }
 
 void WebNotificationTray::DestroyStatusIcon() {
diff --git a/chrome/browser/ui/views/message_center/web_notification_tray.h b/chrome/browser/ui/views/message_center/web_notification_tray.h
index 8d4010d..1bc6fa4 100644
--- a/chrome/browser/ui/views/message_center/web_notification_tray.h
+++ b/chrome/browser/ui/views/message_center/web_notification_tray.h
@@ -84,7 +84,7 @@
 
   PositionInfo GetPositionInfo();
 
-  StatusIcon* GetStatusIcon();
+  void CreateStatusIcon(const gfx::ImageSkia& image, const string16& tool_tip);
   void DestroyStatusIcon();
   void AddQuietModeMenu(StatusIcon* status_icon);
   MessageCenterWidgetDelegate* GetMessageCenterWidgetDelegateForTest();
diff --git a/chrome/browser/ui/views/message_center/web_notification_tray_win.cc b/chrome/browser/ui/views/message_center/web_notification_tray_win.cc
index dcc6e25..54a99f4 100644
--- a/chrome/browser/ui/views/message_center/web_notification_tray_win.cc
+++ b/chrome/browser/ui/views/message_center/web_notification_tray_win.cc
@@ -30,9 +30,9 @@
 }
 
 void WebNotificationTray::DisplayFirstRunBalloon() {
-  StatusIcon* status_icon = GetStatusIcon();
-  if (!status_icon)
-    return;
+  // We should never be calling DisplayFirstRunBalloon without a status icon.
+  // The status icon must have been created before this call.
+  DCHECK(status_icon_);
 
   base::win::Version win_version = base::win::GetVersion();
   if (win_version == base::win::VERSION_PRE_XP)
@@ -52,8 +52,7 @@
   scoped_ptr<SkBitmap> sized_app_icon_bitmap = GetAppIconForSize(icon_size);
   gfx::ImageSkia sized_app_icon_skia =
       gfx::ImageSkia::CreateFrom1xBitmap(*sized_app_icon_bitmap);
-  string16 product_name(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
-  GetStatusIcon()->DisplayBalloon(
+  status_icon_->DisplayBalloon(
       sized_app_icon_skia,
       l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_BALLOON_TITLE),
       l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_BALLOON_TEXT));
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index ec579dc..c5dfb4b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -53,7 +53,7 @@
 // OmniboxPopupContentsView, public:
 
 OmniboxPopupView* OmniboxPopupContentsView::Create(
-    const gfx::Font& font,
+    const gfx::FontList& font_list,
     OmniboxView* omnibox_view,
     OmniboxEditModel* edit_model,
     LocationBarView* location_bar_view) {
@@ -65,10 +65,10 @@
   OmniboxPopupContentsView* view = NULL;
   if (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) {
     view = new TouchOmniboxPopupContentsView(
-        font, omnibox_view, edit_model, location_bar_view);
+        font_list, omnibox_view, edit_model, location_bar_view);
   } else {
     view = new OmniboxPopupContentsView(
-        font, omnibox_view, edit_model, location_bar_view);
+        font_list, omnibox_view, edit_model, location_bar_view);
   }
 
   view->Init();
@@ -76,14 +76,14 @@
 }
 
 OmniboxPopupContentsView::OmniboxPopupContentsView(
-    const gfx::Font& font,
+    const gfx::FontList& font_list,
     OmniboxView* omnibox_view,
     OmniboxEditModel* edit_model,
     LocationBarView* location_bar_view)
     : model_(new OmniboxPopupModel(this, edit_model)),
       omnibox_view_(omnibox_view),
       location_bar_view_(location_bar_view),
-      font_(font),
+      font_list_(font_list),
       ignore_mouse_drag_(false),
       size_animation_(this),
       left_margin_(0),
@@ -101,7 +101,7 @@
   // necessarily our final class yet, and we may have subclasses
   // overriding CreateResultView.
   for (size_t i = 0; i < AutocompleteResult::kMaxMatches; ++i) {
-    OmniboxResultView* result_view = CreateResultView(this, i, font_);
+    OmniboxResultView* result_view = CreateResultView(this, i, font_list_);
     result_view->SetVisible(false);
     AddChildViewAt(result_view, static_cast<int>(i));
   }
@@ -417,8 +417,9 @@
 OmniboxResultView* OmniboxPopupContentsView::CreateResultView(
     OmniboxResultViewModel* model,
     int model_index,
-    const gfx::Font& font) {
-  return new OmniboxResultView(model, model_index, location_bar_view_, font);
+    const gfx::FontList& font_list) {
+  return new OmniboxResultView(model, model_index, location_bar_view_,
+                               font_list);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
index 7b670f4..978008e 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
@@ -12,7 +12,7 @@
 #include "ui/base/animation/animation_delegate.h"
 #include "ui/base/animation/slide_animation.h"
 #include "ui/base/window_open_disposition.h"
-#include "ui/gfx/font.h"
+#include "ui/gfx/font_list.h"
 #include "ui/views/view.h"
 
 struct AutocompleteMatch;
@@ -29,7 +29,7 @@
                                  public ui::AnimationDelegate {
  public:
   // Factory method for creating the AutocompletePopupView.
-  static OmniboxPopupView* Create(const gfx::Font& font,
+  static OmniboxPopupView* Create(const gfx::FontList& font_list,
                                   OmniboxView* omnibox_view,
                                   OmniboxEditModel* edit_model,
                                   LocationBarView* location_bar_view);
@@ -74,7 +74,7 @@
   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
 
  protected:
-  OmniboxPopupContentsView(const gfx::Font& font,
+  OmniboxPopupContentsView(const gfx::FontList& font_list,
                            OmniboxView* omnibox_view,
                            OmniboxEditModel* edit_model,
                            LocationBarView* location_bar_view);
@@ -88,7 +88,7 @@
   virtual int CalculatePopupHeight();
   virtual OmniboxResultView* CreateResultView(OmniboxResultViewModel* model,
                                               int model_index,
-                                              const gfx::Font& font);
+                                              const gfx::FontList& font_list);
 
   // Overridden from views::View:
   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
@@ -144,8 +144,8 @@
 
   LocationBarView* location_bar_view_;
 
-  // The font used for result rows, based on the omnibox font.
-  gfx::Font font_;
+  // The font list used for result rows, based on the omnibox font list.
+  gfx::FontList font_list_;
 
   // If the user cancels a dragging action (i.e. by pressing ESC), we don't have
   // a convenient way to release mouse capture. Instead we use this flag to
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index 1e722aa..fe2225f 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -107,17 +107,18 @@
     OmniboxResultViewModel* model,
     int model_index,
     LocationBarView* location_bar_view,
-    const gfx::Font& font)
+    const gfx::FontList& font_list)
     : edge_item_padding_(LocationBarView::GetItemPadding()),
       item_padding_(LocationBarView::GetItemPadding()),
       minimum_text_vertical_padding_(kMinimumTextVerticalPadding),
       model_(model),
       model_index_(model_index),
       location_bar_view_(location_bar_view),
-      font_(font),
-      font_height_(std::max(font.GetHeight(),
-                            font.DeriveFont(0, gfx::BOLD).GetHeight())),
-      ellipsis_width_(font.GetStringWidth(string16(kEllipsis))),
+      font_list_(font_list),
+      font_height_(std::max(font_list.GetHeight(),
+                            font_list.DeriveFontList(gfx::BOLD).GetHeight())),
+      ellipsis_width_(font_list.GetPrimaryFont().GetStringWidth(
+          string16(kEllipsis))),
       mirroring_context_(new MirroringContext()),
       keyword_icon_(new views::ImageView()),
       animation_(new ui::SlideAnimation(this)) {
@@ -403,7 +404,7 @@
       gfx::RenderText* render_text = render_texts.back();
       current_run->classifications.push_back(render_text);
       render_text->SetText(text.substr(text_start, text_end - text_start));
-      render_text->SetFont(font_);
+      render_text->SetFontList(font_list_);
 
       // Calculate style-related data.
       if (classifications[i].style & ACMatchClassification::MATCH)
@@ -471,7 +472,7 @@
       // Align the text runs to a common baseline.
       const gfx::Rect rect(
           mirroring_context_->mirrored_left_coord(x, x + size.width()),
-          y + font_.GetBaseline() - (*j)->GetBaseline(),
+          y + font_list_.GetBaseline() - (*j)->GetBaseline(),
           size.width(), size.height());
       (*j)->SetDisplayRect(rect);
       (*j)->Draw(canvas);
@@ -513,7 +514,8 @@
 
       // Can we fit at least an ellipsis?
       gfx::Font font((*j)->GetStyle(gfx::BOLD) ?
-          (*j)->GetFont().DeriveFont(0, gfx::Font::BOLD) : (*j)->GetFont());
+          (*j)->GetPrimaryFont().DeriveFont(0, gfx::Font::BOLD) :
+          (*j)->GetPrimaryFont());
       string16 elided_text(
           ui::ElideText((*j)->text(), font, remaining_width, ui::ELIDE_AT_END));
       Classifications::reverse_iterator prior(j + 1);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.h b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
index d426eab..007407b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
@@ -11,7 +11,7 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/animation/animation_delegate.h"
 #include "ui/base/animation/slide_animation.h"
-#include "ui/gfx/font.h"
+#include "ui/gfx/font_list.h"
 #include "ui/gfx/rect.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/view.h"
@@ -48,7 +48,7 @@
   OmniboxResultView(OmniboxResultViewModel* model,
                     int model_index,
                     LocationBarView* location_bar_view,
-                    const gfx::Font& font);
+                    const gfx::FontList& font_list);
   virtual ~OmniboxResultView();
 
   SkColor GetColor(ResultViewState state, ColorKind kind) const;
@@ -148,7 +148,7 @@
 
   LocationBarView* location_bar_view_;
 
-  const gfx::Font font_;
+  const gfx::FontList font_list_;
   int font_height_;
 
   // Width of the ellipsis in the normal font.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 1800210..4073001 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -39,7 +39,7 @@
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/canvas.h"
-#include "ui/gfx/font.h"
+#include "ui/gfx/font_list.h"
 #include "ui/gfx/selection_model.h"
 #include "ui/views/border.h"
 #include "ui/views/button_drag_utils.h"
@@ -104,7 +104,7 @@
                                    CommandUpdater* command_updater,
                                    bool popup_window_mode,
                                    LocationBarView* location_bar,
-                                   const gfx::Font& font,
+                                   const gfx::FontList& font_list,
                                    int font_y_offset)
     : OmniboxView(profile, controller, toolbar_model, command_updater),
       popup_window_mode_(popup_window_mode),
@@ -117,7 +117,7 @@
       select_all_on_gesture_tap_(false) {
   RemoveBorder();
   set_id(VIEW_ID_OMNIBOX);
-  SetFont(font);
+  SetFontList(font_list);
   SetVerticalMargins(font_y_offset, 0);
   SetVerticalAlignment(gfx::ALIGN_TOP);
 }
@@ -147,7 +147,7 @@
 
   // Initialize the popup view using the same font.
   popup_view_.reset(OmniboxPopupContentsView::Create(
-      font(), this, model(), location_bar_view_));
+      font_list(), this, model(), location_bar_view_));
 
 #if defined(OS_CHROMEOS)
   chromeos::input_method::InputMethodManager::Get()->
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.h b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
index b757c69..f853248 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
@@ -47,7 +47,7 @@
                    CommandUpdater* command_updater,
                    bool popup_window_mode,
                    LocationBarView* location_bar,
-                   const gfx::Font& font,
+                   const gfx::FontList& font_list,
                    int font_y_offset);
   virtual ~OmniboxViewViews();
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_win.cc b/chrome/browser/ui/views/omnibox/omnibox_view_win.cc
index df7e448..a3617ea 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_win.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_win.cc
@@ -61,6 +61,7 @@
 #include "ui/base/keycodes/keyboard_codes.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_win.h"
+#include "ui/base/touch/touch_enabled.h"
 #include "ui/base/win/hwnd_util.h"
 #include "ui/base/win/mouse_wheel_util.h"
 #include "ui/base/win/touch_input.h"
@@ -463,12 +464,12 @@
                                LocationBarView* location_bar,
                                CommandUpdater* command_updater,
                                bool popup_window_mode,
-                               const gfx::Font& font,
+                               const gfx::FontList& font_list,
                                int font_y_offset)
     : OmniboxView(location_bar->profile(), controller, toolbar_model,
                   command_updater),
-      popup_view_(
-          OmniboxPopupContentsView::Create(font, this, model(), location_bar)),
+      popup_view_(OmniboxPopupContentsView::Create(
+          font_list, this, model(), location_bar)),
       location_bar_(location_bar),
       popup_window_mode_(popup_window_mode),
       force_hidden_(false),
@@ -478,7 +479,7 @@
       can_discard_mousemove_(false),
       ignore_ime_messages_(false),
       delete_at_end_pressed_(false),
-      font_(font),
+      font_list_(font_list),
       font_y_adjustment_(font_y_offset),
       possible_drag_(false),
       in_drag_(false),
@@ -503,7 +504,7 @@
   Create(location_bar->GetWidget()->GetNativeView(), 0, 0, 0,
          l10n_util::GetExtendedStyles());
   SetReadOnly(popup_window_mode_);
-  gfx::NativeFont native_font(font_.GetNativeFont());
+  gfx::NativeFont native_font(font_list_.GetPrimaryFont().GetNativeFont());
   SetFont(native_font);
 
   // IMF_DUALFONT (on by default) is supposed to use one font for ASCII text
@@ -530,7 +531,7 @@
   base::win::ScopedSelectObject font_in_dc(screen_dc, native_font);
   TEXTMETRIC tm = {0};
   GetTextMetrics(screen_dc, &tm);
-  int cap_height = font_.GetBaseline() - tm.tmInternalLeading;
+  int cap_height = font_list_.GetBaseline() - tm.tmInternalLeading;
   // The ratio of a font's x-height to its cap height.  Sadly, Windows
   // doesn't provide a true value for a font's x-height in its text
   // metrics, so we approximate.
@@ -813,7 +814,7 @@
   // internally in Windows, as well.
   ::DestroyCaret();
   if (model()->is_caret_visible()) {
-    ::CreateCaret(m_hWnd, (HBITMAP) NULL, 1, font_.GetHeight());
+    ::CreateCaret(m_hWnd, (HBITMAP) NULL, 1, font_list_.GetHeight());
     // According to the Windows API documentation, a newly created caret needs
     // ShowCaret to be visible.
     ShowCaret();
@@ -1441,7 +1442,8 @@
     // Enable TSF support of RichEdit.
     SetEditStyle(SES_USECTF, SES_USECTF);
   }
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
+  if ((base::win::GetVersion() >= base::win::VERSION_WIN8) &&
+      ui::AreTouchEventsEnabled()) {
     BOOL touch_mode = RegisterTouchWindow(m_hWnd, TWF_WANTPALM);
     DCHECK(touch_mode);
   }
@@ -2517,7 +2519,7 @@
   const SkScalar kStrokeWidthPixels = SkIntToScalar(2);
   const int kAdditionalSpaceOutsideFont =
       static_cast<int>(ceil(kStrokeWidthPixels * 1.5f));
-  const int font_ascent = font_.GetBaseline();
+  const int font_ascent = font_list_.GetBaseline();
   const CRect scheme_rect(
       PosFromChar(insecure_scheme_component_.begin).x,
       font_top + font_ascent - font_x_height_ - kAdditionalSpaceOutsideFont,
@@ -2605,7 +2607,7 @@
   const CRect highlight_rect(highlight_x,
                              highlight_y,
                              highlight_x + 1,
-                             highlight_y + font_.GetHeight());
+                             highlight_y + font_list_.GetHeight());
 
   // Clip the highlight to the region being painted.
   CRect clip_rect;
@@ -2760,7 +2762,7 @@
   if ((position != -1) && (position <= GetTextLength())) {
     const POINT min_loc(PosFromChar(position));
     const RECT highlight_bounds = {min_loc.x - 1, font_y_adjustment_,
-        min_loc.x + 2, font_.GetHeight() + font_y_adjustment_};
+        min_loc.x + 2, font_list_.GetHeight() + font_y_adjustment_};
     InvalidateRect(&highlight_bounds, false);
   }
 }
@@ -2825,9 +2827,10 @@
 }
 
 int OmniboxViewWin::WidthNeededToDisplay(const string16& text) const {
-  // Use font_.GetStringWidth() instead of PosFromChar(GetTextLength()) because
-  // PosFromChar() is apparently buggy. In both LTR UI and RTL UI with
-  // left-to-right layout, PosFromChar(i) might return 0 when i is greater than
-  // 1.
-  return font_.GetStringWidth(text) + GetHorizontalMargin();
+  // Use font_list_.GetPrimaryFont().GetStringWidth() instead of
+  // PosFromChar(GetTextLength()) because PosFromChar() is apparently buggy.
+  // In both LTR UI and RTL UI with left-to-right layout, PosFromChar(i) might
+  // return 0 when i is greater than 1.
+  return font_list_.GetPrimaryFont().GetStringWidth(text) +
+      GetHorizontalMargin();
 }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_win.h b/chrome/browser/ui/views/omnibox/omnibox_view_win.h
index 7caecff..89a91b6 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_win.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_win.h
@@ -21,7 +21,7 @@
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/base/win/extra_sdk_defines.h"
 #include "ui/base/window_open_disposition.h"
-#include "ui/gfx/font.h"
+#include "ui/gfx/font_list.h"
 
 class LocationBarView;
 class OmniboxPopupView;
@@ -63,7 +63,7 @@
                  LocationBarView* parent_view,
                  CommandUpdater* command_updater,
                  bool popup_window_mode,
-                 const gfx::Font& font,
+                 const gfx::FontList& font_list,
                  int font_y_offset);
   ~OmniboxViewWin();
 
@@ -470,9 +470,8 @@
   scoped_ptr<ui::SimpleMenuModel> context_menu_contents_;
   scoped_ptr<views::MenuRunner> context_menu_runner_;
 
-  // Font we're using.  We keep a reference to make sure the font supplied to
-  // the constructor doesn't go away before we do.
-  gfx::Font font_;
+  // The font list to draw text in Omnibox.
+  gfx::FontList font_list_;
 
   // Metrics about the font, which we keep so we don't need to recalculate them
   // every time we paint.  |font_y_adjustment_| is the number of pixels we need
diff --git a/chrome/browser/ui/views/omnibox/omnibox_views.cc b/chrome/browser/ui/views/omnibox/omnibox_views.cc
index 5d2f4bb..14bec61 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_views.cc
@@ -33,18 +33,18 @@
                                CommandUpdater* command_updater,
                                bool popup_window_mode,
                                LocationBarView* location_bar,
-                               const gfx::Font& font,
+                               const gfx::FontList& font_list,
                                int font_y_offset) {
 #if defined(OS_WIN) && !defined(USE_AURA)
   if (!views::Textfield::IsViewsTextfieldEnabled()) {
     return new OmniboxViewWin(
         controller, toolbar_model, location_bar, command_updater,
-        popup_window_mode, font, font_y_offset);
+        popup_window_mode, font_list, font_y_offset);
   }
 #endif
   OmniboxViewViews* omnibox = new OmniboxViewViews(
       controller, toolbar_model, profile, command_updater, popup_window_mode,
-      location_bar, font, font_y_offset);
+      location_bar, font_list, font_y_offset);
   omnibox->Init();
   return omnibox;
 }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_views.h b/chrome/browser/ui/views/omnibox/omnibox_views.h
index b1b50a4..1cbc947 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_views.h
@@ -15,7 +15,7 @@
 class ToolbarModel;
 
 namespace gfx {
-class Font;
+class FontList;
 }
 
 namespace views {
@@ -35,7 +35,7 @@
                                CommandUpdater* command_updater,
                                bool popup_window_mode,
                                LocationBarView* location_bar,
-                               const gfx::Font& font,
+                               const gfx::FontList& font_list,
                                int font_y_offset);
 
 #endif  // CHROME_BROWSER_UI_VIEWS_OMNIBOX_OMNIBOX_VIEWS_H_
diff --git a/chrome/browser/ui/views/omnibox/touch_omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/touch_omnibox_popup_contents_view.cc
index 5c6c3ef..1a94ea3 100644
--- a/chrome/browser/ui/views/omnibox/touch_omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/touch_omnibox_popup_contents_view.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "ui/gfx/canvas.h"
-#include "ui/gfx/font.h"
+#include "ui/gfx/font_list.h"
 #include "ui/gfx/path.h"
 #include "ui/gfx/rect.h"
 #include "ui/gfx/size.h"
@@ -20,8 +20,8 @@
     OmniboxResultViewModel* model,
     int model_index,
     LocationBarView* location_bar_view,
-    const gfx::Font& font)
-    : OmniboxResultView(model, model_index, location_bar_view, font) {
+    const gfx::FontList& font_list)
+    : OmniboxResultView(model, model_index, location_bar_view, font_list) {
   set_edge_item_padding(8);
   set_item_padding(8);
   set_minimum_text_vertical_padding(10);
@@ -56,11 +56,11 @@
 // TouchOmniboxPopupContentsView -----------------------------------------
 
 TouchOmniboxPopupContentsView::TouchOmniboxPopupContentsView(
-    const gfx::Font& font,
+    const gfx::FontList& font_list,
     OmniboxView* omnibox_view,
     OmniboxEditModel* edit_model,
     LocationBarView* location_bar_view)
-    : OmniboxPopupContentsView(font, omnibox_view, edit_model,
+    : OmniboxPopupContentsView(font_list, omnibox_view, edit_model,
                                location_bar_view) {
 }
 
@@ -102,9 +102,9 @@
 OmniboxResultView* TouchOmniboxPopupContentsView::CreateResultView(
     OmniboxResultViewModel* model,
     int model_index,
-    const gfx::Font& font) {
+    const gfx::FontList& font_list) {
   return new TouchOmniboxResultView(model, model_index, location_bar_view(),
-                                    font);
+                                    font_list);
 }
 
 std::vector<views::View*> TouchOmniboxPopupContentsView::GetVisibleChildren() {
diff --git a/chrome/browser/ui/views/omnibox/touch_omnibox_popup_contents_view.h b/chrome/browser/ui/views/omnibox/touch_omnibox_popup_contents_view.h
index 75cdad0..e87745c 100644
--- a/chrome/browser/ui/views/omnibox/touch_omnibox_popup_contents_view.h
+++ b/chrome/browser/ui/views/omnibox/touch_omnibox_popup_contents_view.h
@@ -25,7 +25,7 @@
   TouchOmniboxResultView(OmniboxResultViewModel* model,
                          int model_index,
                          LocationBarView* location_bar_view,
-                         const gfx::Font& font);
+                         const gfx::FontList& font_list);
 
  private:
   virtual ~TouchOmniboxResultView();
@@ -42,7 +42,7 @@
 class TouchOmniboxPopupContentsView
     : public OmniboxPopupContentsView {
  public:
-  TouchOmniboxPopupContentsView(const gfx::Font& font,
+  TouchOmniboxPopupContentsView(const gfx::FontList& font_list,
                                 OmniboxView* omnibox_view,
                                 OmniboxEditModel* edit_model,
                                 LocationBarView* location_bar_view);
@@ -54,9 +54,10 @@
  protected:
   // OmniboxPopupContentsView:
   virtual void PaintResultViews(gfx::Canvas* canvas) OVERRIDE;
-  virtual OmniboxResultView* CreateResultView(OmniboxResultViewModel* model,
-                                              int model_index,
-                                              const gfx::Font& font) OVERRIDE;
+  virtual OmniboxResultView* CreateResultView(
+      OmniboxResultViewModel* model,
+      int model_index,
+      const gfx::FontList& font_list) OVERRIDE;
 
  private:
   std::vector<View*> GetVisibleChildren();
diff --git a/chrome/browser/ui/views/select_file_dialog_extension.cc b/chrome/browser/ui/views/select_file_dialog_extension.cc
index 33c7de7..e098707 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension.cc
+++ b/chrome/browser/ui/views/select_file_dialog_extension.cc
@@ -32,6 +32,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "ui/base/base_window.h"
 #include "ui/shell_dialogs/selected_file_info.h"
+#include "ui/views/widget/widget.h"
 
 using apps::ShellWindow;
 using content::BrowserThread;
@@ -156,7 +157,7 @@
                    extension_id));
   }
 
-  dialog->Close();
+  dialog->GetWidget()->Close();
 }
 
 // static
@@ -332,9 +333,9 @@
   // (crbug.com/178013 #9-#11). In such a case, we use the last selected
   // directory as a workaround. Real fix is tracked at crbug.com/110119.
   if (default_dialog_path.IsAbsolute() &&
-      (file_manager_util::ConvertFileToRelativeFileSystemPath(
+      (file_manager::util::ConvertFileToRelativeFileSystemPath(
            profile_, kFileBrowserDomain, default_dialog_path, &virtual_path) ||
-       file_manager_util::ConvertFileToRelativeFileSystemPath(
+       file_manager::util::ConvertFileToRelativeFileSystemPath(
            profile_, kFileBrowserDomain, fallback_path, &virtual_path))) {
     virtual_path = base::FilePath("/").Append(virtual_path);
   } else {
@@ -345,7 +346,7 @@
   has_multiple_file_type_choices_ =
       file_types ? file_types->extensions.size() > 1 : true;
 
-  GURL file_browser_url = file_manager_util::GetFileBrowserUrlWithParams(
+  GURL file_browser_url = file_manager::util::GetFileBrowserUrlWithParams(
       type, title, virtual_path, file_types, file_type_index,
       default_extension);
 
@@ -355,7 +356,7 @@
       kFileManagerWidth,
       kFileManagerHeight,
 #if defined(USE_AURA)
-      file_manager_util::GetTitleFromType(type),
+      file_manager::util::GetTitleFromType(type),
 #else
       // HTML-based header used.
       string16(),
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
index f257a3f..931abef 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
@@ -23,10 +23,6 @@
   status_icon_->SetToolTip(tool_tip);
 }
 
-void StatusIconLinuxWrapper::SetClickActionLabel(const string16& label) {
-  status_icon_->SetClickActionLabel(label);
-}
-
 void StatusIconLinuxWrapper::DisplayBalloon(const gfx::ImageSkia& icon,
                                             const string16& title,
                                             const string16& contents) {
@@ -37,10 +33,17 @@
   DispatchClickEvent();
 }
 
-StatusIconLinuxWrapper* StatusIconLinuxWrapper::CreateWrappedStatusIcon() {
+bool StatusIconLinuxWrapper::HasClickAction() {
+  return HasObservers();
+}
+
+StatusIconLinuxWrapper* StatusIconLinuxWrapper::CreateWrappedStatusIcon(
+    const gfx::ImageSkia& image,
+    const string16& tool_tip) {
   const ui::LinuxUI* linux_ui = ui::LinuxUI::instance();
   if (linux_ui) {
-    scoped_ptr<StatusIconLinux> status_icon = linux_ui->CreateLinuxStatusIcon();
+    scoped_ptr<StatusIconLinux> status_icon =
+        linux_ui->CreateLinuxStatusIcon(image, tool_tip);
     if (status_icon.get())
       return new StatusIconLinuxWrapper(status_icon.release());
   }
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
index 5bb7dbf..153ce37 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
@@ -21,15 +21,17 @@
   virtual void SetImage(const gfx::ImageSkia& image) OVERRIDE;
   virtual void SetPressedImage(const gfx::ImageSkia& image) OVERRIDE;
   virtual void SetToolTip(const string16& tool_tip) OVERRIDE;
-  virtual void SetClickActionLabel(const string16& label) OVERRIDE;
   virtual void DisplayBalloon(const gfx::ImageSkia& icon,
                               const string16& title,
                               const string16& contents) OVERRIDE;
 
   // StatusIconLinux::Delegate overrides:
   virtual void OnClick() OVERRIDE;
+  virtual bool HasClickAction() OVERRIDE;
 
-  static StatusIconLinuxWrapper* CreateWrappedStatusIcon();
+  static StatusIconLinuxWrapper* CreateWrappedStatusIcon(
+      const gfx::ImageSkia& image,
+      const string16& tool_tip);
 
  protected:
   // StatusIcon overrides:
diff --git a/chrome/browser/ui/views/status_icons/status_tray_linux.cc b/chrome/browser/ui/views/status_icons/status_tray_linux.cc
index 2aa06c4..58db07a 100644
--- a/chrome/browser/ui/views/status_icons/status_tray_linux.cc
+++ b/chrome/browser/ui/views/status_icons/status_tray_linux.cc
@@ -14,8 +14,11 @@
 StatusTrayLinux::~StatusTrayLinux() {
 }
 
-StatusIcon* StatusTrayLinux::CreatePlatformStatusIcon(StatusIconType type) {
-  return StatusIconLinuxWrapper::CreateWrappedStatusIcon();
+StatusIcon* StatusTrayLinux::CreatePlatformStatusIcon(
+    StatusIconType type,
+    const gfx::ImageSkia& image,
+    const string16& tool_tip) {
+  return StatusIconLinuxWrapper::CreateWrappedStatusIcon(image, tool_tip);
 }
 
 StatusTray* StatusTray::Create() {
diff --git a/chrome/browser/ui/views/status_icons/status_tray_linux.h b/chrome/browser/ui/views/status_icons/status_tray_linux.h
index ea38ae5..d1540a5 100644
--- a/chrome/browser/ui/views/status_icons/status_tray_linux.h
+++ b/chrome/browser/ui/views/status_icons/status_tray_linux.h
@@ -15,7 +15,10 @@
 
  protected:
   // Overriden from StatusTray:
-  virtual StatusIcon* CreatePlatformStatusIcon(StatusIconType type) OVERRIDE;
+  virtual StatusIcon* CreatePlatformStatusIcon(
+      StatusIconType type,
+      const gfx::ImageSkia& image,
+      const string16& tool_tip) OVERRIDE;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(StatusTrayLinux);
diff --git a/chrome/browser/ui/views/status_icons/status_tray_win.cc b/chrome/browser/ui/views/status_icons/status_tray_win.cc
index 8df1cf5..21a00d4 100644
--- a/chrome/browser/ui/views/status_icons/status_tray_win.cc
+++ b/chrome/browser/ui/views/status_icons/status_tray_win.cc
@@ -120,16 +120,24 @@
 }
 
 StatusIcon* StatusTrayWin::CreatePlatformStatusIcon(
-    StatusTray::StatusIconType type) {
+    StatusTray::StatusIconType type,
+    const gfx::ImageSkia& image,
+    const string16& tool_tip) {
   UINT next_icon_id;
   if (type == StatusTray::OTHER_ICON)
     next_icon_id = NextIconId();
   else
     next_icon_id = ReservedIconId(type);
 
+  StatusIcon* icon = NULL;
   if (win8::IsSingleWindowMetroMode())
-    return new StatusIconMetro(next_icon_id);
-  return new StatusIconWin(next_icon_id, window_, kStatusIconMessage);
+    icon = new StatusIconMetro(next_icon_id);
+  else
+    icon = new StatusIconWin(next_icon_id, window_, kStatusIconMessage);
+
+  icon->SetImage(image);
+  icon->SetToolTip(tool_tip);
+  return icon;
 }
 
 UINT StatusTrayWin::NextIconId() {
diff --git a/chrome/browser/ui/views/status_icons/status_tray_win.h b/chrome/browser/ui/views/status_icons/status_tray_win.h
index 87974f0..3e2be2f 100644
--- a/chrome/browser/ui/views/status_icons/status_tray_win.h
+++ b/chrome/browser/ui/views/status_icons/status_tray_win.h
@@ -23,7 +23,10 @@
 
  protected:
   // Overriden from StatusTray:
-  virtual StatusIcon* CreatePlatformStatusIcon(StatusIconType type) OVERRIDE;
+  virtual StatusIcon* CreatePlatformStatusIcon(
+      StatusIconType type,
+      const gfx::ImageSkia& image,
+      const string16& tool_tip) OVERRIDE;
 
  private:
   // Static callback invoked when a message comes in to our messaging window.
diff --git a/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc b/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc
index b42fd96..b021f8c 100644
--- a/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc
+++ b/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc
@@ -14,6 +14,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia.h"
 
 class FakeStatusIconObserver : public StatusIconObserver {
  public:
@@ -43,12 +44,11 @@
   // Create an icon, set the images, tooltip, and context menu, then shut it
   // down.
   StatusTrayWin tray;
-  StatusIcon* icon = tray.CreateStatusIcon(StatusTray::OTHER_ICON);
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
-  icon->SetImage(*image);
+  StatusIcon* icon = tray.CreateStatusIcon(
+      StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip"));
   icon->SetPressedImage(*image);
-  icon->SetToolTip(ASCIIToUTF16("tool tip"));
   ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(NULL);
   menu->AddItem(0, L"foo");
   icon->SetContextMenu(menu);
@@ -58,8 +58,11 @@
 TEST(StatusTrayWinTest, ClickOnIcon) {
   // Create an icon, send a fake click event, make sure observer is called.
   StatusTrayWin tray;
-  StatusIconWin* icon = static_cast<StatusIconWin*>(
-      tray.CreateStatusIcon(StatusTray::OTHER_ICON));
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
+
+  StatusIconWin* icon = static_cast<StatusIconWin*>(tray.CreateStatusIcon(
+      StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip")));
   FakeStatusIconObserver observer;
   icon->AddObserver(&observer);
   // Mimic a click.
@@ -73,8 +76,11 @@
 TEST(StatusTrayWinTest, ClickOnBalloon) {
   // Create an icon, send a fake click event, make sure observer is called.
   StatusTrayWin tray;
-  StatusIconWin* icon = static_cast<StatusIconWin*>(
-      tray.CreateStatusIcon(StatusTray::OTHER_ICON));
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
+
+  StatusIconWin* icon = static_cast<StatusIconWin*>(tray.CreateStatusIcon(
+      StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip")));
   FakeStatusIconObserver observer;
   icon->AddObserver(&observer);
   // Mimic a click.
diff --git a/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc b/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc
index 95c78f8..65cb45e 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc
+++ b/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/ui/sync/one_click_signin_helper.h"
 #include "chrome/browser/ui/sync/one_click_signin_histogram.h"
 #include "chrome/common/url_constants.h"
-#include "content/public/browser/web_contents.h"
 #include "grit/chromium_strings.h"
 #include "grit/generated_resources.h"
 #include "grit/theme_resources.h"
@@ -53,7 +52,8 @@
     BrowserWindow::OneClickSigninBubbleType type,
     const string16& email,
     const string16& error_message,
-    ToolbarView* toolbar_view,
+    scoped_ptr<OneClickSigninBubbleDelegate> delegate,
+    views::View* anchor_view,
     const BrowserWindow::StartSyncCallback& start_sync) {
   if (IsShowing())
     return;
@@ -61,18 +61,18 @@
   switch (type) {
     case BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE:
       bubble_view_ = new OneClickSigninBubbleView(
-          toolbar_view->GetWebContents(), toolbar_view->app_menu(),
-          error_message, string16(), start_sync, false);
+          error_message, string16(), delegate.Pass(),
+          anchor_view, start_sync, false);
       break;
     case BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_MODAL_DIALOG:
       bubble_view_ = new OneClickSigninBubbleView(
-          toolbar_view->GetWebContents(), toolbar_view->location_bar(),
-          string16(), string16(), start_sync, true);
+          string16(), string16(), delegate.Pass(),
+          anchor_view, start_sync, true);
       break;
     case BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_SAML_MODAL_DIALOG:
       bubble_view_ = new OneClickSigninBubbleView(
-          toolbar_view->GetWebContents(), toolbar_view->location_bar(),
-          string16(), email, start_sync, true);
+          string16(), email, delegate.Pass(),
+          anchor_view, start_sync, true);
       break;
   }
 
@@ -91,14 +91,14 @@
 }
 
 OneClickSigninBubbleView::OneClickSigninBubbleView(
-    content::WebContents* web_contents,
-    views::View* anchor_view,
     const string16& error_message,
     const string16& email,
+    scoped_ptr<OneClickSigninBubbleDelegate> delegate,
+    views::View* anchor_view,
     const BrowserWindow::StartSyncCallback& start_sync_callback,
     bool is_sync_dialog)
     : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
-      web_contents_(web_contents),
+      delegate_(delegate.Pass()),
       error_message_(error_message),
       email_(email),
       start_sync_callback_(start_sync_callback),
@@ -342,14 +342,7 @@
           one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE);
       clicked_learn_more_ = true;
     }
-
-    WindowOpenDisposition location =
-      is_sync_dialog_ ? NEW_WINDOW : NEW_FOREGROUND_TAB;
-
-    content::OpenURLParams params(
-        GURL(chrome::kChromeSyncLearnMoreURL), content::Referrer(),
-        location, content::PAGE_TRANSITION_LINK, false);
-    web_contents_->OpenURL(params);
+    delegate_->OnLearnMoreLinkClicked(is_sync_dialog_);
 
     // don't hide the modal dialog, as this is an informational link
     if (is_sync_dialog_)
@@ -364,10 +357,7 @@
       base::ResetAndReturn(&start_sync_callback_).Run(
       OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST);
     } else {
-      content::OpenURLParams params(
-          GURL(chrome::kChromeUISettingsURL), content::Referrer(),
-          CURRENT_TAB, content::PAGE_TRANSITION_LINK, false);
-      web_contents_->OpenURL(params);
+      delegate_->OnAdvancedLinkClicked();
     }
   }
 
diff --git a/chrome/browser/ui/views/sync/one_click_signin_bubble_view.h b/chrome/browser/ui/views/sync/one_click_signin_bubble_view.h
index a04cb55..78b58f8 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_bubble_view.h
+++ b/chrome/browser/ui/views/sync/one_click_signin_bubble_view.h
@@ -9,9 +9,10 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/views/toolbar_view.h"
+#include "chrome/browser/ui/sync/one_click_signin_bubble_delegate.h"
 #include "ui/views/bubble/bubble_delegate.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/link_listener.h"
@@ -22,7 +23,9 @@
 
 namespace views {
 class GridLayout;
+class ImageButton;
 class LabelButton;
+class View;
 }
 
 // OneClickSigninBubbleView is a view intended to be used as the content of an
@@ -38,7 +41,8 @@
   static void ShowBubble(BrowserWindow::OneClickSigninBubbleType type,
                          const string16& email,
                          const string16& error_message,
-                         ToolbarView* toolbar_view,
+                         scoped_ptr<OneClickSigninBubbleDelegate> delegate,
+                         views::View* anchor_view,
                          const BrowserWindow::StartSyncCallback& start_sync);
 
   static bool IsShowing();
@@ -52,32 +56,25 @@
  protected:
   // Creates a OneClickSigninBubbleView.
   OneClickSigninBubbleView(
-      content::WebContents* web_contents,
-      views::View* anchor_view,
       const string16& error_message,
       const string16& email,
+      scoped_ptr<OneClickSigninBubbleDelegate> delegate,
+      views::View* anchor_view,
       const BrowserWindow::StartSyncCallback& start_sync_callback,
       bool is_sync_dialog);
 
   virtual ~OneClickSigninBubbleView();
 
  private:
-  friend class OneClickSigninBubbleViewBrowserTest;
+  friend class OneClickSigninBubbleViewTest;
 
-  FRIEND_TEST_ALL_PREFIXES(
-    OneClickSigninBubbleViewBrowserTest, BubbleOkButton);
-  FRIEND_TEST_ALL_PREFIXES(
-    OneClickSigninBubbleViewBrowserTest, DialogOkButton);
-  FRIEND_TEST_ALL_PREFIXES(
-    OneClickSigninBubbleViewBrowserTest, DialogUndoButton);
-  FRIEND_TEST_ALL_PREFIXES(
-    OneClickSigninBubbleViewBrowserTest, BubbleAdvancedLink);
-  FRIEND_TEST_ALL_PREFIXES(
-    OneClickSigninBubbleViewBrowserTest, DialogAdvancedLink);
-  FRIEND_TEST_ALL_PREFIXES(
-    OneClickSigninBubbleViewBrowserTest, BubbleLearnMoreLink);
-  FRIEND_TEST_ALL_PREFIXES(
-    OneClickSigninBubbleViewBrowserTest, DialogLearnMoreLink);
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninBubbleViewTest, BubbleOkButton);
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninBubbleViewTest, DialogOkButton);
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninBubbleViewTest, DialogUndoButton);
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninBubbleViewTest, BubbleAdvancedLink);
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninBubbleViewTest, DialogAdvancedLink);
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninBubbleViewTest, BubbleLearnMoreLink);
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninBubbleViewTest, DialogLearnMoreLink);
 
   // Overridden from views::BubbleDelegateView:
   virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
@@ -114,8 +111,8 @@
   // Creates advanced link to be used at the bottom of the bubble.
   void InitAdvancedLink();
 
-  // The bubble/dialog will always outlive the web_content, so this is ok.
-  content::WebContents* web_contents_;
+  // Delegate to handle clicking on links in the bubble.
+  scoped_ptr<OneClickSigninBubbleDelegate> delegate_;
 
   // Alternate error message to be displayed.
   const string16 error_message_;
diff --git a/chrome/browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc b/chrome/browser/ui/views/sync/one_click_signin_bubble_view_unittest.cc
similarity index 62%
rename from chrome/browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc
rename to chrome/browser/ui/views/sync/one_click_signin_bubble_view_unittest.cc
index a2ed79f..24dddcd 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/sync/one_click_signin_bubble_view_unittest.cc
@@ -1,32 +1,63 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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.
 
 #include "chrome/browser/ui/views/sync/one_click_signin_bubble_view.h"
 
 #include "base/bind.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/test/base/in_process_browser_test.h"
+#include "base/bind_helpers.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/ui/sync/one_click_signin_bubble_delegate.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/common/page_transition_types.h"
 #include "ui/views/controls/button/label_button.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget.h"
 
-class OneClickSigninBubbleViewBrowserTest : public InProcessBrowserTest {
+class OneClickSigninBubbleViewTest : public views::ViewsTestBase {
  public:
-  OneClickSigninBubbleViewBrowserTest()
+  OneClickSigninBubbleViewTest()
       : on_start_sync_called_(false),
-        mode_(OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST) {
+        mode_(OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST),
+        bubble_learn_more_click_count_(0),
+        dialog_learn_more_click_count_(0),
+        advanced_click_count_(0),
+        anchor_widget_(NULL) {
   }
 
+  virtual void SetUp() OVERRIDE {
+    views::ViewsTestBase::SetUp();
+
+    // Create a widget to host the anchor view.
+    anchor_widget_ = new views::Widget;
+    views::Widget::InitParams widget_params = CreateParams(
+        views::Widget::InitParams::TYPE_WINDOW);
+    anchor_widget_->Init(widget_params);
+    anchor_widget_->Show();
+  }
+
+  virtual void TearDown() OVERRIDE {
+    OneClickSigninBubbleView::Hide();
+    anchor_widget_->Close();
+    anchor_widget_ = NULL;
+    views::ViewsTestBase::TearDown();
+  }
+
+ protected:
   OneClickSigninBubbleView* ShowOneClickSigninBubble(
     BrowserWindow::OneClickSigninBubbleType bubble_type) {
-    browser()->window()->ShowOneClickSigninBubble(
+
+    scoped_ptr<OneClickSigninBubbleDelegate> delegate;
+    delegate.reset(new OneClickSigninBubbleTestDelegate(this));
+
+    OneClickSigninBubbleView::ShowBubble(
         bubble_type,
         string16(),
         string16(),
-        base::Bind(&OneClickSigninBubbleViewBrowserTest::OnStartSync, this));
+        delegate.Pass(),
+        anchor_widget_->GetContentsView(),
+        base::Bind(&OneClickSigninBubbleViewTest::OnStartSync,
+                   base::Unretained(this)));
 
     OneClickSigninBubbleView* view =
         OneClickSigninBubbleView::view_for_testing();
@@ -40,28 +71,59 @@
     mode_ = mode;
   }
 
- protected:
   bool on_start_sync_called_;
   OneClickSigninSyncStarter::StartSyncMode mode_;
+  int bubble_learn_more_click_count_;
+  int dialog_learn_more_click_count_;
+  int advanced_click_count_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(OneClickSigninBubbleViewBrowserTest);
+  friend class OneClickSigninBubbleTestDelegate;
+
+  class OneClickSigninBubbleTestDelegate
+      : public OneClickSigninBubbleDelegate {
+   public:
+    // |test| is not owned by this object.
+    explicit OneClickSigninBubbleTestDelegate(
+        OneClickSigninBubbleViewTest* test) : test_(test) {}
+
+    // OneClickSigninBubbleDelegate:
+    virtual void OnLearnMoreLinkClicked(bool is_dialog) OVERRIDE {
+      if (is_dialog)
+        ++test_->dialog_learn_more_click_count_;
+      else
+        ++test_->bubble_learn_more_click_count_;
+    }
+    virtual void OnAdvancedLinkClicked() OVERRIDE {
+      ++test_->advanced_click_count_;
+    }
+
+   private:
+    OneClickSigninBubbleViewTest* test_;
+
+    DISALLOW_COPY_AND_ASSIGN(OneClickSigninBubbleTestDelegate);
+  };
+
+  // Widget to host the anchor view of the bubble. Destroys itself when closed.
+  views::Widget* anchor_widget_;
+
+  DISALLOW_COPY_AND_ASSIGN(OneClickSigninBubbleViewTest);
 };
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest, ShowBubble) {
+TEST_F(OneClickSigninBubbleViewTest, ShowBubble) {
   ShowOneClickSigninBubble(BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE);
   content::RunAllPendingInMessageLoop();
   EXPECT_TRUE(OneClickSigninBubbleView::IsShowing());
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest, ShowDialog) {
+TEST_F(OneClickSigninBubbleViewTest, ShowDialog) {
   ShowOneClickSigninBubble(
     BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_MODAL_DIALOG);
   content::RunAllPendingInMessageLoop();
   EXPECT_TRUE(OneClickSigninBubbleView::IsShowing());
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest, HideBubble) {
+TEST_F(OneClickSigninBubbleViewTest, HideBubble) {
   ShowOneClickSigninBubble(BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE);
 
   OneClickSigninBubbleView::Hide();
@@ -69,7 +131,7 @@
   EXPECT_FALSE(OneClickSigninBubbleView::IsShowing());
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest, HideDialog) {
+TEST_F(OneClickSigninBubbleViewTest, HideDialog) {
   ShowOneClickSigninBubble(
     BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_MODAL_DIALOG);
 
@@ -80,7 +142,7 @@
   EXPECT_EQ(OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS, mode_);
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest, BubbleOkButton) {
+TEST_F(OneClickSigninBubbleViewTest, BubbleOkButton) {
   OneClickSigninBubbleView* view =
     ShowOneClickSigninBubble(
       BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE);
@@ -99,7 +161,7 @@
   EXPECT_FALSE(OneClickSigninBubbleView::IsShowing());
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest, DialogOkButton) {
+TEST_F(OneClickSigninBubbleViewTest, DialogOkButton) {
   OneClickSigninBubbleView* view = ShowOneClickSigninBubble(
       BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_MODAL_DIALOG);
 
@@ -119,8 +181,7 @@
   EXPECT_EQ(OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS, mode_);
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest,
-                       DialogUndoButton) {
+TEST_F(OneClickSigninBubbleViewTest, DialogUndoButton) {
   OneClickSigninBubbleView* view = ShowOneClickSigninBubble(
     BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_MODAL_DIALOG);
 
@@ -140,63 +201,69 @@
   EXPECT_EQ(OneClickSigninSyncStarter::UNDO_SYNC, mode_);
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest,
-                       BubbleAdvancedLink) {
-  int starting_tab_count = browser()->tab_strip_model()->count();
-
+TEST_F(OneClickSigninBubbleViewTest, BubbleAdvancedLink) {
   OneClickSigninBubbleView* view = ShowOneClickSigninBubble(
     BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE);
 
-  // Simulate pressing a link in the bubble.This should replace the current tab.
+  // Simulate pressing a link in the bubble.
   views::LinkListener* listener = view;
   listener->LinkClicked(view->advanced_link_, 0);
 
-  // View should no longer be showing and the current tab should be replaced.
+  // View should no longer be showing and the OnAdvancedLinkClicked method
+  // of the delegate should have been called.
   content::RunAllPendingInMessageLoop();
   EXPECT_FALSE(OneClickSigninBubbleView::IsShowing());
-  int tab_count = browser()->tab_strip_model()->count();
-  EXPECT_EQ(starting_tab_count, tab_count);
+  EXPECT_EQ(1, advanced_click_count_);
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest,
-                       DialogAdvancedLink) {
-  int starting_tab_count = browser()->tab_strip_model()->count();
-
+TEST_F(OneClickSigninBubbleViewTest, DialogAdvancedLink) {
   OneClickSigninBubbleView* view = ShowOneClickSigninBubble(
     BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_MODAL_DIALOG);
 
-  // Simulate pressing a link in the bubble.  This should open a new tab.
+  // Simulate pressing a link in the bubble.
   views::LinkListener* listener = view;
   listener->LinkClicked(view->advanced_link_, 0);
 
-  // View should no longer be showing and a new tab should be opened.
+  // View should no longer be showing. No delegate method should have been
+  // called: the callback is responsible to open the settings page.
   content::RunAllPendingInMessageLoop();
   EXPECT_TRUE(on_start_sync_called_);
   EXPECT_EQ(OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST, mode_);
   EXPECT_FALSE(OneClickSigninBubbleView::IsShowing());
-  int tab_count = browser()->tab_strip_model()->count();
-  EXPECT_EQ(starting_tab_count, tab_count);
+  EXPECT_EQ(0, advanced_click_count_);
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest,
-                       BubbleLearnMoreLink) {
-  int starting_tab_count = browser()->tab_strip_model()->count();
-
+TEST_F(OneClickSigninBubbleViewTest, BubbleLearnMoreLink) {
   OneClickSigninBubbleView* view = ShowOneClickSigninBubble(
     BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE);
 
-  // View should no longer be showing and a new tab should be added.
   views::LinkListener* listener = view;
   listener->LinkClicked(view->learn_more_link_, 0);
 
+  // View should no longer be showing and the OnLearnMoreLinkClicked method
+  // of the delegate should have been called with |is_dialog| == false.
   content::RunAllPendingInMessageLoop();
   EXPECT_FALSE(OneClickSigninBubbleView::IsShowing());
-  int tab_count = browser()->tab_strip_model()->count();
-  EXPECT_EQ(starting_tab_count + 1, tab_count);
+  EXPECT_EQ(1, bubble_learn_more_click_count_);
+  EXPECT_EQ(0, dialog_learn_more_click_count_);
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest,
-                       BubblePressEnterKey) {
+TEST_F(OneClickSigninBubbleViewTest, DialogLearnMoreLink) {
+  OneClickSigninBubbleView* view = ShowOneClickSigninBubble(
+      BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_MODAL_DIALOG);
+
+  views::LinkListener* listener = view;
+  listener->LinkClicked(view->learn_more_link_, 0);
+
+  // View should still be showing and the OnLearnMoreLinkClicked method
+  // of the delegate should have been called with |is_dialog| == true.
+  content::RunAllPendingInMessageLoop();
+  EXPECT_TRUE(OneClickSigninBubbleView::IsShowing());
+  EXPECT_EQ(0, bubble_learn_more_click_count_);
+  EXPECT_EQ(1, dialog_learn_more_click_count_);
+}
+
+TEST_F(OneClickSigninBubbleViewTest, BubblePressEnterKey) {
   OneClickSigninBubbleView* one_click_view = ShowOneClickSigninBubble(
     BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE);
 
@@ -211,8 +278,7 @@
   EXPECT_FALSE(OneClickSigninBubbleView::IsShowing());
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest,
-                       DialogPressEnterKey) {
+TEST_F(OneClickSigninBubbleViewTest, DialogPressEnterKey) {
   OneClickSigninBubbleView* one_click_view = ShowOneClickSigninBubble(
     BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_MODAL_DIALOG);
 
@@ -229,8 +295,7 @@
   EXPECT_EQ(OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS, mode_);
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest,
-                       BubblePressEscapeKey) {
+TEST_F(OneClickSigninBubbleViewTest, BubblePressEscapeKey) {
   OneClickSigninBubbleView* one_click_view = ShowOneClickSigninBubble(
     BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE);
 
@@ -245,8 +310,7 @@
   EXPECT_FALSE(OneClickSigninBubbleView::IsShowing());
 }
 
-IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest,
-                       DialogPressEscapeKey) {
+TEST_F(OneClickSigninBubbleViewTest, DialogPressEscapeKey) {
   OneClickSigninBubbleView* one_click_view = ShowOneClickSigninBubble(
     BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_MODAL_DIALOG);
 
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
index 3439214..75a873d 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
@@ -140,7 +140,7 @@
   return true;
 }
 
-void ProfileSigninConfirmationDialogViews::OnClose() {
+void ProfileSigninConfirmationDialogViews::OnClosed() {
   Cancel();
 }
 
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
index 3499cf1..f36cff5 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
@@ -51,7 +51,7 @@
   virtual views::View* CreateExtraView() OVERRIDE;
   virtual bool Accept() OVERRIDE;
   virtual bool Cancel() OVERRIDE;
-  virtual void OnClose() OVERRIDE;
+  virtual void OnClosed() OVERRIDE;
   virtual ui::ModalType GetModalType() const OVERRIDE;
   virtual void ViewHierarchyChanged(
       const ViewHierarchyChangedDetails& details) OVERRIDE;
diff --git a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc
index b6e35ab..56c0978 100644
--- a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc
+++ b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.cc
@@ -39,8 +39,7 @@
 TabModalConfirmDialogViews::TabModalConfirmDialogViews(
     TabModalConfirmDialogDelegate* delegate,
     content::WebContents* web_contents)
-    : web_contents_(web_contents),
-      delegate_(delegate),
+    : delegate_(delegate),
       dialog_(NULL),
       browser_context_(web_contents->GetBrowserContext()) {
   views::MessageBoxView::InitParams init_params(delegate->GetMessage());
@@ -60,7 +59,7 @@
       web_contents_modal_dialog_manager->delegate()->
           GetWebContentsModalDialogHost());
   web_contents_modal_dialog_manager->ShowDialog(dialog_->GetNativeView());
-  delegate_->set_operations_delegate(this);
+  delegate_->set_close_delegate(this);
 }
 
 TabModalConfirmDialogViews::~TabModalConfirmDialogViews() {
@@ -78,13 +77,6 @@
   dialog_->Close();
 }
 
-void TabModalConfirmDialogViews::SetPreventCloseOnLoadStart(bool prevent) {
-  WebContentsModalDialogManager* web_contents_modal_dialog_manager =
-      WebContentsModalDialogManager::FromWebContents(web_contents_);
-  web_contents_modal_dialog_manager->SetPreventCloseOnLoadStart(
-      dialog_->GetNativeView(), prevent);
-}
-
 //////////////////////////////////////////////////////////////////////////////
 // TabModalConfirmDialogViews, views::LinkListener implementation:
 
@@ -119,6 +111,11 @@
   return true;
 }
 
+bool TabModalConfirmDialogViews::Close() {
+  delegate_->Close();
+  return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // TabModalConfirmDialogViews, views::WidgetDelegate implementation:
 
diff --git a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h
index 36e3de3..06f0610 100644
--- a/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h
+++ b/chrome/browser/ui/views/tab_modal_confirm_dialog_views.h
@@ -39,6 +39,7 @@
   virtual string16 GetDialogButtonLabel(ui::DialogButton button) const OVERRIDE;
   virtual bool Cancel() OVERRIDE;
   virtual bool Accept() OVERRIDE;
+  virtual bool Close() OVERRIDE;
 
   // views::WidgetDelegate:
   virtual views::View* GetContentsView() OVERRIDE;
@@ -58,13 +59,10 @@
 
   // TabModalConfirmDialogCloseDelegate:
   virtual void CloseDialog() OVERRIDE;
-  virtual void SetPreventCloseOnLoadStart(bool prevent) OVERRIDE;
 
   // views::LinkListener:
   virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
 
-  content::WebContents* web_contents_;
-
   scoped_ptr<TabModalConfirmDialogDelegate> delegate_;
 
   // The message box view whose commands we handle.
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
index 68fc48f..6aedb9b 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/favicon/favicon_tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/search.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/tabs/tab_menu_model.h"
@@ -248,9 +249,12 @@
 }
 
 bool BrowserTabStripController::IsNewTabPage(int model_index) const {
-  return model_->ContainsIndex(model_index) &&
-      model_->GetWebContentsAt(model_index)->GetURL() ==
-      GURL(chrome::kChromeUINewTabURL);
+  if (!model_->ContainsIndex(model_index))
+    return false;
+
+  const WebContents* contents = model_->GetWebContentsAt(model_index);
+  return contents && (contents->GetURL() == GURL(chrome::kChromeUINewTabURL) ||
+      chrome::IsInstantNTP(contents));
 }
 
 void BrowserTabStripController::SelectTab(int model_index) {
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index fc9c3e2..a115371 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -401,9 +401,15 @@
 
 }  // namespace
 
+#if defined(OS_WIN) && defined(USE_AURA)
+#define MAYBE_DragToSeparateWindow DISABLED_DragToSeparateWindow
+#else
+#define MAYBE_DragToSeparateWindow DragToSeparateWindow
+#endif
+
 // Creates two browsers, drags from first into second.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
-                       DragToSeparateWindow) {
+                       MAYBE_DragToSeparateWindow) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Add another tab to browser().
diff --git a/chrome/browser/ui/views/task_manager_view.cc b/chrome/browser/ui/views/task_manager_view.cc
index 65195cd..b93603f 100644
--- a/chrome/browser/ui/views/task_manager_view.cc
+++ b/chrome/browser/ui/views/task_manager_view.cc
@@ -46,6 +46,9 @@
 #endif
 
 #if defined(OS_WIN)
+#include "chrome/browser/shell_integration.h"
+#include "ui/base/win/shell.h"
+#include "ui/views/win/hwnd_util.h"
 #include "win8/util/win8_util.h"
 #endif
 
@@ -509,6 +512,17 @@
   DialogDelegate::CreateDialogWidget(instance_, window, NULL);
   instance_->InitAlwaysOnTopState();
   instance_->model_->StartUpdating();
+#if defined(OS_WIN)
+  // Set the app id for the task manager to the app id of its parent browser. If
+  // no parent is specified, the app id will default to that of the initial
+  // process.
+  if (browser) {
+    ui::win::SetAppIdForWindow(
+        ShellIntegration::GetChromiumModelIdForProfile(
+            browser->profile()->GetPath()),
+        views::HWNDForWidget(instance_->GetWidget()));
+  }
+#endif
   instance_->GetWidget()->Show();
 
   // Set the initial focus to the list of tasks.
diff --git a/chrome/browser/ui/webui/bidi_checker_web_ui_test.cc b/chrome/browser/ui/webui/bidi_checker_web_ui_test.cc
index 5c0ef3a..2e6229c 100644
--- a/chrome/browser/ui/webui/bidi_checker_web_ui_test.cc
+++ b/chrome/browser/ui/webui/bidi_checker_web_ui_test.cc
@@ -193,27 +193,6 @@
 }
 
 //==============================
-// chrome://bugreport
-//==============================
-
-IN_PROC_BROWSER_TEST_F(WebUIBidiCheckerBrowserTestLTR, TestFeedbackPage) {
-  // The bugreport page receives its contents as GET arguments. Here we provide
-  // a custom, Hebrew typed, description message.
-  RunBidiCheckerOnPage(
-      "chrome://feedback?session_id=1&tab_index=0&description=%D7%91%D7%93%D7%99%D7%A7%D7%94");
-}
-
-// Test disabled because it is flaky (http://crbug.com/134434)
-#if defined(OS_CHROMEOS)
-#define MAYBE_TestFeedbackPage DISABLED_TestFeedbackPage
-#else
-#define MAYBE_TestFeedbackPage TestFeedbackPage
-#endif
-IN_PROC_BROWSER_TEST_F(WebUIBidiCheckerBrowserTestRTL, MAYBE_TestFeedbackPage) {
-  RunBidiCheckerOnPage("chrome://feedback?session_id=1&tab_index=0&description=test");
-}
-
-//==============================
 // chrome://crashes
 //==============================
 
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index b73b847..96fa1f9 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/ui/webui/downloads_ui.h"
 #include "chrome/browser/ui/webui/extensions/extension_info_ui.h"
 #include "chrome/browser/ui/webui/extensions/extensions_ui.h"
-#include "chrome/browser/ui/webui/feedback_ui.h"
 #include "chrome/browser/ui/webui/flags_ui.h"
 #include "chrome/browser/ui/webui/flash_ui.h"
 #include "chrome/browser/ui/webui/help/help_ui.h"
@@ -152,9 +151,9 @@
 }
 
 template<>
-WebUIController* NewWebUI<ExtensionInfoUI>(WebUI* web_ui,
-                                           const GURL& url) {
-  return new ExtensionInfoUI(web_ui, url);
+WebUIController* NewWebUI<extensions::ExtensionInfoUI>(WebUI* web_ui,
+                                                       const GURL& url) {
+  return new extensions::ExtensionInfoUI(web_ui, url);
 }
 
 // Special case for older about: handlers.
@@ -287,9 +286,6 @@
   // Downloads list on Android uses the built-in download manager.
   if (url.host() == chrome::kChromeUIDownloadsHost)
     return &NewWebUI<DownloadsUI>;
-  // Feedback on Android uses the built-in feedback app.
-  if (url.host() == chrome::kChromeUIFeedbackHost)
-    return &NewWebUI<FeedbackUI>;
   // Flash is not available on android.
   if (url.host() == chrome::kChromeUIFlashHost)
     return &NewWebUI<FlashUI>;
@@ -426,10 +422,10 @@
 #if defined(ENABLE_EXTENSIONS)
   if (url.host() == chrome::kChromeUIExtensionInfoHost &&
       extensions::FeatureSwitch::script_badges()->IsEnabled()) {
-    return &NewWebUI<ExtensionInfoUI>;
+    return &NewWebUI<extensions::ExtensionInfoUI>;
   }
   if (url.host() == chrome::kChromeUIExtensionsFrameHost)
-    return &NewWebUI<ExtensionsUI>;
+    return &NewWebUI<extensions::ExtensionsUI>;
 #endif
 #if defined(ENABLE_PRINTING)
   if (url.host() == chrome::kChromeUIPrintHost &&
@@ -589,9 +585,16 @@
     return DownloadsUI::GetFaviconResourceBytes(scale_factor);
 
   // Android doesn't use the Options pages.
-  if (page_url.host() == chrome::kChromeUISettingsFrameHost)
+  if (page_url.host() == chrome::kChromeUISettingsHost ||
+      page_url.host() == chrome::kChromeUISettingsFrameHost)
     return options::OptionsUI::GetFaviconResourceBytes(scale_factor);
 
+#if defined(ENABLE_EXTENSIONS)
+  if (page_url.host() == chrome::kChromeUIExtensionsHost ||
+      page_url.host() == chrome::kChromeUIExtensionsFrameHost)
+    return extensions::ExtensionsUI::GetFaviconResourceBytes(scale_factor);
+#endif
+
   // Android doesn't use the plugins pages.
   if (page_url.host() == chrome::kChromeUIPluginsHost)
     return PluginsUI::GetFaviconResourceBytes(scale_factor);
diff --git a/chrome/browser/ui/webui/chromeos/about_network.cc b/chrome/browser/ui/webui/chromeos/about_network.cc
index b377709..96e6a36 100644
--- a/chrome/browser/ui/webui/chromeos/about_network.cc
+++ b/chrome/browser/ui/webui/chromeos/about_network.cc
@@ -111,7 +111,7 @@
       WrapWithTD(network->error()) +
       WrapWithTD(network->ip_address()) +
       WrapWithTD(network->security()) +
-      WrapWithTD(network->technology()) +
+      WrapWithTD(network->network_technology()) +
       WrapWithTD(network->activation_state()) +
       WrapWithTD(network->roaming()) +
       WrapWithTD(base::IntToString(network->cellular_out_of_credits())) +
diff --git a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
index 498ecf7..6568946 100644
--- a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
@@ -138,7 +138,7 @@
 
   const drive::PlatformFileInfoProto& file_info = entry.file_info();
   StringAppendF(&out, "  file_info\n");
-  StringAppendF(&out, "    size: %"PRId64"\n", file_info.size());
+  StringAppendF(&out, "    size: %" PRId64 "\n", file_info.size());
   StringAppendF(&out, "    is_directory: %d\n", file_info.is_directory());
   StringAppendF(&out, "    is_symbolic_link: %d\n",
                 file_info.is_symbolic_link());
@@ -177,13 +177,26 @@
     StringAppendF(&out, "  directory_info\n");
     const drive::DirectorySpecificInfo& directory_specific_info =
         entry.directory_specific_info();
-    StringAppendF(&out, "    changestamp: %"PRId64"\n",
+    StringAppendF(&out, "    changestamp: %" PRId64 "\n",
                   directory_specific_info.changestamp());
   }
 
   return out;
 }
 
+std::string SeverityToString(logging::LogSeverity severity) {
+  switch (severity) {
+    case logging::LOG_INFO:
+      return "info";
+    case logging::LOG_WARNING:
+      return "warning";
+    case logging::LOG_ERROR:
+      return "error";
+    default:  // Treat all other higher severities as ERROR.
+      return "error";
+  }
+}
+
 // Class to handle messages from chrome://drive-internals.
 class DriveInternalsWebUIHandler : public content::WebUIMessageHandler {
  public:
@@ -682,10 +695,13 @@
     if (log[i].id <= last_sent_event_id_)
       continue;
 
+    std::string severity = SeverityToString(log[i].severity);
+
     base::DictionaryValue* dict = new DictionaryValue;
     dict->SetString("key",
         google_apis::util::FormatTimeAsStringLocaltime(log[i].when));
-    dict->SetString("value", log[i].what);
+    dict->SetString("value", "[" + severity + "] " + log[i].what);
+    dict->SetString("class", "log-" + severity);
     list.Append(dict);
     last_sent_event_id_ = log[i].id;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc
index 166fcf1..2fd1450 100644
--- a/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
 
+#include "base/logging.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
 #include "content/public/browser/web_ui.h"
@@ -60,7 +61,14 @@
          l10n_util::GetStringUTF16(message_id_b));
 }
 
-BaseScreenHandler::BaseScreenHandler() : page_is_ready_(false) {
+BaseScreenHandler::BaseScreenHandler()
+    : page_is_ready_(false) {
+}
+
+BaseScreenHandler::BaseScreenHandler(const std::string& js_screen_path)
+    : page_is_ready_(false),
+      js_screen_path_prefix_(js_screen_path + ".") {
+  CHECK(!js_screen_path.empty());
 }
 
 BaseScreenHandler::~BaseScreenHandler() {
@@ -81,7 +89,7 @@
 }
 
 void BaseScreenHandler::CallJS(const std::string& method) {
-  web_ui()->CallJavascriptFunction(method);
+  web_ui()->CallJavascriptFunction(FullMethodPath(method));
 }
 
 void BaseScreenHandler::ShowScreen(const char* screen_name,
@@ -99,4 +107,9 @@
   return LoginDisplayHostImpl::default_host()->GetNativeWindow();
 }
 
+std::string BaseScreenHandler::FullMethodPath(const std::string& method) const {
+  DCHECK(!method.empty());
+  return js_screen_path_prefix_ + method;
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
index 0142c6a..35b333d 100644
--- a/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
@@ -78,7 +78,12 @@
 // Base class for the OOBE/Login WebUI handlers.
 class BaseScreenHandler : public content::WebUIMessageHandler {
  public:
+  // C-tor used when JS screen prefix is not needed.
   BaseScreenHandler();
+
+  // C-tor used when JS screen prefix is needed.
+  explicit BaseScreenHandler(const std::string& js_screen_path);
+
   virtual ~BaseScreenHandler();
 
   // Gets localized strings to be used on the page.
@@ -103,12 +108,13 @@
 
   template<typename A1>
   void CallJS(const std::string& method, const A1& arg1) {
-    web_ui()->CallJavascriptFunction(method, MakeValue(arg1));
+    web_ui()->CallJavascriptFunction(FullMethodPath(method), MakeValue(arg1));
   }
 
   template<typename A1, typename A2>
   void CallJS(const std::string& method, const A1& arg1, const A2& arg2) {
-    web_ui()->CallJavascriptFunction(method, MakeValue(arg1), MakeValue(arg2));
+    web_ui()->CallJavascriptFunction(FullMethodPath(method), MakeValue(arg1),
+                                     MakeValue(arg2));
   }
 
   template<typename A1, typename A2, typename A3>
@@ -116,7 +122,7 @@
               const A1& arg1,
               const A2& arg2,
               const A3& arg3) {
-    web_ui()->CallJavascriptFunction(method,
+    web_ui()->CallJavascriptFunction(FullMethodPath(method),
                                      MakeValue(arg1),
                                      MakeValue(arg2),
                                      MakeValue(arg3));
@@ -128,7 +134,7 @@
               const A2& arg2,
               const A3& arg3,
               const A4& arg4) {
-    web_ui()->CallJavascriptFunction(method,
+    web_ui()->CallJavascriptFunction(FullMethodPath(method),
                                      MakeValue(arg1),
                                      MakeValue(arg2),
                                      MakeValue(arg3),
@@ -201,11 +207,20 @@
   virtual gfx::NativeWindow GetNativeWindow();
 
  private:
+  // Returns full name of JS method based on screen and method
+  // names.
+  std::string FullMethodPath(const std::string& method) const;
+
   // Keeps whether page is ready.
   bool page_is_ready_;
 
   base::DictionaryValue* localized_values_;
 
+  // Full name of the corresponding JS screen object. Can be empty, if
+  // there are no corresponding screen object or several different
+  // objects.
+  std::string js_screen_path_prefix_;
+
   DISALLOW_COPY_AND_ASSIGN(BaseScreenHandler);
 };
 
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index 5a6a1c9..d07dd47 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -24,6 +24,8 @@
 
 namespace {
 
+const char kJsScreenPath[] = "cr.ui.Oobe";
+
 // JS API callbacks names.
 const char kJsApiEnableHighContrast[] = "enableHighContrast";
 const char kJsApiEnableScreenMagnifier[] = "enableScreenMagnifier";
@@ -39,7 +41,8 @@
 // Note that show_oobe_ui_ defaults to false because WizardController assumes
 // OOBE UI is not visible by default.
 CoreOobeHandler::CoreOobeHandler(OobeUI* oobe_ui)
-    : oobe_ui_(oobe_ui),
+    : BaseScreenHandler(kJsScreenPath),
+      oobe_ui_(oobe_ui),
       show_oobe_ui_(false),
       version_info_updater_(this),
       delegate_(NULL) {
@@ -119,6 +122,63 @@
               &CoreOobeHandler::HandleSetDeviceRequisition);
 }
 
+void CoreOobeHandler::ShowSignInError(
+    int login_attempts,
+    const std::string& error_text,
+    const std::string& help_link_text,
+    HelpAppLauncher::HelpTopic help_topic_id) {
+  CallJS("showSignInError", login_attempts, error_text,
+         help_link_text, static_cast<int>(help_topic_id));
+}
+
+void CoreOobeHandler::ShowTpmError() {
+  CallJS("showTpmError");
+}
+
+void CoreOobeHandler::ShowSignInUI(const std::string& email) {
+  CallJS("showSigninUI", email);
+}
+
+void CoreOobeHandler::ResetSignInUI(bool force_online) {
+  CallJS("resetSigninUI", force_online);
+}
+
+void CoreOobeHandler::ClearUserPodPassword() {
+  CallJS("clearUserPodPassword");
+}
+
+void CoreOobeHandler::RefocusCurrentPod() {
+  CallJS("refocusCurrentPod");
+}
+
+void CoreOobeHandler::OnLoginSuccess(const std::string& username) {
+  CallJS("onLoginSuccess", username);
+}
+
+void CoreOobeHandler::ShowPasswordChangedScreen(bool show_password_error) {
+  CallJS("showPasswordChangedScreen", show_password_error);
+}
+
+void CoreOobeHandler::SetUsageStats(bool checked) {
+  CallJS("setUsageStats", checked);
+}
+
+void CoreOobeHandler::SetOemEulaUrl(const std::string& oem_eula_url) {
+  CallJS("setOemEulaUrl", oem_eula_url);
+}
+
+void CoreOobeHandler::SetTpmPassword(const std::string& tpm_password) {
+  CallJS("setTpmPassword", tpm_password);
+}
+
+void CoreOobeHandler::ClearErrors() {
+  CallJS("clearErrors");
+}
+
+void CoreOobeHandler::ReloadContent(const base::DictionaryValue& dictionary) {
+  CallJS("reloadContent", dictionary);
+}
+
 void CoreOobeHandler::HandleInitialized() {
   oobe_ui_->InitializeHandlers();
 }
@@ -183,7 +243,7 @@
                        AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
   a11y_info.SetBoolean("screenMagnifierEnabled",
                        MagnificationManager::Get()->IsMagnifierEnabled());
-  CallJS("cr.ui.Oobe.refreshA11yInfo", a11y_info);
+  CallJS("refreshA11yInfo", a11y_info);
 }
 
 void CoreOobeHandler::UpdateOobeUIVisibility() {
@@ -194,10 +254,10 @@
       channel == chrome::VersionInfo::CHANNEL_BETA) {
     should_show_version = false;
   }
-  CallJS("cr.ui.Oobe.showVersion", should_show_version);
-  CallJS("cr.ui.Oobe.showOobeUI", show_oobe_ui_);
+  CallJS("showVersion", should_show_version);
+  CallJS("showOobeUI", show_oobe_ui_);
   if (system::keyboard_settings::ForceKeyboardDrivenUINavigation())
-    CallJS("cr.ui.Oobe.enableKeyboardFlow", true);
+    CallJS("enableKeyboardFlow", true);
 }
 
 void CoreOobeHandler::OnOSVersionLabelTextUpdated(
@@ -212,16 +272,16 @@
 
 void CoreOobeHandler::OnEnterpriseInfoUpdated(
     const std::string& message_text) {
-  CallJS("cr.ui.Oobe.setEnterpriseInfo", message_text);
+  CallJS("setEnterpriseInfo", message_text);
 }
 
 void CoreOobeHandler::UpdateLabel(const std::string& id,
                                   const std::string& text) {
-  CallJS("cr.ui.Oobe.setLabelText", id, text);
+  CallJS("setLabelText", id, text);
 }
 
 void CoreOobeHandler::UpdateDeviceRequisition() {
-  CallJS("cr.ui.Oobe.updateDeviceRequisition",
+  CallJS("updateDeviceRequisition",
          g_browser_process->browser_policy_connector()->
              GetDeviceCloudPolicyManager()->GetDeviceRequisition());
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
index 087a099..7ecd9e6 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
 #include "chrome/browser/chromeos/login/version_info_updater.h"
 #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
 #include "content/public/browser/notification_observer.h"
@@ -19,7 +20,8 @@
 // The core handler for Javascript messages related to the "oobe" view.
 class CoreOobeHandler : public BaseScreenHandler,
                         public VersionInfoUpdater::Delegate,
-                        public content::NotificationObserver {
+                        public content::NotificationObserver,
+                        public CoreOobeActor {
  public:
   class Delegate {
    public:
@@ -55,6 +57,25 @@
   }
 
  private:
+  // CoreOobeActor implementation:
+  virtual void ShowSignInError(
+      int login_attempts,
+      const std::string& error_text,
+      const std::string& help_link_text,
+      HelpAppLauncher::HelpTopic help_topic_id) OVERRIDE;
+  virtual void ShowTpmError() OVERRIDE;
+  virtual void ShowSignInUI(const std::string& email) OVERRIDE;
+  virtual void ResetSignInUI(bool force_online) OVERRIDE;
+  virtual void ClearUserPodPassword() OVERRIDE;
+  virtual void RefocusCurrentPod() OVERRIDE;
+  virtual void OnLoginSuccess(const std::string& username) OVERRIDE;
+  virtual void ShowPasswordChangedScreen(bool show_password_error) OVERRIDE;
+  virtual void SetUsageStats(bool checked) OVERRIDE;
+  virtual void SetOemEulaUrl(const std::string& oem_eula_url) OVERRIDE;
+  virtual void SetTpmPassword(const std::string& tmp_password) OVERRIDE;
+  virtual void ClearErrors() OVERRIDE;
+  virtual void ReloadContent(const base::DictionaryValue& dictionary) OVERRIDE;
+
   // Handlers for JS WebUI messages.
   void HandleEnableLargeCursor(bool enabled);
   void HandleEnableHighContrast(bool enabled);
diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
index 95d3d6d..589564b 100644
--- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
@@ -29,6 +29,8 @@
 
 namespace {
 
+const char kJsScreenPath[] = "login.OAuthEnrollmentScreen";
+
 // Start page of GAIA authentication extension.
 const char kGaiaExtStartPage[] =
     "chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik/main.html";
@@ -74,7 +76,8 @@
 // EnrollmentScreenHandler, public ------------------------------
 
 EnrollmentScreenHandler::EnrollmentScreenHandler()
-    : controller_(NULL),
+    : BaseScreenHandler(kJsScreenPath),
+      controller_(NULL),
       show_on_init_(false),
       is_auto_enrollment_(false),
       can_exit_enrollment_(true),
@@ -353,7 +356,7 @@
 }
 
 void EnrollmentScreenHandler::ShowStep(const char* step) {
-  CallJS("login.OAuthEnrollmentScreen.showStep", std::string(step));
+  CallJS("showStep", std::string(step));
 }
 
 void EnrollmentScreenHandler::ShowError(int message_id,
@@ -364,12 +367,11 @@
 void EnrollmentScreenHandler::ShowErrorMessage(
     const std::string& message,
     bool retry) {
-  CallJS("login.OAuthEnrollmentScreen.showError", message, retry);
+  CallJS("showError", message, retry);
 }
 
 void EnrollmentScreenHandler::ShowWorking(int message_id) {
-  CallJS("login.OAuthEnrollmentScreen.showWorking",
-         l10n_util::GetStringUTF16(message_id));
+  CallJS("showWorking", l10n_util::GetStringUTF16(message_id));
 }
 
 void EnrollmentScreenHandler::OnTokenFetched(
diff --git a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
index 88e1bf4..bc47831 100644
--- a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
@@ -14,6 +14,12 @@
 #include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
 #include "grit/generated_resources.h"
 
+namespace {
+
+const char kJsScreenPath[] = "login.ErrorMessageScreen";
+
+}  // namespace
+
 namespace chromeos {
 
 namespace {
@@ -34,7 +40,8 @@
 
 ErrorScreenHandler::ErrorScreenHandler(
     const scoped_refptr<NetworkStateInformer>& network_state_informer)
-    : network_state_informer_(network_state_informer),
+    : BaseScreenHandler(kJsScreenPath),
+      network_state_informer_(network_state_informer),
       show_on_init_(false) {
   DCHECK(network_state_informer_.get());
 }
@@ -86,22 +93,21 @@
 
 void ErrorScreenHandler::SetUIState(ErrorScreen::UIState ui_state) {
   ui_state_ = ui_state;
-  CallJS("login.ErrorMessageScreen.setUIState", static_cast<int>(ui_state_));
+  CallJS("setUIState", static_cast<int>(ui_state_));
 }
 
 void ErrorScreenHandler::SetErrorState(ErrorScreen::ErrorState error_state,
                                        const std::string& network) {
   error_state_ = error_state;
-  CallJS("login.ErrorMessageScreen.setErrorState",
-         static_cast<int>(error_state_), network);
+  CallJS("setErrorState", static_cast<int>(error_state_), network);
 }
 
 void ErrorScreenHandler::AllowGuestSignin(bool allowed) {
-  CallJS("login.ErrorMessageScreen.allowGuestSignin", allowed);
+  CallJS("allowGuestSignin", allowed);
 }
 
 void ErrorScreenHandler::AllowOfflineLogin(bool allowed) {
-  CallJS("login.ErrorMessageScreen.allowOfflineLogin", allowed);
+  CallJS("allowOfflineLogin", allowed);
 }
 
 void ErrorScreenHandler::NetworkErrorShown() {
diff --git a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
index 2ac4d4d..3c12fda 100644
--- a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "chrome/browser/chromeos/login/help_app_launcher.h"
+#include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
 #include "chrome/browser/chromeos/login/webui_login_display.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "chrome/common/url_constants.h"
@@ -15,10 +16,19 @@
 #include "grit/generated_resources.h"
 #include "ui/views/widget/widget.h"
 
+namespace {
+
+const char kJsScreenPath[] = "login.EulaScreen";
+
+}  // namespace
+
 namespace chromeos {
 
-EulaScreenHandler::EulaScreenHandler()
-    : delegate_(NULL), show_on_init_(false) {
+EulaScreenHandler::EulaScreenHandler(CoreOobeActor* core_oobe_actor)
+    : BaseScreenHandler(kJsScreenPath),
+      delegate_(NULL),
+      core_oobe_actor_(core_oobe_actor),
+      show_on_init_(false) {
 }
 
 EulaScreenHandler::~EulaScreenHandler() {
@@ -83,14 +93,12 @@
   if (!page_is_ready() || !delegate_)
     return;
 
-  CallJS("cr.ui.Oobe.setUsageStats", delegate_->IsUsageStatsEnabled());
+  core_oobe_actor_->SetUsageStats(delegate_->IsUsageStatsEnabled());
 
   // This OEM EULA is a file:// URL which we're unable to load in iframe.
   // Instead if it's defined we use chrome://terms/oem that will load same file.
-  if (!delegate_->GetOemEulaUrl().is_empty()) {
-    CallJS("cr.ui.Oobe.setOemEulaUrl",
-           std::string(chrome::kChromeUITermsOemURL));
-  }
+  if (!delegate_->GetOemEulaUrl().is_empty())
+    core_oobe_actor_->SetOemEulaUrl(chrome::kChromeUITermsOemURL);
 
   if (show_on_init_) {
     Show();
@@ -106,7 +114,7 @@
 }
 
 void EulaScreenHandler::OnPasswordFetched(const std::string& tpm_password) {
-  CallJS("cr.ui.Oobe.setTpmPassword", tpm_password);
+  core_oobe_actor_->SetTpmPassword(tpm_password);
 }
 
 void EulaScreenHandler::HandleOnExit(bool accepted, bool usage_stats_enabled) {
diff --git a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
index 766be25..4a8a0c8 100644
--- a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
@@ -18,6 +18,7 @@
 
 namespace chromeos {
 
+class CoreOobeActor;
 class HelpAppLauncher;
 
 // WebUI implementation of EulaScreenActor. It is used to interact
@@ -26,7 +27,7 @@
                           public BaseScreenHandler,
                           public TpmPasswordFetcherDelegate {
  public:
-  EulaScreenHandler();
+  explicit EulaScreenHandler(CoreOobeActor* core_oobe_actor);
   virtual ~EulaScreenHandler();
 
   // EulaScreenActor implementation:
@@ -50,7 +51,8 @@
   void HandleOnLearnMore();
   void HandleOnInstallationSettingsPopupOpened();
 
-  Delegate* delegate_;
+  EulaScreenActor::Delegate* delegate_;
+  CoreOobeActor* core_oobe_actor_;
 
   // Help application used for help dialogs.
   scoped_refptr<HelpAppLauncher> help_app_;
diff --git a/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
index edf9af6..a0564bd 100644
--- a/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
@@ -26,6 +26,8 @@
 
 namespace {
 
+const char kJsScreenPath[] = "login.AutolaunchScreen";
+
 // Autolaunch screen id.
 const char kAutolaunchScreen[] = "autolaunch";
 
@@ -34,7 +36,10 @@
 namespace chromeos {
 
 KioskAutolaunchScreenHandler::KioskAutolaunchScreenHandler()
-    : delegate_(NULL), show_on_init_(false), is_visible_(false) {
+    : BaseScreenHandler(kJsScreenPath),
+      delegate_(NULL),
+      show_on_init_(false),
+      is_visible_(false) {
   KioskAppManager::Get()->AddObserver(this);
 }
 
@@ -81,8 +86,7 @@
     icon_url = webui::GetBitmapDataUrl(*app.icon.bitmap());
 
   app_info.SetString("appIconUrl", icon_url);
-  web_ui()->CallJavascriptFunction("login.AutolaunchScreen.updateApp",
-                                   app_info);
+  CallJS("updateApp", app_info);
 }
 
 void KioskAutolaunchScreenHandler::DeclareLocalizedValues(
diff --git a/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
index 523038c..3e82671 100644
--- a/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
@@ -16,6 +16,8 @@
 
 namespace {
 
+const char kJsScreenPath[] = "login.KioskEnableScreen";
+
 // Reset screen id.
 const char kKioskEnableScreen[] = "kiosk-enable";
 
@@ -24,7 +26,8 @@
 namespace chromeos {
 
 KioskEnableScreenHandler::KioskEnableScreenHandler()
-    : delegate_(NULL),
+    : BaseScreenHandler(kJsScreenPath),
+      delegate_(NULL),
       show_on_init_(false),
       is_configurable_(false),
       weak_ptr_factory_(this) {
@@ -130,9 +133,7 @@
   if (!success)
     LOG(WARNING) << "Consumer kiosk mode can't be enabled!";
 
-  base::FundamentalValue value(success);
-  web_ui()->CallJavascriptFunction("login.KioskEnableScreen.onCompleted",
-                                   value);
+  CallJS("onCompleted", success);
   if (success) {
     content::NotificationService::current()->Notify(
         chrome::NOTIFICATION_KIOSK_ENABLED,
diff --git a/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.cc
index c2a594b..164a96c 100644
--- a/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/locally_managed_user_creation_screen_handler.cc
@@ -20,6 +20,8 @@
 
 namespace {
 
+const char kJsScreenPath[] = "login.LocallyManagedUserCreationScreen";
+
 // Locally managed user creation screen id.
 const char kLocallyManagedUserCreationScreen[] =
     "locally-managed-user-creation";
@@ -29,7 +31,9 @@
 namespace chromeos {
 
 LocallyManagedUserCreationScreenHandler::
-LocallyManagedUserCreationScreenHandler() : delegate_(NULL) {
+LocallyManagedUserCreationScreenHandler()
+    : BaseScreenHandler(kJsScreenPath),
+      delegate_(NULL) {
 }
 
 LocallyManagedUserCreationScreenHandler::
@@ -177,36 +181,35 @@
 void LocallyManagedUserCreationScreenHandler::Hide() {}
 
 void LocallyManagedUserCreationScreenHandler::ShowIntroPage() {
-  CallJS("login.LocallyManagedUserCreationScreen.showIntroPage");
+  CallJS("showIntroPage");
 }
 
 void LocallyManagedUserCreationScreenHandler::ShowManagerPasswordError() {
-  CallJS("login.LocallyManagedUserCreationScreen.showManagerPasswordError");
+  CallJS("showManagerPasswordError");
 }
 
 void LocallyManagedUserCreationScreenHandler::ShowStatusMessage(
     bool is_progress,
     const string16& message) {
   if (is_progress)
-    CallJS("login.LocallyManagedUserCreationScreen.showProgress", message);
+    CallJS("showProgress", message);
   else
-    CallJS("login.LocallyManagedUserCreationScreen.showStatusError", message);
+    CallJS("showStatusError", message);
 }
 
 void LocallyManagedUserCreationScreenHandler::ShowUsernamePage() {
-  CallJS("login.LocallyManagedUserCreationScreen.showUsernamePage");
+  CallJS("showUsernamePage");
 }
 
 void LocallyManagedUserCreationScreenHandler::ShowTutorialPage() {
-  CallJS("login.LocallyManagedUserCreationScreen.showTutorialPage");
+  CallJS("showTutorialPage");
 }
 
 void LocallyManagedUserCreationScreenHandler::ShowErrorPage(
     const string16& title,
     const string16& message,
     const string16& button_text) {
-  CallJS("login.LocallyManagedUserCreationScreen.showErrorPage",
-      title, message, button_text);
+  CallJS("showErrorPage", title, message, button_text);
 }
 
 void LocallyManagedUserCreationScreenHandler::SetDelegate(Delegate* delegate) {
@@ -234,11 +237,11 @@
     const string16& name) {
   if (NULL != UserManager::Get()->
           FindLocallyManagedUser(CollapseWhitespace(name, true))) {
-    CallJS("login.LocallyManagedUserCreationScreen.managedUserNameError",
-           name, l10n_util::GetStringUTF16(
+    CallJS("managedUserNameError", name,
+           l10n_util::GetStringUTF16(
                IDS_CREATE_LOCALLY_MANAGED_USER_CREATE_USERNAME_ALREADY_EXISTS));
   } else {
-    CallJS("login.LocallyManagedUserCreationScreen.managedUserNameOk", name);
+    CallJS("managedUserNameOk", name);
   }
 }
 
@@ -249,8 +252,8 @@
     return;
   const string16 new_user_name = CollapseWhitespace(new_raw_user_name, true);
   if (NULL != UserManager::Get()->FindLocallyManagedUser(new_user_name)) {
-    CallJS("login.LocallyManagedUserCreationScreen.managedUserNameError",
-           new_user_name, l10n_util::GetStringFUTF16(
+    CallJS("managedUserNameError", new_user_name,
+           l10n_util::GetStringFUTF16(
                IDS_CREATE_LOCALLY_MANAGED_USER_CREATE_USERNAME_ALREADY_EXISTS,
                new_user_name));
     return;
@@ -258,7 +261,7 @@
 
   // TODO(antrim): Any other password checks here?
   if (new_user_password.length() == 0) {
-    CallJS("login.LocallyManagedUserCreationScreen.showPasswordError",
+    CallJS("showPasswordError",
            l10n_util::GetStringUTF16(
                IDS_CREATE_LOCALLY_MANAGED_USER_CREATE_PASSWORD_TOO_SHORT));
     return;
@@ -296,7 +299,7 @@
     image_data->SetString("title", GetDefaultImageDescription(i));
     image_urls.Append(image_data.release());
   }
-  CallJS("login.LocallyManagedUserCreationScreen.setDefaultImages", image_urls);
+  CallJS("setDefaultImages", image_urls);
 }
 
 void LocallyManagedUserCreationScreenHandler::HandlePhotoTaken
@@ -324,7 +327,7 @@
 }
 
 void LocallyManagedUserCreationScreenHandler::SetCameraPresent(bool present) {
-  CallJS("login.LocallyManagedUserCreationScreen.setCameraPresent", present);
+  CallJS("setCameraPresent", present);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc b/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc
index e920e32..ebe2463 100644
--- a/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc
@@ -10,6 +10,8 @@
 
 namespace {
 
+const char kJsScreenPath[] = "cr.ui.DropDown";
+
 // JS API callbacks names.
 const char kJsApiNetworkItemChosen[] = "networkItemChosen";
 const char kJsApiNetworkDropdownShow[] = "networkDropdownShow";
@@ -20,7 +22,8 @@
 
 namespace chromeos {
 
-NetworkDropdownHandler::NetworkDropdownHandler() {
+NetworkDropdownHandler::NetworkDropdownHandler()
+    : BaseScreenHandler(kJsScreenPath) {
 }
 
 NetworkDropdownHandler::~NetworkDropdownHandler() {
diff --git a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
index 6a13f8a..4633d8f 100644
--- a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/input_method/input_method_util.h"
 #include "chrome/browser/chromeos/login/language_switch_menu.h"
+#include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.h"
 #include "chromeos/ime/input_method_manager.h"
@@ -21,6 +22,8 @@
 
 namespace {
 
+const char kJsScreenPath[] = "login.NetworkScreen";
+
 // JS API callbacks names.
 const char kJsApiNetworkOnExit[] = "networkOnExit";
 const char kJsApiNetworkOnLanguageChanged[] = "networkOnLanguageChanged";
@@ -32,10 +35,13 @@
 
 // NetworkScreenHandler, public: -----------------------------------------------
 
-NetworkScreenHandler::NetworkScreenHandler()
-    : screen_(NULL),
+NetworkScreenHandler::NetworkScreenHandler(CoreOobeActor* core_oobe_actor)
+    : BaseScreenHandler(kJsScreenPath),
+      screen_(NULL),
+      core_oobe_actor_(core_oobe_actor),
       is_continue_enabled_(false),
       show_on_init_(false) {
+  DCHECK(core_oobe_actor_);
 }
 
 NetworkScreenHandler::~NetworkScreenHandler() {
@@ -65,12 +71,12 @@
 }
 
 void NetworkScreenHandler::ShowError(const string16& message) {
-  CallJS("login.NetworkScreen.showError", message);
+  CallJS("showError", message);
 }
 
 void NetworkScreenHandler::ClearErrors() {
   if (page_is_ready())
-    CallJS("cr.ui.Oobe.clearErrors");
+    core_oobe_actor_->ClearErrors();
 }
 
 void NetworkScreenHandler::ShowConnectingStatus(
@@ -88,7 +94,7 @@
 void NetworkScreenHandler::EnableContinue(bool enabled) {
   is_continue_enabled_ = enabled;
   if (page_is_ready())
-    CallJS("login.NetworkScreen.enableContinueButton", enabled);
+    CallJS("enableContinueButton", enabled);
 }
 
 // NetworkScreenHandler, BaseScreenHandler implementation: --------------------
@@ -147,7 +153,8 @@
   DictionaryValue localized_strings;
   static_cast<OobeUI*>(web_ui()->GetController())->GetLocalizedStrings(
       &localized_strings);
-  CallJS("cr.ui.Oobe.reloadContent", localized_strings);
+  core_oobe_actor_->ReloadContent(localized_strings);
+
   // Buttons are recreated, updated "Continue" button state.
   EnableContinue(is_continue_enabled_);
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h
index 49d8265..ec190d0 100644
--- a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h
@@ -14,12 +14,14 @@
 
 namespace chromeos {
 
+class CoreOobeActor;
+
 // WebUI implementation of NetworkScreenActor. It is used to interact with
 // the welcome screen (part of the page) of the OOBE.
 class NetworkScreenHandler : public NetworkScreenActor,
                              public BaseScreenHandler {
  public:
-  NetworkScreenHandler();
+  explicit NetworkScreenHandler(CoreOobeActor* core_oobe_actor);
   virtual ~NetworkScreenHandler();
 
   // NetworkScreenActor implementation:
@@ -60,6 +62,7 @@
   static base::ListValue* GetInputMethods();
 
   NetworkScreenActor::Delegate* screen_;
+  CoreOobeActor* core_oobe_actor_;
 
   bool is_continue_enabled_;
 
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index 120986e..a018ffa 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -175,11 +175,12 @@
   update_screen_handler_ = new UpdateScreenHandler();
   AddScreenHandler(update_screen_handler_);
 
-  NetworkScreenHandler* network_screen_handler = new NetworkScreenHandler();
+  NetworkScreenHandler* network_screen_handler =
+      new NetworkScreenHandler(core_handler_);
   network_screen_actor_ = network_screen_handler;
   AddScreenHandler(network_screen_handler);
 
-  EulaScreenHandler* eula_screen_handler = new EulaScreenHandler();
+  EulaScreenHandler* eula_screen_handler = new EulaScreenHandler(core_handler_);
   eula_screen_actor_ = eula_screen_handler;
   AddScreenHandler(eula_screen_handler);
 
@@ -228,7 +229,8 @@
   AddScreenHandler(error_screen_handler_);
 
   signin_screen_handler_ = new SigninScreenHandler(network_state_informer_,
-                                                   error_screen_handler_);
+                                                   error_screen_handler_,
+                                                   core_handler_);
   AddScreenHandler(signin_screen_handler_);
 
   // Initialize KioskAppMenuHandler. Note that it is NOT a screen handler.
diff --git a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
index 94f7307..535842b 100644
--- a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
@@ -21,6 +21,8 @@
 
 namespace {
 
+const char kJsScreenPath[] = "login.ResetScreen";
+
 // Reset screen id.
 const char kResetScreen[] = "reset";
 
@@ -29,7 +31,9 @@
 namespace chromeos {
 
 ResetScreenHandler::ResetScreenHandler()
-    : delegate_(NULL), show_on_init_(false) {
+    : BaseScreenHandler(kJsScreenPath),
+      delegate_(NULL),
+      show_on_init_(false) {
 }
 
 ResetScreenHandler::~ResetScreenHandler() {
diff --git a/chrome/browser/ui/webui/chromeos/login/screen_manager_handler.cc b/chrome/browser/ui/webui/chromeos/login/screen_manager_handler.cc
index 161c7ce..2070f6f 100644
--- a/chrome/browser/ui/webui/chromeos/login/screen_manager_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/screen_manager_handler.cc
@@ -13,7 +13,8 @@
 
 namespace chromeos {
 
-ScreenManagerHandler::ScreenManagerHandler() : delegate_(NULL) {
+ScreenManagerHandler::ScreenManagerHandler()
+    : delegate_(NULL) {
 }
 
 ScreenManagerHandler::~ScreenManagerHandler() {
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 83257da..c3c4831 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/chromeos/login/hwid_checker.h"
 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
 #include "chrome/browser/chromeos/login/screen_locker.h"
+#include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
 #include "chrome/browser/chromeos/login/user.h"
 #include "chrome/browser/chromeos/login/webui_login_display.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
@@ -381,7 +382,8 @@
 
 SigninScreenHandler::SigninScreenHandler(
     const scoped_refptr<NetworkStateInformer>& network_state_informer,
-    ErrorScreenActor* error_screen_actor)
+    ErrorScreenActor* error_screen_actor,
+    CoreOobeActor* core_oobe_actor)
     : ui_state_(UI_STATE_UNKNOWN),
       frame_state_(FRAME_STATE_UNKNOWN),
       frame_error_(net::OK),
@@ -401,12 +403,14 @@
       webui_visible_(false),
       preferences_changed_delayed_(false),
       error_screen_actor_(error_screen_actor),
+      core_oobe_actor_(core_oobe_actor),
       is_first_update_state_call_(true),
       offline_login_active_(false),
       last_network_state_(NetworkStateInformer::UNKNOWN),
       has_pending_auth_ui_(false) {
   DCHECK(network_state_informer_.get());
   DCHECK(error_screen_actor_);
+  DCHECK(core_oobe_actor_);
   network_state_informer_->AddObserver(this);
   CrosSettings::Get()->AddSettingsObserver(kAccountsPrefAllowNewUser, this);
   CrosSettings::Get()->AddSettingsObserver(kAccountsPrefAllowGuest, this);
@@ -501,11 +505,11 @@
   builder->Add("publicAccountEnter", IDS_LOGIN_PUBLIC_ACCOUNT_ENTER);
   builder->Add("publicAccountEnterAccessibleName",
                IDS_LOGIN_PUBLIC_ACCOUNT_ENTER_ACCESSIBLE_NAME);
-  builder->AddF("removeManagedUserWarningText",
-               IDS_USER_IS_LOCALLY_MANAGED_REMOVE_WARNING,
+  builder->AddF("removeUserWarningText",
+               IDS_LOGIN_POD_USER_REMOVE_WARNING,
                UTF8ToUTF16(chrome::kSupervisedUserManagementDisplayURL));
-  builder->Add("removeManagedUserWarningButtonTitle",
-               IDS_USER_IS_LOCALLY_MANAGED_REMOVE_WARNING_BUTTON);
+  builder->Add("removeUserWarningButtonTitle",
+               IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON);
 
   if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
     builder->Add("demoLoginMessage", IDS_KIOSK_MODE_LOGIN_MESSAGE);
@@ -886,19 +890,19 @@
 }
 
 void SigninScreenHandler::ClearAndEnablePassword() {
-  CallJS("cr.ui.Oobe.resetSigninUI", false);
+  core_oobe_actor_->ResetSignInUI(false);
 }
 
 void SigninScreenHandler::ClearUserPodPassword() {
-  CallJS("cr.ui.Oobe.clearUserPodPassword");
+  core_oobe_actor_->ClearUserPodPassword();
 }
 
 void SigninScreenHandler::RefocusCurrentPod() {
-  CallJS("cr.ui.Oobe.refocusCurrentPod");
+  core_oobe_actor_->RefocusCurrentPod();
 }
 
 void SigninScreenHandler::OnLoginSuccess(const std::string& username) {
-  CallJS("cr.ui.Oobe.onLoginSuccess", username);
+  core_oobe_actor_->OnLoginSuccess(username);
 }
 
 void SigninScreenHandler::OnUserRemoved(const std::string& username) {
@@ -936,14 +940,14 @@
                                     const std::string& error_text,
                                     const std::string& help_link_text,
                                     HelpAppLauncher::HelpTopic help_topic_id) {
-  CallJS("cr.ui.Oobe.showSignInError", login_attempts, error_text,
-         help_link_text, static_cast<int>(help_topic_id));
+  core_oobe_actor_->ShowSignInError(login_attempts, error_text, help_link_text,
+                                    help_topic_id);
 }
 
 void SigninScreenHandler::ShowErrorScreen(LoginDisplay::SigninError error_id) {
   switch (error_id) {
     case LoginDisplay::TPM_ERROR:
-      CallJS("cr.ui.Oobe.showTpmError");
+      core_oobe_actor_->ShowTpmError();
       break;
     default:
       NOTREACHED() << "Unknown sign in error";
@@ -952,18 +956,18 @@
 }
 
 void SigninScreenHandler::ShowSigninUI(const std::string& email) {
-  CallJS("cr.ui.Oobe.showSigninUI", email);
+
 }
 
 void SigninScreenHandler::ShowGaiaPasswordChanged(const std::string& username) {
   email_ = username;
   password_changed_for_.insert(email_);
-  CallJS("cr.ui.Oobe.showSigninUI", email_);
+  core_oobe_actor_->ShowSignInUI(email_);
   CallJS("login.AccountPickerScreen.updateUserGaiaNeeded", email_);
 }
 
 void SigninScreenHandler::ShowPasswordChangedDialog(bool show_password_error) {
-  CallJS("cr.ui.Oobe.showPasswordChangedScreen", show_password_error);
+  core_oobe_actor_->ShowPasswordChangedScreen(show_password_error);
 }
 
 void SigninScreenHandler::ShowSigninScreenForCreds(
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index 5768ab2..f5fbd6b 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -34,6 +34,7 @@
 namespace chromeos {
 
 class CaptivePortalWindowProxy;
+class CoreOobeActor;
 class LocallyManagedUserCreationScreenHandler;
 class NativeWindowDelegate;
 class User;
@@ -171,7 +172,8 @@
  public:
   SigninScreenHandler(
       const scoped_refptr<NetworkStateInformer>& network_state_informer,
-      ErrorScreenActor* error_screen_actor);
+      ErrorScreenActor* error_screen_actor,
+      CoreOobeActor* core_oobe_actor);
   virtual ~SigninScreenHandler();
 
   // Shows the sign in screen. |oobe_ui| indicates whether the signin
@@ -453,6 +455,7 @@
   bool preferences_changed_delayed_;
 
   ErrorScreenActor* error_screen_actor_;
+  CoreOobeActor* core_oobe_actor_;
 
   bool is_first_update_state_call_;
   bool offline_login_active_;
diff --git a/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
index 61daf34..6af88b9 100644
--- a/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
@@ -8,10 +8,17 @@
 #include "grit/chromium_strings.h"
 #include "grit/generated_resources.h"
 
+namespace {
+
+const char kJsScreenPath[] = "login.TermsOfServiceScreen";
+
+}  // namespace
+
 namespace chromeos {
 
 TermsOfServiceScreenHandler::TermsOfServiceScreenHandler()
-    : screen_(NULL),
+    : BaseScreenHandler(kJsScreenPath),
+      screen_(NULL),
       show_on_init_(false),
       load_error_(false) {
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
index 59dae51..fdda13b 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
@@ -11,10 +11,17 @@
 #include "grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
+namespace {
+
+const char kJsScreenPath[] = "login.UpdateScreen";
+
+}  // namespace
+
 namespace chromeos {
 
 UpdateScreenHandler::UpdateScreenHandler()
-    : screen_(NULL),
+    : BaseScreenHandler(kJsScreenPath),
+      screen_(NULL),
       show_on_init_(false) {
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
index 7eb604a..44cb018 100644
--- a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
@@ -19,10 +19,17 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
+namespace {
+
+const char kJsScreenPath[] = "login.UserImageScreen";
+
+}  // namespace
+
 namespace chromeos {
 
 UserImageScreenHandler::UserImageScreenHandler()
-    : screen_(NULL),
+    : BaseScreenHandler(kJsScreenPath),
+      screen_(NULL),
       show_on_init_(false) {
 }
 
@@ -99,18 +106,18 @@
 
 void UserImageScreenHandler::SelectImage(int index) {
   if (page_is_ready())
-    CallJS("login.UserImageScreen.setSelectedImage", GetDefaultImageUrl(index));
+    CallJS("setSelectedImage", GetDefaultImageUrl(index));
 }
 
 void UserImageScreenHandler::SendProfileImage(const std::string& data_url) {
   if (page_is_ready())
-    CallJS("login.UserImageScreen.setProfileImage", data_url);
+    CallJS("setProfileImage", data_url);
 }
 
 void UserImageScreenHandler::OnProfileImageAbsent() {
   if (page_is_ready()) {
     scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
-    CallJS("login.UserImageScreen.setProfileImage", *null_value);
+    CallJS("setProfileImage", *null_value);
   }
 }
 
@@ -127,7 +134,7 @@
     image_data->SetString("title", GetDefaultImageDescription(i));
     image_urls.Append(image_data.release());
   }
-  CallJS("login.UserImageScreen.setDefaultImages", image_urls);
+  CallJS("setDefaultImages", image_urls);
   if (!screen_)
     return;
   if (screen_->selected_image() != User::kInvalidImageIndex)
@@ -175,11 +182,11 @@
 }
 
 void UserImageScreenHandler::SetCameraPresent(bool present) {
-  CallJS("login.UserImageScreen.setCameraPresent", present);
+  CallJS("setCameraPresent", present);
 }
 
 void UserImageScreenHandler::SetProfilePictureEnabled(bool enabled) {
-  CallJS("login.UserImageScreen.setProfilePictureEnabled", enabled);
+  CallJS("setProfilePictureEnabled", enabled);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
index 3f11abb..1fa895e 100644
--- a/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
@@ -8,10 +8,18 @@
 #include "grit/chromium_strings.h"
 #include "grit/generated_resources.h"
 
+namespace {
+
+const char kJsScreenPath[] = "login.WrongHWIDScreen";
+
+}  // namespace
+
 namespace chromeos {
 
 WrongHWIDScreenHandler::WrongHWIDScreenHandler()
-    : delegate_(NULL), show_on_init_(false) {
+    : BaseScreenHandler(kJsScreenPath),
+      delegate_(NULL),
+      show_on_init_(false) {
 }
 
 WrongHWIDScreenHandler::~WrongHWIDScreenHandler() {
@@ -70,4 +78,3 @@
 }
 
 }  // namespace chromeos
-
diff --git a/chrome/browser/ui/webui/downloads_dom_handler.cc b/chrome/browser/ui/webui/downloads_dom_handler.cc
index d31964b..f107e2c 100644
--- a/chrome/browser/ui/webui/downloads_dom_handler.cc
+++ b/chrome/browser/ui/webui/downloads_dom_handler.cc
@@ -30,6 +30,9 @@
 #include "chrome/browser/download/download_service.h"
 #include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/download/download_util.h"
+#include "chrome/browser/extensions/api/downloads/downloads_api.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/fileicon_source.h"
@@ -135,6 +138,21 @@
   file_value->SetString("file_url",
                         net::FilePathToFileURL(download_path).spec());
 
+  DownloadedByExtension* by_ext = DownloadedByExtension::Get(download_item);
+  if (by_ext) {
+    file_value->SetString("by_ext_id", by_ext->id());
+    file_value->SetString("by_ext_name", by_ext->name());
+    // Lookup the extension's current name() in case the user changed their
+    // language. This won't work if the extension was uninstalled, so the name
+    // might be the wrong language.
+    bool include_disabled = true;
+    const extensions::Extension* extension = extensions::ExtensionSystem::Get(
+        Profile::FromBrowserContext(download_item->GetBrowserContext()))->
+      extension_service()->GetExtensionById(by_ext->id(), include_disabled);
+    if (extension)
+      file_value->SetString("by_ext_name", extension->name());
+  }
+
   // Keep file names as LTR.
   string16 file_name =
     download_item->GetFileNameToReportUser().LossyDisplayName();
diff --git a/chrome/browser/ui/webui/downloads_ui.cc b/chrome/browser/ui/webui/downloads_ui.cc
index b90b28c..ebb01f3 100644
--- a/chrome/browser/ui/webui/downloads_ui.cc
+++ b/chrome/browser/ui/webui/downloads_ui.cc
@@ -24,6 +24,7 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "grit/browser_resources.h"
+#include "grit/chromium_strings.h"
 #include "grit/generated_resources.h"
 #include "grit/theme_resources.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -60,7 +61,7 @@
   source->AddLocalizedString("danger_uncommon_desc",
                              IDS_PROMPT_UNCOMMON_DOWNLOAD_CONTENT);
   source->AddLocalizedString("danger_potentially_unwanted_desc",
-                             IDS_PROMPT_POTENTIALLY_UNWANTED_DOWNLOAD);
+                             IDS_PROMPT_DOWNLOAD_CHANGES_SEARCH_SETTINGS);
   source->AddLocalizedString("danger_save", IDS_CONFIRM_DOWNLOAD);
   source->AddLocalizedString("danger_discard", IDS_DISCARD_DOWNLOAD);
 
@@ -74,6 +75,8 @@
   source->AddLocalizedString("control_resume", IDS_DOWNLOAD_LINK_RESUME);
   source->AddLocalizedString("control_removefromlist",
                              IDS_DOWNLOAD_LINK_REMOVE);
+  source->AddLocalizedString("control_by_extension",
+                             IDS_DOWNLOAD_BY_EXTENSION);
 
   PrefService* prefs = profile->GetPrefs();
   source->AddBoolean("allow_deleting_history",
diff --git a/chrome/browser/ui/webui/extensions/extension_icon_source.cc b/chrome/browser/ui/webui/extensions/extension_icon_source.cc
index a25f0e3..22dcf45 100644
--- a/chrome/browser/ui/webui/extensions/extension_icon_source.cc
+++ b/chrome/browser/ui/webui/extensions/extension_icon_source.cc
@@ -37,6 +37,8 @@
 #include "ui/gfx/skbitmap_operations.h"
 #include "url/gurl.h"
 
+namespace extensions {
+
 namespace {
 
 scoped_refptr<base::RefCountedMemory> BitmapToMemory(const SkBitmap* image) {
@@ -59,29 +61,26 @@
 
 }  // namespace
 
-ExtensionIconSource::ExtensionIconSource(Profile* profile)
-    : profile_(profile) {
+ExtensionIconSource::ExtensionIconSource(Profile* profile) : profile_(profile) {
 }
 
 struct ExtensionIconSource::ExtensionIconRequest {
   content::URLDataSource::GotDataCallback callback;
-  scoped_refptr<const extensions::Extension> extension;
+  scoped_refptr<const Extension> extension;
   bool grayscale;
   int size;
   ExtensionIconSet::MatchType match;
 };
 
 // static
-GURL ExtensionIconSource::GetIconURL(const extensions::Extension* extension,
+GURL ExtensionIconSource::GetIconURL(const Extension* extension,
                                      int icon_size,
                                      ExtensionIconSet::MatchType match,
                                      bool grayscale,
                                      bool* exists) {
-  if (exists)
-    *exists = true;
-  if (exists && extensions::IconsInfo::GetIconURL(
-          extension, icon_size, match) == GURL()) {
-    *exists = false;
+  if (exists) {
+    *exists =
+        IconsInfo::GetIconURL(extension, icon_size, match) != GURL::EmptyGURL();
   }
 
   GURL icon_url(base::StringPrintf("%s%s/%d/%d%s",
@@ -134,7 +133,7 @@
   }
 
   ExtensionIconRequest* request = GetData(next_id);
-  extensions::ExtensionResource icon = extensions::IconsInfo::GetIconResource(
+  ExtensionResource icon = IconsInfo::GetIconResource(
       request->extension, request->size, request->match);
 
   if (icon.relative_path().empty()) {
@@ -200,11 +199,10 @@
   FinalizeImage(&resized_image, request_id);
 }
 
-void ExtensionIconSource::LoadExtensionImage(
-    const extensions::ExtensionResource& icon,
-    int request_id) {
+void ExtensionIconSource::LoadExtensionImage(const ExtensionResource& icon,
+                                             int request_id) {
   ExtensionIconRequest* request = GetData(request_id);
-  extensions::ImageLoader::Get(profile_)->LoadImageAsync(
+  ImageLoader::Get(profile_)->LoadImageAsync(
       request->extension, icon,
       gfx::Size(request->size, request->size),
       base::Bind(&ExtensionIconSource::OnImageLoaded, AsWeakPtr(), request_id));
@@ -219,8 +217,8 @@
     return;
   }
 
-  GURL favicon_url = extensions::AppLaunchInfo::GetFullLaunchURL(
-      GetData(request_id)->extension);
+  GURL favicon_url =
+      AppLaunchInfo::GetFullLaunchURL(GetData(request_id)->extension);
   favicon_service->GetRawFaviconForURL(
       FaviconService::FaviconForURLParams(
           profile_, favicon_url, chrome::FAVICON, gfx::kFaviconSize),
@@ -262,7 +260,7 @@
 
 void ExtensionIconSource::LoadIconFailed(int request_id) {
   ExtensionIconRequest* request = GetData(request_id);
-  extensions::ExtensionResource icon = extensions::IconsInfo::GetIconResource(
+  ExtensionResource icon = IconsInfo::GetIconResource(
       request->extension, request->size, request->match);
 
   if (request->size == extension_misc::EXTENSION_ICON_BITTY)
@@ -304,9 +302,8 @@
     match_type = ExtensionIconSet::MATCH_EXACTLY;
 
   std::string extension_id = path_parts.at(0);
-  const extensions::Extension* extension =
-      extensions::ExtensionSystem::Get(profile_)->extension_service()->
-          GetInstalledExtension(extension_id);
+  const Extension* extension = ExtensionSystem::Get(profile_)->
+      extension_service()->GetInstalledExtension(extension_id);
   if (!extension)
     return false;
 
@@ -320,7 +317,7 @@
 void ExtensionIconSource::SetData(
     int request_id,
     const content::URLDataSource::GotDataCallback& callback,
-    const extensions::Extension* extension,
+    const Extension* extension,
     bool grayscale,
     int size,
     ExtensionIconSet::MatchType match) {
@@ -347,3 +344,5 @@
   delete i->second;
   request_map_.erase(i);
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/ui/webui/extensions/extension_icon_source.h b/chrome/browser/ui/webui/extensions/extension_icon_source.h
index a238551..7a94d11 100644
--- a/chrome/browser/ui/webui/extensions/extension_icon_source.h
+++ b/chrome/browser/ui/webui/extensions/extension_icon_source.h
@@ -22,7 +22,6 @@
 
 namespace extensions {
 class Extension;
-}
 
 // ExtensionIconSource serves extension icons through network level chrome:
 // requests. Icons can be retrieved for any installed extension or app.
@@ -60,7 +59,7 @@
   // desaturated version of the icon. |exists|, if non-NULL, will be set to true
   // if the icon exists; false if it will lead to a default or not-present
   // image.
-  static GURL GetIconURL(const extensions::Extension* extension,
+  static GURL GetIconURL(const Extension* extension,
                          int icon_size,
                          ExtensionIconSet::MatchType match,
                          bool grayscale,
@@ -101,7 +100,7 @@
 
   // Loads the extension's |icon| for the given |request_id| and returns the
   // image to the client.
-  void LoadExtensionImage(const extensions::ExtensionResource& icon,
+  void LoadExtensionImage(const ExtensionResource& icon,
                           int request_id);
 
   // Loads the favicon image for the app associated with the |request_id|. If
@@ -134,7 +133,7 @@
   // as an ExtensionIconRequest via GetData.
   void SetData(int request_id,
                const content::URLDataSource::GotDataCallback& callback,
-               const extensions::Extension* extension,
+               const Extension* extension,
                bool grayscale,
                int size,
                ExtensionIconSet::MatchType match);
@@ -162,4 +161,6 @@
   DISALLOW_COPY_AND_ASSIGN(ExtensionIconSource);
 };
 
+}  // namespace extensions
+
 #endif  // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_ICON_SOURCE_H_
diff --git a/chrome/browser/ui/webui/extensions/extension_info_ui.cc b/chrome/browser/ui/webui/extensions/extension_info_ui.cc
index 456cc78..3714df1 100644
--- a/chrome/browser/ui/webui/extensions/extension_info_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extension_info_ui.cc
@@ -23,6 +23,8 @@
 #include "grit/browser_resources.h"
 #include "grit/generated_resources.h"
 
+namespace extensions {
+
 ExtensionInfoUI::ExtensionInfoUI(content::WebUI* web_ui, const GURL& url)
     : content::WebUIController(web_ui),
       source_(content::WebUIDataSource::Create(
@@ -55,15 +57,15 @@
 
 void ExtensionInfoUI::AddExtensionDataToSource(
     const std::string& extension_id) {
-  ExtensionService* extension_service = extensions::ExtensionSystem::Get(
+  ExtensionService* extension_service = ExtensionSystem::Get(
       Profile::FromWebUI(web_ui()))->extension_service();
-  const extensions::Extension* extension =
+  const Extension* extension =
       extension_service->extensions()->GetByID(extension_id);
   if (!extension)
     return;
 
   DictionaryValue extension_data;
-  extensions::GetExtensionBasicInfo(extension, true, &extension_data);
+  GetExtensionBasicInfo(extension, true, &extension_data);
   source_->AddLocalizedStrings(extension_data);
 
   // Set the icon URL.
@@ -78,3 +80,5 @@
       GetInstallTime(extension_id);
   source_->AddString("installTime", base::TimeFormatShortDate(install_time));
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/ui/webui/extensions/extension_info_ui.h b/chrome/browser/ui/webui/extensions/extension_info_ui.h
index 37843c5..2f58272 100644
--- a/chrome/browser/ui/webui/extensions/extension_info_ui.h
+++ b/chrome/browser/ui/webui/extensions/extension_info_ui.h
@@ -17,6 +17,8 @@
 class WebUIDataSource;
 }
 
+namespace extensions {
+
 // WebUI controller for the informative bubble shown on clicking a script badge.
 class ExtensionInfoUI : public content::WebUIController {
  public:
@@ -36,4 +38,6 @@
   DISALLOW_COPY_AND_ASSIGN(ExtensionInfoUI);
 };
 
+}  // namespace extensions
+
 #endif  // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_INFO_UI_H_
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
index 74063d8..37c1c1a 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
+++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
@@ -82,11 +82,18 @@
 
 using content::RenderViewHost;
 using content::WebContents;
-using extensions::Extension;
-using extensions::ExtensionUpdater;
-using extensions::ExtensionWarning;
-using extensions::ManagementPolicy;
-using extensions::Manifest;
+
+namespace extensions {
+
+ExtensionPage::ExtensionPage(const GURL& url,
+                             int render_process_id,
+                             int render_view_id,
+                             bool incognito)
+    : url(url),
+      render_process_id(render_process_id),
+      render_view_id(render_view_id),
+      incognito(incognito) {
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 //
@@ -137,10 +144,10 @@
 DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue(
     const Extension* extension,
     const std::vector<ExtensionPage>& pages,
-    const extensions::ExtensionWarningService* warning_service) {
+    const ExtensionWarningService* warning_service) {
   DictionaryValue* extension_data = new DictionaryValue();
   bool enabled = extension_service_->IsExtensionEnabled(extension->id());
-  extensions::GetExtensionBasicInfo(extension, enabled, extension_data);
+  GetExtensionBasicInfo(extension, enabled, extension_data);
 
   extension_data->SetBoolean(
       "userModifiable",
@@ -171,14 +178,14 @@
   extension_data->SetBoolean("is_hosted_app", extension->is_hosted_app());
   extension_data->SetBoolean("is_platform_app", extension->is_platform_app());
   extension_data->SetBoolean("homepageProvided",
-      extensions::ManifestURL::GetHomepageURL(extension).is_valid());
+      ManifestURL::GetHomepageURL(extension).is_valid());
 
   string16 location_text;
   if (extension->location() == Manifest::EXTERNAL_POLICY_DOWNLOAD) {
     location_text = l10n_util::GetStringUTF16(
         IDS_OPTIONS_INSTALL_LOCATION_ENTERPRISE);
   } else if (extension->location() == Manifest::INTERNAL &&
-      !extensions::ManifestURL::UpdatesFromGallery(extension)) {
+      !ManifestURL::UpdatesFromGallery(extension)) {
     location_text = l10n_util::GetStringUTF16(
         IDS_OPTIONS_INSTALL_LOCATION_UNKNOWN);
   } else if (extension->location() == Manifest::EXTERNAL_REGISTRY) {
@@ -194,7 +201,7 @@
   else
     extension_data->SetInteger("order", 2);
 
-  if (!extensions::ExtensionActionAPI::GetBrowserActionVisibility(
+  if (!ExtensionActionAPI::GetBrowserActionVisibility(
           extension_service_->extension_prefs(), extension->id())) {
     extension_data->SetBoolean("enable_show_button", true);
   }
@@ -204,7 +211,7 @@
   for (std::vector<ExtensionPage>::const_iterator iter = pages.begin();
        iter != pages.end(); ++iter) {
     DictionaryValue* view_value = new DictionaryValue;
-    if (iter->url.scheme() == extensions::kExtensionScheme) {
+    if (iter->url.scheme() == kExtensionScheme) {
       // No leading slash.
       view_value->SetString("path", iter->url.path().substr(1));
     } else {
@@ -217,8 +224,8 @@
     views->Append(view_value);
   }
   extension_data->Set("views", views);
-  extensions::ExtensionActionManager* extension_action_manager =
-      extensions::ExtensionActionManager::Get(extension_service_->profile());
+  ExtensionActionManager* extension_action_manager =
+      ExtensionActionManager::Get(extension_service_->profile());
   extension_data->SetBoolean(
       "hasPopupAction",
       extension_action_manager->GetBrowserAction(*extension) ||
@@ -241,15 +248,14 @@
 
   // Add install warnings (these are not the same as warnings!).
   if (Manifest::IsUnpackedLocation(extension->location())) {
-    const std::vector<extensions::InstallWarning>& install_warnings =
+    const std::vector<InstallWarning>& install_warnings =
         extension->install_warnings();
     if (!install_warnings.empty()) {
       scoped_ptr<ListValue> list(new ListValue());
-      for (std::vector<extensions::InstallWarning>::const_iterator it =
+      for (std::vector<InstallWarning>::const_iterator it =
                install_warnings.begin(); it != install_warnings.end(); ++it) {
         DictionaryValue* item = new DictionaryValue();
-        item->SetBoolean("isHTML",
-                         it->format == extensions::InstallWarning::FORMAT_HTML);
+        item->SetBoolean("isHTML", it->format == InstallWarning::FORMAT_HTML);
         item->SetString("message", it->message);
         list->Append(item);
       }
@@ -322,6 +328,10 @@
      l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED));
   source->AddString("extensionSettingsManagedMode",
      l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOCKED_MANAGED_USER));
+  source->AddString("extensionSettingsUseAppsDevTools",
+     l10n_util::GetStringUTF16(IDS_EXTENSIONS_USE_APPS_DEV_TOOLS));
+  source->AddString("extensionSettingsOpenAppsDevTools",
+     l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPEN_APPS_DEV_TOOLS));
   source->AddString("sideloadWipeoutUrl",
       chrome::kSideloadWipeoutHelpURL);
   source->AddString("sideloadWipoutLearnMore",
@@ -386,7 +396,7 @@
         GetExtensionService();
   }
   if (!management_policy_) {
-    management_policy_ = extensions::ExtensionSystem::Get(
+    management_policy_ = ExtensionSystem::Get(
         extension_service_->profile())->management_policy();
   }
 
@@ -441,7 +451,7 @@
                                             int index,
                                             void* params) {
   last_unpacked_directory_ = base::FilePath(path);
-  extensions::UnpackedInstaller::Create(extension_service_)->Load(path);
+  UnpackedInstaller::Create(extension_service_)->Load(path);
 }
 
 void ExtensionSettingsHandler::MultiFilesSelected(
@@ -478,6 +488,7 @@
       break;
     case chrome::NOTIFICATION_EXTENSION_LOADED:
     case chrome::NOTIFICATION_EXTENSION_UNLOADED:
+    case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
     case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED:
     case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED:
       MaybeUpdateAfterNotification();
@@ -564,8 +575,8 @@
   // Add the extensions to the results structure.
   ListValue* extensions_list = new ListValue();
 
-  extensions::ExtensionWarningService* warnings =
-      extensions::ExtensionSystem::Get(profile)->warning_service();
+  ExtensionWarningService* warnings =
+      ExtensionSystem::Get(profile)->warning_service();
 
   const ExtensionSet* extensions = extension_service_->extensions();
   for (ExtensionSet::const_iterator extension = extensions->begin();
@@ -600,15 +611,15 @@
   }
   results.Set("extensions", extensions_list);
 
-  ManagedUserService* service =
-      ManagedUserServiceFactory::GetForProfile(profile);
-
-  bool is_managed = service->ProfileIsManaged();
+  bool is_managed = profile->IsManaged();
   bool developer_mode =
       !is_managed &&
       profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
   results.SetBoolean("profileIsManaged", is_managed);
   results.SetBoolean("developerMode", developer_mode);
+  results.SetBoolean(
+      "appsDevToolsEnabled",
+      CommandLine::ForCurrentProcess()->HasSwitch(switches::kAppsDevtool));
 
   bool load_unpacked_disabled =
       extension_service_->extension_prefs()->ExtensionsBlacklistedByDefault();
@@ -623,7 +634,7 @@
 void ExtensionSettingsHandler::HandleToggleDeveloperMode(
     const ListValue* args) {
   Profile* profile = Profile::FromWebUI(web_ui());
-  if (ManagedUserServiceFactory::GetForProfile(profile)->ProfileIsManaged())
+  if (profile->IsManaged())
     return;
 
   bool developer_mode =
@@ -655,7 +666,7 @@
 
     ExtensionService* service = extension_service_;
     if (incognito)
-      service = extensions::ExtensionSystem::Get(extension_service_->
+      service = ExtensionSystem::Get(extension_service_->
           profile()->GetOffTheRecordProfile())->extension_service();
 
     service->InspectBackgroundPage(extension);
@@ -676,7 +687,7 @@
   CHECK_EQ(1U, args->GetSize());
   std::string extension_id;
   CHECK(args->GetString(0, &extension_id));
-  const extensions::Extension* extension =
+  const Extension* extension =
       extension_service_->GetExtensionById(extension_id, false);
   chrome::OpenApplication(chrome::AppLaunchParams(extension_service_->profile(),
                                                   extension,
@@ -714,9 +725,9 @@
   }
 
   if (enable_str == "true") {
-    extensions::ExtensionPrefs* prefs = extension_service_->extension_prefs();
+    ExtensionPrefs* prefs = extension_service_->extension_prefs();
     if (prefs->DidExtensionEscalatePermissions(extension_id)) {
-      extensions::ShowExtensionDisabledDialog(
+      ShowExtensionDisabledDialog(
           extension_service_, web_ui()->GetWebContents(), extension);
     } else if ((prefs->GetDisableReasons(extension_id) &
                    Extension::DISABLE_UNSUPPORTED_REQUIREMENT) &&
@@ -725,7 +736,7 @@
       scoped_refptr<const Extension> extension =
           extension_service_->GetExtensionById(extension_id,
                                                true /* include disabled */);
-      requirements_checker_.reset(new extensions::RequirementsChecker());
+      requirements_checker_.reset(new RequirementsChecker);
       requirements_checker_->Check(
           extension,
           base::Bind(&ExtensionSettingsHandler::OnRequirementsChecked,
@@ -734,7 +745,7 @@
       extension_service_->EnableExtension(extension_id);
 
       // Make sure any browser action contained within it is not hidden.
-      extensions::ExtensionActionAPI::SetBrowserActionVisibility(
+      ExtensionActionAPI::SetBrowserActionVisibility(
           prefs, extension->id(), true);
     }
   } else {
@@ -817,8 +828,7 @@
 
 void ExtensionSettingsHandler::HandleOptionsMessage(const ListValue* args) {
   const Extension* extension = GetActiveExtension(args);
-  if (!extension ||
-      extensions::ManifestURL::GetOptionsPage(extension).is_empty())
+  if (!extension || ManifestURL::GetOptionsPage(extension).is_empty())
     return;
   ExtensionTabUtil::OpenOptionsPage(extension,
       chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()));
@@ -838,7 +848,7 @@
   extension_id_prompting_ = extension->id();
   prompt_.reset(new ExtensionInstallPrompt(web_contents()));
   std::vector<base::FilePath> retained_file_paths;
-  if (extension->HasAPIPermission(extensions::APIPermission::kFileSystem)) {
+  if (extension->HasAPIPermission(APIPermission::kFileSystem)) {
     std::vector<apps::SavedFileEntry> retained_file_entries =
         apps::SavedFilesService::Get(Profile::FromWebUI(
             web_ui()))->GetAllFileEntries(extension_id_prompting_);
@@ -853,7 +863,7 @@
   const Extension* extension = GetActiveExtension(args);
   if (!extension)
     return;
-  extensions::ExtensionActionAPI::SetBrowserActionVisibility(
+  ExtensionActionAPI::SetBrowserActionVisibility(
       extension_service_->extension_prefs(), extension->id(), true);
 }
 
@@ -923,6 +933,8 @@
                  content::Source<Profile>(profile));
   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
                  content::Source<Profile>(profile));
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
+                 content::Source<Profile>(profile));
   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED,
                  content::Source<Profile>(profile));
   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_CREATED,
@@ -936,7 +948,7 @@
   registrar_.Add(
       this,
       chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
-      content::Source<extensions::ExtensionPrefs>(
+      content::Source<ExtensionPrefs>(
           profile->GetExtensionService()->extension_prefs()));
 
   content::RenderViewHost::AddCreatedCallback(rvh_created_callback_);
@@ -944,7 +956,7 @@
   content::WebContentsObserver::Observe(web_ui()->GetWebContents());
 
   warning_service_observer_.Add(
-      extensions::ExtensionSystem::Get(profile)->warning_service());
+      ExtensionSystem::Get(profile)->warning_service());
 
   base::Closure callback = base::Bind(
       &ExtensionSettingsHandler::MaybeUpdateAfterNotification,
@@ -961,8 +973,7 @@
 
   // Get the extension process's active views.
   ExtensionProcessManager* process_manager =
-      extensions::ExtensionSystem::Get(extension_service_->profile())->
-          process_manager();
+      ExtensionSystem::Get(extension_service_->profile())->process_manager();
   GetInspectablePagesForExtensionProcess(
       process_manager->GetRenderViewHostsForExtension(extension->id()),
       &result);
@@ -972,31 +983,29 @@
       extension_service_->profile(), &result);
 
   // Include a link to start the lazy background page, if applicable.
-  if (extensions::BackgroundInfo::HasLazyBackgroundPage(extension) &&
+  if (BackgroundInfo::HasLazyBackgroundPage(extension) &&
       extension_is_enabled &&
       !process_manager->GetBackgroundHostForExtension(extension->id())) {
-    result.push_back(
-        ExtensionPage(extensions::BackgroundInfo::GetBackgroundURL(extension),
-                      -1, -1, false));
+    result.push_back(ExtensionPage(
+        BackgroundInfo::GetBackgroundURL(extension), -1, -1, false));
   }
 
   // Repeat for the incognito process, if applicable. Don't try to get
   // shell windows for incognito processes.
   if (extension_service_->profile()->HasOffTheRecordProfile() &&
-      extensions::IncognitoInfo::IsSplitMode(extension)) {
+      IncognitoInfo::IsSplitMode(extension)) {
     ExtensionProcessManager* process_manager =
-        extensions::ExtensionSystem::Get(extension_service_->profile()->
+        ExtensionSystem::Get(extension_service_->profile()->
             GetOffTheRecordProfile())->process_manager();
     GetInspectablePagesForExtensionProcess(
         process_manager->GetRenderViewHostsForExtension(extension->id()),
         &result);
 
-    if (extensions::BackgroundInfo::HasLazyBackgroundPage(extension)
-        && extension_is_enabled &&
+    if (BackgroundInfo::HasLazyBackgroundPage(extension) &&
+        extension_is_enabled &&
         !process_manager->GetBackgroundHostForExtension(extension->id())) {
-      result.push_back(
-          ExtensionPage(extensions::BackgroundInfo::GetBackgroundURL(extension),
-                        -1, -1, true));
+      result.push_back(ExtensionPage(
+          BackgroundInfo::GetBackgroundURL(extension), -1, -1, true));
     }
   }
 
@@ -1010,10 +1019,10 @@
        iter != views.end(); ++iter) {
     RenderViewHost* host = *iter;
     WebContents* web_contents = WebContents::FromRenderViewHost(host);
-    extensions::ViewType host_type = extensions::GetViewType(web_contents);
+    ViewType host_type = GetViewType(web_contents);
     if (host == deleting_rvh_ ||
-        extensions::VIEW_TYPE_EXTENSION_POPUP == host_type ||
-        extensions::VIEW_TYPE_EXTENSION_DIALOG == host_type)
+        VIEW_TYPE_EXTENSION_POPUP == host_type ||
+        VIEW_TYPE_EXTENSION_DIALOG == host_type)
       continue;
 
     GURL url = web_contents->GetURL();
@@ -1028,14 +1037,13 @@
     const Extension* extension,
     Profile* profile,
     std::vector<ExtensionPage>* result) {
-  extensions::ShellWindowRegistry* registry =
-      extensions::ShellWindowRegistry::Get(profile);
+  ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile);
   if (!registry) return;
 
-  const extensions::ShellWindowRegistry::ShellWindowList windows =
+  const ShellWindowRegistry::ShellWindowList windows =
       registry->GetShellWindowsForApp(extension->id());
 
-  for (extensions::ShellWindowRegistry::const_iterator it = windows.begin();
+  for (ShellWindowRegistry::const_iterator it = windows.begin();
        it != windows.end(); ++it) {
     WebContents* web_contents = (*it)->web_contents();
     RenderViewHost* host = web_contents->GetRenderViewHost();
@@ -1076,3 +1084,5 @@
   }
   requirements_checker_.reset();
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.h b/chrome/browser/ui/webui/extensions/extension_settings_handler.h
index 55e2c30..e3188e3 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_handler.h
+++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.h
@@ -38,25 +38,21 @@
 class WebUIDataSource;
 }
 
-namespace extensions {
-class Extension;
-class ExtensionHost;
-class ManagementPolicy;
-}
-
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
 
+namespace extensions {
+class Extension;
+class ManagementPolicy;
+
 // Information about a page running in an extension, for example a popup bubble,
 // a background page, or a tab contents.
 struct ExtensionPage {
-  ExtensionPage(const GURL& url, int render_process_id, int render_view_id,
-                bool incognito)
-      : url(url),
-        render_process_id(render_process_id),
-        render_view_id(render_view_id),
-        incognito(incognito) {}
+  ExtensionPage(const GURL& url,
+                int render_process_id,
+                int render_view_id,
+                bool incognito);
   GURL url;
   int render_process_id;
   int render_view_id;
@@ -71,7 +67,7 @@
       public ui::SelectFileDialog::Listener,
       public ExtensionInstallPrompt::Delegate,
       public ExtensionUninstallDialog::Delegate,
-      public extensions::ExtensionWarningService::Observer,
+      public ExtensionWarningService::Observer,
       public base::SupportsWeakPtr<ExtensionSettingsHandler> {
  public:
   ExtensionSettingsHandler();
@@ -83,9 +79,9 @@
   // testing.
   // Note: |warning_service| can be NULL in unit tests.
   base::DictionaryValue* CreateExtensionDetailValue(
-      const extensions::Extension* extension,
+      const Extension* extension,
       const std::vector<ExtensionPage>& pages,
-      const extensions::ExtensionWarningService* warning_service);
+      const ExtensionWarningService* warning_service);
 
   void GetLocalizedValues(content::WebUIDataSource* source);
 
@@ -103,14 +99,15 @@
 
   // Allows injection for testing by friend classes.
   ExtensionSettingsHandler(ExtensionService* service,
-                           extensions::ManagementPolicy* policy);
+                           ManagementPolicy* policy);
 
   // WebUIMessageHandler implementation.
   virtual void RegisterMessages() OVERRIDE;
 
   // SelectFileDialog::Listener implementation.
   virtual void FileSelected(const base::FilePath& path,
-                            int index, void* params) OVERRIDE;
+                            int index,
+                            void* params) OVERRIDE;
   virtual void MultiFilesSelected(
       const std::vector<base::FilePath>& files, void* params) OVERRIDE;
   virtual void FileSelectionCanceled(void* params) OVERRIDE {}
@@ -125,7 +122,7 @@
   virtual void ExtensionUninstallAccepted() OVERRIDE;
   virtual void ExtensionUninstallCanceled() OVERRIDE;
 
-  // extensions::ExtensionWarningService::Observer implementation.
+  // ExtensionWarningService::Observer implementation.
   virtual void ExtensionWarningsChanged() OVERRIDE;
 
   // ExtensionInstallPrompt::Delegate implementation.
@@ -185,7 +182,7 @@
 
   // Utility for callbacks that get an extension ID as the sole argument.
   // Returns NULL if the extension isn't active.
-  const extensions::Extension* GetActiveExtension(const base::ListValue* args);
+  const Extension* GetActiveExtension(const base::ListValue* args);
 
   // Forces a UI update if appropriate after a notification is received.
   void MaybeUpdateAfterNotification();
@@ -195,12 +192,12 @@
 
   // Helper that lists the current inspectable html pages for an extension.
   std::vector<ExtensionPage> GetInspectablePagesForExtension(
-      const extensions::Extension* extension, bool extension_is_enabled);
+      const Extension* extension, bool extension_is_enabled);
   void GetInspectablePagesForExtensionProcess(
       const std::set<content::RenderViewHost*>& views,
       std::vector<ExtensionPage>* result);
   void GetShellWindowPagesForExtensionProfile(
-      const extensions::Extension* extension,
+      const Extension* extension,
       Profile* profile,
       std::vector<ExtensionPage>* result);
 
@@ -216,7 +213,7 @@
   ExtensionService* extension_service_;
 
   // A convenience member, filled once the extension_service_ is known.
-  extensions::ManagementPolicy* management_policy_;
+  ManagementPolicy* management_policy_;
 
   // Used to pick the directory when loading an extension.
   scoped_refptr<ui::SelectFileDialog> load_extension_dialog_;
@@ -258,16 +255,17 @@
   // This will not be empty when a requirements check is in progress. Doing
   // another Check() before the previous one is complete will cause the first
   // one to abort.
-  scoped_ptr<extensions::RequirementsChecker> requirements_checker_;
+  scoped_ptr<RequirementsChecker> requirements_checker_;
 
   // The UI for showing what permissions the extension has.
   scoped_ptr<ExtensionInstallPrompt> prompt_;
 
-  ScopedObserver<extensions::ExtensionWarningService,
-                 extensions::ExtensionWarningService::Observer>
+  ScopedObserver<ExtensionWarningService, ExtensionWarningService::Observer>
       warning_service_observer_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsHandler);
 };
 
+}  // namespace extensions
+
 #endif  // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc
index 25ab454..7d7c9fa 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -14,11 +14,15 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "grit/browser_resources.h"
+#include "grit/theme_resources.h"
+#include "ui/base/resource/resource_bundle.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h"
 #endif
 
+namespace extensions {
+
 namespace {
 
 content::WebUIDataSource* CreateExtensionsHTMLSource() {
@@ -50,8 +54,7 @@
   pack_handler->GetLocalizedValues(source);
   web_ui->AddMessageHandler(pack_handler);
 
-  extensions::CommandHandler* commands_handler =
-      new extensions::CommandHandler(profile);
+  CommandHandler* commands_handler = new CommandHandler(profile);
   commands_handler->GetLocalizedValues(source);
   web_ui->AddMessageHandler(commands_handler);
 
@@ -72,5 +75,13 @@
   content::WebUIDataSource::Add(profile, source);
 }
 
-ExtensionsUI::~ExtensionsUI() {
+ExtensionsUI::~ExtensionsUI() {}
+
+// static
+base::RefCountedMemory* ExtensionsUI::GetFaviconResourceBytes(
+    ui::ScaleFactor scale_factor) {
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  return rb.LoadDataResourceBytesForScale(IDR_EXTENSIONS_FAVICON, scale_factor);
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.h b/chrome/browser/ui/webui/extensions/extensions_ui.h
index 54ea76b..75b1df1 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.h
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.h
@@ -5,12 +5,28 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSIONS_UI_H_
 #define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSIONS_UI_H_
 
+#include "base/basictypes.h"
 #include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+namespace base {
+class RefCountedMemory;
+}
+
+namespace extensions {
 
 class ExtensionsUI : public content::WebUIController {
  public:
   explicit ExtensionsUI(content::WebUI* web_ui);
   virtual ~ExtensionsUI();
+
+  static base::RefCountedMemory* GetFaviconResourceBytes(
+      ui::ScaleFactor scale_factor);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ExtensionsUI);
 };
 
+}  // namespace extensions
+
 #endif  // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSIONS_UI_H_
diff --git a/chrome/browser/ui/webui/extensions/install_extension_handler.cc b/chrome/browser/ui/webui/extensions/install_extension_handler.cc
index 4904820..17e466d 100644
--- a/chrome/browser/ui/webui/extensions/install_extension_handler.cc
+++ b/chrome/browser/ui/webui/extensions/install_extension_handler.cc
@@ -23,6 +23,8 @@
 #include "net/base/net_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
+namespace extensions {
+
 InstallExtensionHandler::InstallExtensionHandler() {
 }
 
@@ -36,7 +38,7 @@
       l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALL_DROP_TARGET));
   source->AddBoolean(
       "offStoreInstallEnabled",
-      extensions::FeatureSwitch::easy_off_store_install()->IsEnabled());
+      FeatureSwitch::easy_off_store_install()->IsEnabled());
 }
 
 void InstallExtensionHandler::RegisterMessages() {
@@ -94,13 +96,12 @@
 
   Profile* profile = Profile::FromBrowserContext(
       web_ui()->GetWebContents()->GetBrowserContext());
-  scoped_refptr<extensions::CrxInstaller> crx_installer(
-      extensions::CrxInstaller::Create(
-          extensions::ExtensionSystem::Get(profile)->extension_service(),
-          new ExtensionInstallPrompt(web_ui()->GetWebContents())));
+  scoped_refptr<CrxInstaller> crx_installer(CrxInstaller::Create(
+      ExtensionSystem::Get(profile)->extension_service(),
+      new ExtensionInstallPrompt(web_ui()->GetWebContents())));
   crx_installer->set_error_on_unsupported_requirements(true);
   crx_installer->set_off_store_install_allow_reason(
-      extensions::CrxInstaller::OffStoreInstallAllowedFromSettingsPage);
+      CrxInstaller::OffStoreInstallAllowedFromSettingsPage);
   crx_installer->set_install_wait_for_idle(false);
 
   const bool kCaseSensitive = false;
@@ -125,7 +126,9 @@
     const ListValue* args) {
   Profile* profile = Profile::FromBrowserContext(
       web_ui()->GetWebContents()->GetBrowserContext());
-  extensions::UnpackedInstaller::Create(
-      extensions::ExtensionSystem::Get(profile)->
+  UnpackedInstaller::Create(
+      ExtensionSystem::Get(profile)->
           extension_service())->Load(file_to_install_);
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/ui/webui/extensions/install_extension_handler.h b/chrome/browser/ui/webui/extensions/install_extension_handler.h
index 81afefd..f19497e 100644
--- a/chrome/browser/ui/webui/extensions/install_extension_handler.h
+++ b/chrome/browser/ui/webui/extensions/install_extension_handler.h
@@ -14,6 +14,8 @@
 class WebUIDataSource;
 }
 
+namespace extensions {
+
 // Handles installing an extension when its file is dragged onto the page.
 class InstallExtensionHandler : public content::WebUIMessageHandler {
  public:
@@ -55,4 +57,6 @@
   DISALLOW_COPY_AND_ASSIGN(InstallExtensionHandler);
 };
 
+}  // namespace extensions
+
 #endif  // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_INSTALL_EXTENSION_HANDLER_H_
diff --git a/chrome/browser/ui/webui/extensions/pack_extension_handler.cc b/chrome/browser/ui/webui/extensions/pack_extension_handler.cc
index 59c8884..7653446 100644
--- a/chrome/browser/ui/webui/extensions/pack_extension_handler.cc
+++ b/chrome/browser/ui/webui/extensions/pack_extension_handler.cc
@@ -15,6 +15,8 @@
 #include "grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
+namespace extensions {
+
 PackExtensionHandler::PackExtensionHandler() {
 }
 
@@ -68,21 +70,19 @@
                                          const base::FilePath& pem_file) {
   ListValue arguments;
   arguments.Append(Value::CreateStringValue(
-      UTF16ToUTF8(extensions::PackExtensionJob::StandardSuccessMessage(
+      UTF16ToUTF8(PackExtensionJob::StandardSuccessMessage(
           crx_file, pem_file))));
   web_ui()->CallJavascriptFunction(
       "PackExtensionOverlay.showSuccessMessage", arguments);
 }
 
-void PackExtensionHandler::OnPackFailure(
-    const std::string& error,
-    extensions::ExtensionCreator::ErrorType type) {
-  if (type == extensions::ExtensionCreator::kCRXExists) {
+void PackExtensionHandler::OnPackFailure(const std::string& error,
+                                         ExtensionCreator::ErrorType type) {
+  if (type == ExtensionCreator::kCRXExists) {
     base::StringValue error_str(error);
-    base::StringValue extension_path_str(extension_path_);
-    base::StringValue key_path_str(private_key_path_);
-    base::FundamentalValue overwrite_flag(
-        extensions::ExtensionCreator::kOverwriteCRX);
+    base::StringValue extension_path_str(extension_path_.value());
+    base::StringValue key_path_str(private_key_path_.value());
+    base::FundamentalValue overwrite_flag(ExtensionCreator::kOverwriteCRX);
 
     web_ui()->CallJavascriptFunction(
         "ExtensionSettings.askToOverrideWarning", error_str, extension_path_str,
@@ -105,25 +105,26 @@
 }
 
 void PackExtensionHandler::HandlePackMessage(const ListValue* args) {
-
   DCHECK_EQ(3U, args->GetSize());
 
-  if (!args->GetString(0, &extension_path_) ||
-      !args->GetString(1, &private_key_path_))
-    NOTREACHED();
-
   double flags_double = 0.0;
-  if (!args->GetDouble(2, &flags_double))
+  base::FilePath::StringType extension_path_str;
+  base::FilePath::StringType private_key_path_str;
+  if (!args->GetString(0, &extension_path_str) ||
+      !args->GetString(1, &private_key_path_str) ||
+      !args->GetDouble(2, &flags_double)) {
     NOTREACHED();
+    return;
+  }
+
+  extension_path_ = base::FilePath(extension_path_str);
+  private_key_path_ = base::FilePath(private_key_path_str);
 
   int run_flags = static_cast<int>(flags_double);
 
-  base::FilePath root_directory =
-      base::FilePath::FromWStringHack(UTF8ToWide(extension_path_));
-  base::FilePath key_file =
-      base::FilePath::FromWStringHack(UTF8ToWide(private_key_path_));
-  last_used_path_ =
-      base::FilePath::FromWStringHack(UTF8ToWide(extension_path_));
+  base::FilePath root_directory = extension_path_;
+  base::FilePath key_file = private_key_path_;
+  last_used_path_ = extension_path_;
 
   if (root_directory.empty()) {
     if (extension_path_.empty()) {
@@ -143,8 +144,7 @@
     return;
   }
 
-  pack_job_ = new extensions::PackExtensionJob(
-      this, root_directory, key_file, run_flags);
+  pack_job_ = new PackExtensionJob(this, root_directory, key_file, run_flags);
   pack_job_->Start();
 }
 
@@ -204,3 +204,5 @@
   arguments.Append(Value::CreateStringValue(message));
   web_ui()->CallJavascriptFunction("PackExtensionOverlay.showError", arguments);
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/ui/webui/extensions/pack_extension_handler.h b/chrome/browser/ui/webui/extensions/pack_extension_handler.h
index 84ffbcd..49ec4ab 100644
--- a/chrome/browser/ui/webui/extensions/pack_extension_handler.h
+++ b/chrome/browser/ui/webui/extensions/pack_extension_handler.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/files/file_path.h"
 #include "chrome/browser/browsing_data/browsing_data_remover.h"
 #include "chrome/browser/extensions/pack_extension_job.h"
 #include "chrome/browser/plugins/plugin_data_remover_helper.h"
@@ -17,10 +18,12 @@
 class WebUIDataSource;
 }
 
+namespace extensions {
+
 // Clear browser data handler page UI handler.
 class PackExtensionHandler : public content::WebUIMessageHandler,
                              public ui::SelectFileDialog::Listener,
-                             public extensions::PackExtensionJob::Client {
+                             public PackExtensionJob::Client {
  public:
   PackExtensionHandler();
   virtual ~PackExtensionHandler();
@@ -35,7 +38,7 @@
                              const base::FilePath& key_file) OVERRIDE;
 
   virtual void OnPackFailure(const std::string& error,
-                             extensions::ExtensionCreator::ErrorType) OVERRIDE;
+                             ExtensionCreator::ErrorType) OVERRIDE;
 
  private:
   // SelectFileDialog::Listener implementation.
@@ -59,17 +62,17 @@
   void ShowAlert(const std::string& message);
 
   // Used to package the extension.
-  scoped_refptr<extensions::PackExtensionJob> pack_job_;
+  scoped_refptr<PackExtensionJob> pack_job_;
 
   // Returned by the SelectFileDialog machinery. Used to initiate the selection
   // dialog.
   scoped_refptr<ui::SelectFileDialog> load_extension_dialog_;
 
-  // Path to root directory of extension
-  std::string extension_path_;
+  // Path to root directory of extension.
+  base::FilePath extension_path_;
 
-  // Path to private key file, or null if none specified
-  std::string private_key_path_;
+  // Path to private key file, or null if none specified.
+  base::FilePath private_key_path_;
 
   // Path to the last used folder to load an extension.
   base::FilePath last_used_path_;
@@ -77,4 +80,6 @@
   DISALLOW_COPY_AND_ASSIGN(PackExtensionHandler);
 };
 
+}  // namespace extensions
+
 #endif  // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_PACK_EXTENSION_HANDLER_H_
diff --git a/chrome/browser/ui/webui/feedback_ui.cc b/chrome/browser/ui/webui/feedback_ui.cc
deleted file mode 100644
index 0f58e2c..0000000
--- a/chrome/browser/ui/webui/feedback_ui.cc
+++ /dev/null
@@ -1,727 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/ui/webui/feedback_ui.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "base/base64.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/files/file_enumerator.h"
-#include "base/logging.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/prefs/pref_service.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/download/download_prefs.h"
-#include "chrome/browser/feedback/feedback_data.h"
-#include "chrome/browser/feedback/feedback_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/signin/signin_manager.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/singleton_tabs.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/webui/screenshot_source.h"
-#include "chrome/browser/ui/window_snapshot/window_snapshot.h"
-#include "chrome/common/cancelable_task_tracker.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/navigation_controller.h"
-#include "content/public/browser/navigation_entry.h"
-#include "content/public/browser/url_data_source.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "content/public/browser/web_ui_message_handler.h"
-#include "grit/browser_resources.h"
-#include "grit/chromium_strings.h"
-#include "grit/generated_resources.h"
-#include "grit/locale_settings.h"
-#include "net/base/escape.h"
-#include "ui/base/resource/resource_bundle.h"
-
-#if defined(OS_CHROMEOS)
-#include "ash/shell.h"
-#include "ash/shell_delegate.h"
-#include "base/file_util.h"
-#include "base/path_service.h"
-#include "chrome/browser/chromeos/drive/drive.pb.h"
-#include "chrome/browser/chromeos/drive/drive_integration_service.h"
-#include "chrome/browser/chromeos/drive/file_system_interface.h"
-#include "chrome/browser/chromeos/drive/file_system_util.h"
-#include "chrome/browser/chromeos/login/user_manager.h"
-#include "chrome/browser/chromeos/system_logs/system_logs_fetcher.h"
-#include "ui/aura/root_window.h"
-#include "ui/aura/window.h"
-#endif
-
-using content::BrowserThread;
-using content::WebContents;
-using content::WebUIMessageHandler;
-using ui::WebDialogUI;
-
-namespace {
-
-const char kCategoryTagParameter[] = "categoryTag=";
-const char kDescriptionParameter[] = "description=";
-const char kSessionIDParameter[] = "session_id=";
-const char kTabIndexParameter[] = "tab_index=";
-const char kCustomPageUrlParameter[] = "customPageUrl=";
-
-#if defined(OS_CHROMEOS)
-
-const char kTimestampParameter[] = "timestamp=";
-
-const size_t kMaxSavedScreenshots = 2;
-size_t kMaxNumScanFiles = 1000;
-
-// Compare two screenshot filepaths, which include the screenshot timestamp
-// in the format of screenshot-yyyymmdd-hhmmss.png. Return true if |filepath1|
-// is more recent |filepath2|.
-bool ScreenshotTimestampComp(const std::string& filepath1,
-                             const std::string& filepath2) {
-  return filepath1 > filepath2;
-}
-
-std::string GetUserEmail() {
-  chromeos::UserManager* manager = chromeos::UserManager::Get();
-  if (!manager)
-    return std::string();
-  else
-    return manager->GetLoggedInUser()->display_email();
-}
-
-bool ScreenshotDriveTimestampComp(const drive::ResourceEntry& entry1,
-                                  const drive::ResourceEntry& entry2) {
-  return entry1.file_info().last_modified() >
-      entry2.file_info().last_modified();
-}
-
-void ReadDirectoryCallback(size_t max_saved,
-                           std::vector<std::string>* saved_screenshots,
-                           base::Closure callback,
-                           drive::FileError error,
-                           scoped_ptr<drive::ResourceEntryVector> entries) {
-  if (error != drive::FILE_ERROR_OK) {
-    callback.Run();
-    return;
-  }
-
-  size_t max_scan = std::min(kMaxNumScanFiles, entries->size());
-  std::vector<drive::ResourceEntry> screenshot_entries;
-  for (size_t i = 0; i < max_scan; ++i) {
-    const drive::ResourceEntry& entry = (*entries)[i];
-    if (StartsWithASCII(entry.base_name(),
-                        ScreenshotSource::kScreenshotPrefix, true) &&
-        EndsWith(entry.base_name(),
-                 ScreenshotSource::kScreenshotSuffix, true)) {
-      screenshot_entries.push_back(entry);
-    }
-  }
-
-  size_t sort_size = std::min(max_saved, screenshot_entries.size());
-  std::partial_sort(screenshot_entries.begin(),
-                    screenshot_entries.begin() + sort_size,
-                    screenshot_entries.end(),
-                    ScreenshotDriveTimestampComp);
-  for (size_t i = 0; i < sort_size; ++i) {
-    const drive::ResourceEntry& entry = screenshot_entries[i];
-    saved_screenshots->push_back(
-        std::string(ScreenshotSource::kScreenshotUrlRoot) +
-        std::string(ScreenshotSource::kScreenshotSaved) +
-        entry.base_name());
-  }
-  callback.Run();
-}
-
-#else
-
-std::string GetUserEmail() {
-  Profile* profile = ProfileManager::GetLastUsedProfile();
-  if (!profile)
-    return std::string();
-
-  SigninManager* signin = SigninManagerFactory::GetForProfile(profile);
-  if (!signin)
-    return std::string();
-
-  return signin->GetAuthenticatedUsername();
-}
-
-#endif  // OS_CHROMEOS
-
-// Returns the index of the feedback tab if already open, -1 otherwise
-int GetIndexOfFeedbackTab(Browser* browser) {
-  GURL feedback_url(chrome::kChromeUIFeedbackURL);
-  for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
-    WebContents* tab = browser->tab_strip_model()->GetWebContentsAt(i);
-    if (tab && tab->GetURL().GetWithEmptyPath() == feedback_url)
-      return i;
-  }
-
-  return -1;
-}
-
-}  // namespace
-
-namespace chrome {
-
-void ShowFeedbackPage(Browser* browser,
-                      const std::string& description_template,
-                      const std::string& category_tag) {
-#if defined(OS_CHROMEOS)
-  // Grab the timestamp before we do anything else - this is crucial to help
-  // diagnose some hardware issues.
-  base::Time now = base::Time::Now();
-  std::string timestamp = base::DoubleToString(now.ToDoubleT());
-#endif
-
-  // First check if we're already open (we cannot depend on ShowSingletonTab
-  // for this functionality since we need to make *sure* we never get
-  // instantiated again while we are open - with singleton tabs, that can
-  // happen).
-  int feedback_tab_index = GetIndexOfFeedbackTab(browser);
-  if (feedback_tab_index >= 0) {
-    // Do not refresh screenshot, do not create a new tab.
-    browser->tab_strip_model()->ActivateTabAt(feedback_tab_index, true);
-    return;
-  }
-
-  if (category_tag != kAppLauncherCategoryTag) {
-    std::vector<unsigned char>* last_screenshot_png =
-        FeedbackUtil::GetScreenshotPng();
-    last_screenshot_png->clear();
-
-    gfx::NativeWindow native_window;
-    gfx::Rect snapshot_bounds;
-
-#if defined(OS_CHROMEOS)
-    // For ChromeOS, don't use the browser window but the root window
-    // instead to grab the screenshot. We want everything on the screen, not
-    // just the current browser.
-    native_window = ash::Shell::GetPrimaryRootWindow();
-    snapshot_bounds = gfx::Rect(native_window->bounds());
-#else
-    native_window = browser->window()->GetNativeWindow();
-    snapshot_bounds = gfx::Rect(browser->window()->GetBounds().size());
-#endif
-    bool success = chrome::GrabWindowSnapshotForUser(native_window,
-                                                     last_screenshot_png,
-                                                     snapshot_bounds);
-    FeedbackUtil::SetScreenshotSize(success ? snapshot_bounds : gfx::Rect());
-  }
-  std::string feedback_url = std::string(chrome::kChromeUIFeedbackURL) + "?" +
-      kSessionIDParameter + base::IntToString(browser->session_id().id()) +
-      "&" + kTabIndexParameter +
-      base::IntToString(browser->tab_strip_model()->active_index()) +
-      "&" + kDescriptionParameter +
-      net::EscapeUrlEncodedData(description_template, false) + "&" +
-      kCategoryTagParameter + net::EscapeUrlEncodedData(category_tag, false);
-
-#if defined(OS_CHROMEOS)
-  feedback_url = feedback_url + "&" + kTimestampParameter +
-                 net::EscapeUrlEncodedData(timestamp, false);
-#endif
-  chrome::ShowSingletonTab(browser, GURL(feedback_url));
-}
-
-}  // namespace chrome
-
-// The handler for Javascript messages related to the "bug report" dialog
-class FeedbackHandler : public WebUIMessageHandler,
-                        public base::SupportsWeakPtr<FeedbackHandler> {
- public:
-  explicit FeedbackHandler(content::WebContents* tab);
-  virtual ~FeedbackHandler();
-
-  // Init work after Attach.  Returns true on success.
-  bool Init();
-
-  // WebUIMessageHandler implementation.
-  virtual void RegisterMessages() OVERRIDE;
-
- private:
-  void HandleGetDialogDefaults(const ListValue* args);
-  void HandleRefreshCurrentScreenshot(const ListValue* args);
-#if defined(OS_CHROMEOS)
-  void HandleRefreshSavedScreenshots(const ListValue* args);
-  void RefreshSavedScreenshotsCallback(
-      std::vector<std::string>* saved_screenshots);
-  void GetMostRecentScreenshotsDrive(
-      const base::FilePath& filepath,
-      std::vector<std::string>* saved_screenshots,
-      size_t max_saved, base::Closure callback);
-  void StartSyslogsCollection();
-#endif
-  void HandleSendReport(const ListValue* args);
-  void HandleCancel(const ListValue* args);
-  void HandleOpenSystemTab(const ListValue* args);
-
-  void SetupScreenshotsSource();
-  void ClobberScreenshotsSource();
-
-  void CloseFeedbackTab();
-
-  WebContents* tab_;
-  ScreenshotSource* screenshot_source_;
-
-  scoped_refptr<FeedbackData> feedback_data_;
-  std::string target_tab_url_;
-  std::string category_tag_;
-#if defined(OS_CHROMEOS)
-  // Timestamp of when the feedback request was initiated.
-  std::string timestamp_;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(FeedbackHandler);
-};
-
-content::WebUIDataSource* CreateFeedbackUIHTMLSource(bool successful_init) {
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(chrome::kChromeUIFeedbackHost);
-  source->SetUseJsonJSFormatV2();
-
-  source->AddLocalizedString("title", IDS_FEEDBACK_TITLE);
-  source->AddLocalizedString("page-title", IDS_FEEDBACK_REPORT_PAGE_TITLE);
-  source->AddLocalizedString("page-url", IDS_FEEDBACK_REPORT_URL_LABEL);
-  source->AddLocalizedString("description", IDS_FEEDBACK_DESCRIPTION_LABEL);
-  source->AddLocalizedString("current-screenshot",
-                             IDS_FEEDBACK_SCREENSHOT_LABEL);
-  source->AddLocalizedString("saved-screenshot",
-                             IDS_FEEDBACK_SAVED_SCREENSHOT_LABEL);
-  source->AddLocalizedString("user-email", IDS_FEEDBACK_USER_EMAIL_LABEL);
-
-#if defined(OS_CHROMEOS)
-  source->AddLocalizedString("sysinfo",
-                             IDS_FEEDBACK_INCLUDE_SYSTEM_INFORMATION_CHKBOX);
-  source->AddLocalizedString("currentscreenshots",
-                             IDS_FEEDBACK_CURRENT_SCREENSHOTS);
-  source->AddLocalizedString("savedscreenshots",
-                             IDS_FEEDBACK_SAVED_SCREENSHOTS);
-  source->AddLocalizedString("choose-different-screenshot",
-                             IDS_FEEDBACK_CHOOSE_DIFFERENT_SCREENSHOT);
-  source->AddLocalizedString("choose-original-screenshot",
-                             IDS_FEEDBACK_CHOOSE_ORIGINAL_SCREENSHOT);
-  source->AddLocalizedString("attach-file-label",
-                             IDS_FEEDBACK_ATTACH_FILE_LABEL);
-  source->AddLocalizedString("attach-file-note",
-                             IDS_FEEDBACK_ATTACH_FILE_NOTE);
-  source->AddLocalizedString("attach-file-to-big",
-                             IDS_FEEDBACK_ATTACH_FILE_TO_BIG);
-  source->AddLocalizedString("reading-file", IDS_FEEDBACK_READING_FILE);
-#else
-  source->AddLocalizedString("currentscreenshots",
-                             IDS_FEEDBACK_INCLUDE_NEW_SCREEN_IMAGE);
-#endif
-  source->AddLocalizedString("noscreenshot",
-                             IDS_FEEDBACK_INCLUDE_NO_SCREENSHOT);
-
-  source->AddLocalizedString("send-report", IDS_FEEDBACK_SEND_REPORT);
-  source->AddLocalizedString("cancel", IDS_CANCEL);
-
-  source->AddLocalizedString("no-description", IDS_FEEDBACK_NO_DESCRIPTION);
-  source->AddLocalizedString("no-saved-screenshots",
-                             IDS_FEEDBACK_NO_SAVED_SCREENSHOTS_HELP);
-  source->AddLocalizedString("privacy-note", IDS_FEEDBACK_PRIVACY_NOTE);
-  source->AddLocalizedString("launcher-title", IDS_FEEDBACK_LAUNCHER_TITLE);
-  source->AddLocalizedString("launcher-description",
-                             IDS_FEEDBACK_LAUNCHER_DESCRIPTION_LABEL);
-
-  source->SetJsonPath("strings.js");
-  source->AddResourcePath("feedback.js", IDR_FEEDBACK_JS);
-  source->SetDefaultResource(
-      successful_init ? IDR_FEEDBACK_HTML : IDR_FEEDBACK_HTML_INVALID);
-
-  return source;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// FeedbackHandler
-//
-////////////////////////////////////////////////////////////////////////////////
-FeedbackHandler::FeedbackHandler(WebContents* tab)
-    : tab_(tab),
-      screenshot_source_(NULL),
-      feedback_data_(NULL) {
-  DCHECK(tab);
-}
-
-FeedbackHandler::~FeedbackHandler() {
-  // Make sure we don't leave any screenshot data around.
-  FeedbackUtil::ClearScreenshotPng();
-}
-
-void FeedbackHandler::ClobberScreenshotsSource() {
-  // Re-create our screenshots data source (this clobbers the last source)
-  // setting the screenshot to NULL, effectively disabling the source
-  // TODO(rkc): Once there is a method to 'remove' a source, change this code
-  Profile* profile = Profile::FromBrowserContext(tab_->GetBrowserContext());
-  content::URLDataSource::Add(profile, new ScreenshotSource(NULL, profile));
-
-  FeedbackUtil::ClearScreenshotPng();
-}
-
-void FeedbackHandler::SetupScreenshotsSource() {
-  Profile* profile = Profile::FromBrowserContext(tab_->GetBrowserContext());
-  screenshot_source_ =
-      new ScreenshotSource(FeedbackUtil::GetScreenshotPng(), profile);
-  // Add the source to the data manager.
-  content::URLDataSource::Add(profile, screenshot_source_);
-}
-
-bool FeedbackHandler::Init() {
-  std::string page_url;
-  if (tab_->GetController().GetActiveEntry()) {
-     page_url = tab_->GetController().GetActiveEntry()->GetURL().spec();
-  }
-
-  url_parse::Parsed parts;
-  ParseStandardURL(page_url.c_str(), page_url.length(), &parts);
-
-  size_t params_start = page_url.find("?");
-  std::string query = page_url.substr(params_start + 1);
-
-  int session_id = -1;
-  int index = -1;
-
-  std::vector<std::string> params;
-  std::string custom_page_url;
-  if (Tokenize(query, std::string("&"), &params)) {
-    for (std::vector<std::string>::iterator it = params.begin();
-         it != params.end(); ++it) {
-      std::string query_str = *it;
-      if (StartsWithASCII(query_str, std::string(kSessionIDParameter), true)) {
-        ReplaceFirstSubstringAfterOffset(
-            &query_str, 0, kSessionIDParameter, std::string());
-        if (!base::StringToInt(query_str, &session_id))
-          return false;
-      } else if (StartsWithASCII(*it, std::string(kTabIndexParameter), true)) {
-        ReplaceFirstSubstringAfterOffset(
-            &query_str, 0, kTabIndexParameter, std::string());
-        if (!base::StringToInt(query_str, &index))
-          return false;
-      } else if (StartsWithASCII(*it, std::string(kCustomPageUrlParameter),
-                                 true)) {
-        ReplaceFirstSubstringAfterOffset(
-            &query_str, 0, kCustomPageUrlParameter, std::string());
-        custom_page_url = query_str;
-      } else if (StartsWithASCII(*it, std::string(kCategoryTagParameter),
-                                 true)) {
-        ReplaceFirstSubstringAfterOffset(
-            &query_str, 0, kCategoryTagParameter, std::string());
-        category_tag_ = query_str;
-#if defined(OS_CHROMEOS)
-      } else if (StartsWithASCII(*it, std::string(kTimestampParameter), true)) {
-        ReplaceFirstSubstringAfterOffset(
-            &query_str, 0, kTimestampParameter, "");
-        timestamp_ = query_str;
-#endif
-      }
-    }
-  }
-
-  // If we don't have a page url specified, get it from the tab index.
-  if (custom_page_url.empty()) {
-    if (session_id == -1)
-      return false;
-
-    Browser* browser = chrome::FindBrowserWithID(session_id);
-    // Sanity checks.
-    if (!browser || index >= browser->tab_strip_model()->count())
-      return false;
-
-    if (index >= 0) {
-      WebContents* target_tab =
-          browser->tab_strip_model()->GetWebContentsAt(index);
-      if (target_tab)
-        target_tab_url_ = target_tab->GetURL().spec();
-    }
-
-    // Note: We don't need to setup a screenshot source if we're using a
-    // custom page URL since we were invoked from JS/an extension, in which
-    // case we don't actually have a screenshot anyway.
-    SetupScreenshotsSource();
-  } else {
-    target_tab_url_ = custom_page_url;
-  }
-
-  return true;
-}
-
-void FeedbackHandler::RegisterMessages() {
-  web_ui()->RegisterMessageCallback("getDialogDefaults",
-      base::Bind(&FeedbackHandler::HandleGetDialogDefaults,
-                 base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("refreshCurrentScreenshot",
-      base::Bind(&FeedbackHandler::HandleRefreshCurrentScreenshot,
-                 base::Unretained(this)));
-#if defined(OS_CHROMEOS)
-  web_ui()->RegisterMessageCallback("refreshSavedScreenshots",
-      base::Bind(&FeedbackHandler::HandleRefreshSavedScreenshots,
-                 base::Unretained(this)));
-#endif
-  web_ui()->RegisterMessageCallback("sendReport",
-      base::Bind(&FeedbackHandler::HandleSendReport,
-                 base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("cancel",
-      base::Bind(&FeedbackHandler::HandleCancel,
-                 base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("openSystemTab",
-      base::Bind(&FeedbackHandler::HandleOpenSystemTab,
-                 base::Unretained(this)));
-}
-
-void FeedbackHandler::HandleGetDialogDefaults(const ListValue*) {
-  feedback_data_ = new FeedbackData();
-
-  // Send back values which the dialog js needs initially.
-  DictionaryValue dialog_defaults;
-
-  if (category_tag_ == chrome::kAppLauncherCategoryTag)
-    dialog_defaults.SetBoolean("launcherFeedback", true);
-
-  // Current url.
-  dialog_defaults.SetString("currentUrl", target_tab_url_);
-
-  // Are screenshots disabled?
-  dialog_defaults.SetBoolean(
-      "disableScreenshots",
-      g_browser_process->local_state()->GetBoolean(prefs::kDisableScreenshots));
-
-  // User e-mail
-  std::string user_email = GetUserEmail();
-  dialog_defaults.SetString("userEmail", user_email);
-
-  // Set email checkbox to checked by default for cros, unchecked for Chrome.
-  dialog_defaults.SetBoolean(
-      "emailCheckboxDefault",
-#if defined(OS_CHROMEOS)
-      true);
-#else
-      false);
-#endif
-
-
-#if defined(OS_CHROMEOS)
-  feedback_data_->StartSyslogsCollection();
-
-  // On ChromeOS if the user's email is blank, it means we don't
-  // have a logged in user, hence don't use saved screenshots.
-  dialog_defaults.SetBoolean("useSaved", !user_email.empty());
-#endif
-
-  web_ui()->CallJavascriptFunction("setupDialogDefaults", dialog_defaults);
-}
-
-void FeedbackHandler::HandleRefreshCurrentScreenshot(const ListValue*) {
-  std::string current_screenshot(
-          std::string(ScreenshotSource::kScreenshotUrlRoot) +
-          std::string(ScreenshotSource::kScreenshotCurrent));
-  StringValue screenshot(current_screenshot);
-  web_ui()->CallJavascriptFunction("setupCurrentScreenshot", screenshot);
-}
-
-#if defined(OS_CHROMEOS)
-void FeedbackHandler::HandleRefreshSavedScreenshots(const ListValue*) {
-  std::vector<std::string>* saved_screenshots = new std::vector<std::string>;
-  base::FilePath filepath = DownloadPrefs::FromBrowserContext(
-      tab_->GetBrowserContext())->DownloadPath();
-  base::Closure refresh_callback = base::Bind(
-      &FeedbackHandler::RefreshSavedScreenshotsCallback,
-      AsWeakPtr(), base::Owned(saved_screenshots));
-  if (drive::util::IsUnderDriveMountPoint(filepath)) {
-    GetMostRecentScreenshotsDrive(
-        filepath, saved_screenshots, kMaxSavedScreenshots, refresh_callback);
-  } else {
-    BrowserThread::PostTaskAndReply(
-        BrowserThread::FILE, FROM_HERE,
-        base::Bind(&FeedbackUI::GetMostRecentScreenshots, filepath,
-                   base::Unretained(saved_screenshots), kMaxSavedScreenshots),
-        refresh_callback);
-  }
-}
-
-void FeedbackHandler::RefreshSavedScreenshotsCallback(
-    std::vector<std::string>* saved_screenshots) {
-  ListValue screenshots_list;
-  for (size_t i = 0; i < saved_screenshots->size(); ++i)
-    screenshots_list.Append(new StringValue((*saved_screenshots)[i]));
-  web_ui()->CallJavascriptFunction("setupSavedScreenshots", screenshots_list);
-}
-
-void FeedbackHandler::GetMostRecentScreenshotsDrive(
-    const base::FilePath& filepath, std::vector<std::string>* saved_screenshots,
-    size_t max_saved, base::Closure callback) {
-  drive::FileSystemInterface* file_system =
-      drive::DriveIntegrationServiceFactory::GetForProfile(
-          Profile::FromWebUI(web_ui()))->file_system();
-  file_system->ReadDirectoryByPath(
-      drive::util::ExtractDrivePath(filepath),
-      base::Bind(&ReadDirectoryCallback, max_saved, saved_screenshots,
-                 callback));
-}
-#endif
-
-
-void FeedbackHandler::HandleSendReport(const ListValue* list_value) {
-  if (!feedback_data_.get()) {
-    LOG(ERROR) << "Bug report hasn't been intialized yet.";
-    return;
-  }
-
-  ListValue::const_iterator i = list_value->begin();
-  std::string page_url;
-  (*i++)->GetAsString(&page_url);
-  std::string category_tag;
-  (*i++)->GetAsString(&category_tag);
-  std::string description;
-  (*i++)->GetAsString(&description);
-  std::string user_email;
-  (*i++)->GetAsString(&user_email);
-  std::string screenshot_path;
-  (*i++)->GetAsString(&screenshot_path);
-  screenshot_path.erase(0, strlen(ScreenshotSource::kScreenshotUrlRoot));
-
-  // Get the image to send in the report.
-  ScreenshotDataPtr image_ptr;
-  if (!screenshot_path.empty() &&  screenshot_source_)
-    image_ptr = screenshot_source_->GetCachedScreenshot(screenshot_path);
-
-#if defined(OS_CHROMEOS)
-  std::string sys_info_checkbox;
-  (*i++)->GetAsString(&sys_info_checkbox);
-  bool send_sys_info = (sys_info_checkbox == "true");
-
-  std::string attached_filename;
-  scoped_ptr<std::string> attached_filedata;
-  // If we have an attached file, we'll still have more data in the list.
-  if (i != list_value->end()) {
-    (*i++)->GetAsString(&attached_filename);
-    if (base::FilePath::IsSeparator(attached_filename[0])) {
-      // We have an attached filepath, not filename, hence we need read this
-      // this file in chrome. We won't have any file data, skip over it.
-      i++;
-    } else {
-      std::string encoded_filedata;
-      attached_filedata.reset(new std::string);
-      (*i++)->GetAsString(&encoded_filedata);
-      if (!base::Base64Decode(
-          base::StringPiece(encoded_filedata), attached_filedata.get())) {
-        LOG(ERROR) << "Unable to attach file: " << attached_filename;
-        // Clear the filename so feedback_util doesn't try to attach the file.
-        attached_filename = "";
-      }
-    }
-  }
-#endif
-
-  // TODO(rkc): We are not setting the category tag here since this
-  // functionality is broken on the feedback server side. Fix this once the
-  // issue is resolved.
-  feedback_data_->set_category_tag(category_tag);
-  feedback_data_->set_description(description);
-  feedback_data_->set_image(image_ptr);
-  feedback_data_->set_page_url(page_url);
-  feedback_data_->set_profile(Profile::FromWebUI(web_ui()));
-  feedback_data_->set_user_email(user_email);
-#if defined(OS_CHROMEOS)
-  feedback_data_->set_attached_filedata(attached_filedata.Pass());
-  feedback_data_->set_attached_filename(attached_filename);
-  feedback_data_->set_send_sys_info(send_sys_info);
-  feedback_data_->set_timestamp(timestamp_);
-#endif
-
-  // Signal the feedback object that the data from the feedback page has been
-  // filled - the object will manage sending of the actual report.
-  feedback_data_->FeedbackPageDataComplete();
-
-  CloseFeedbackTab();
-}
-
-void FeedbackHandler::HandleCancel(const ListValue*) {
-  CloseFeedbackTab();
-}
-
-void FeedbackHandler::HandleOpenSystemTab(const ListValue* args) {
-#if defined(OS_CHROMEOS)
-  web_ui()->GetWebContents()->GetDelegate()->OpenURLFromTab(
-      web_ui()->GetWebContents(),
-      content::OpenURLParams(GURL(chrome::kChromeUISystemInfoURL),
-                             content::Referrer(),
-                             NEW_FOREGROUND_TAB,
-                             content::PAGE_TRANSITION_LINK,
-                             false));
-#endif
-}
-
-void FeedbackHandler::CloseFeedbackTab() {
-  ClobberScreenshotsSource();
-  tab_->GetDelegate()->CloseContents(tab_);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// FeedbackUI
-//
-////////////////////////////////////////////////////////////////////////////////
-FeedbackUI::FeedbackUI(content::WebUI* web_ui)
-    : WebDialogUI(web_ui) {
-  FeedbackHandler* handler = new FeedbackHandler(web_ui->GetWebContents());
-  web_ui->AddMessageHandler(handler);
-
-  // The handler's init will determine whether we show the error html page.
-  content::WebUIDataSource* html_source =
-      CreateFeedbackUIHTMLSource(handler->Init());
-
-  // Set up the chrome://feedback/ source.
-  Profile* profile = Profile::FromWebUI(web_ui);
-  content::WebUIDataSource::Add(profile, html_source);
-}
-
-#if defined(OS_CHROMEOS)
-// static
-void FeedbackUI::GetMostRecentScreenshots(
-    const base::FilePath& filepath,
-    std::vector<std::string>* saved_screenshots,
-    size_t max_saved) {
-  std::string pattern =
-      std::string(ScreenshotSource::kScreenshotPrefix) + "*" +
-                  ScreenshotSource::kScreenshotSuffix;
-  base::FileEnumerator screenshots(filepath, false,
-                                   base::FileEnumerator::FILES, pattern);
-  base::FilePath screenshot = screenshots.Next();
-
-  std::vector<std::string> screenshot_filepaths;
-  while (!screenshot.empty()) {
-    screenshot_filepaths.push_back(screenshot.BaseName().value());
-    screenshot = screenshots.Next();
-  }
-
-  size_t sort_size = std::min(max_saved, screenshot_filepaths.size());
-  std::partial_sort(screenshot_filepaths.begin(),
-                    screenshot_filepaths.begin() + sort_size,
-                    screenshot_filepaths.end(),
-                    ScreenshotTimestampComp);
-  for (size_t i = 0; i < sort_size; ++i)
-    saved_screenshots->push_back(
-        std::string(ScreenshotSource::kScreenshotUrlRoot) +
-        std::string(ScreenshotSource::kScreenshotSaved) +
-        screenshot_filepaths[i]);
-}
-#endif
diff --git a/chrome/browser/ui/webui/feedback_ui.h b/chrome/browser/ui/webui/feedback_ui.h
deleted file mode 100644
index 4bef57d..0000000
--- a/chrome/browser/ui/webui/feedback_ui.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_FEEDBACK_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_FEEDBACK_UI_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "ui/web_dialogs/web_dialog_ui.h"
-
-namespace base {
-class FilePath;
-}
-
-class FeedbackUI : public ui::WebDialogUI {
- public:
-  explicit FeedbackUI(content::WebUI* web_ui);
-
-#if defined(OS_CHROMEOS)
-  static void GetMostRecentScreenshots(
-      const base::FilePath& filepath,
-      std::vector<std::string>* saved_screenshots,
-      size_t max_saved);
-#endif
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FeedbackUI);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_FEEDBACK_UI_H_
diff --git a/chrome/browser/ui/webui/feedback_ui_unittest.cc b/chrome/browser/ui/webui/feedback_ui_unittest.cc
deleted file mode 100644
index 6bcf7d3..0000000
--- a/chrome/browser/ui/webui/feedback_ui_unittest.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2012 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.
-
-// The tests in this file are chromeos only.
-
-#include "chrome/browser/ui/webui/feedback_ui.h"
-
-#include "base/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-// This macro helps avoid wrapped lines in the test structs.
-#define FPL(x) FILE_PATH_LITERAL(x)
-
-namespace {
-
-// Simple function to create a file with |filename|.
-void CreateFile(const base::FilePath& filename) {
-  FILE* fp = file_util::OpenFile(filename, "w");
-  ASSERT_TRUE(fp != NULL);
-  file_util::CloseFile(fp);
-}
-
-std::string GetScreenshotFilename(const std::string timestamp) {
-  return std::string("Screenshot ") + timestamp + std::string(".png");
-}
-
-std::string GetScreenshotUrl(const std::string timestamp) {
-  return std::string("chrome://screenshots/saved/") +
-      GetScreenshotFilename(timestamp);
-}
-
-class FeedbackUITest : public testing::Test {
- public:
-  FeedbackUITest() {}
-  virtual ~FeedbackUITest() {}
-  virtual void SetUp() OVERRIDE {
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-  }
- protected:
-  void CreateScreenshotFile(const std::string& timestamp) {
-    base::FilePath filepath = temp_dir_.path().Append(
-        FILE_PATH_LITERAL(GetScreenshotFilename(timestamp)));
-    ASSERT_NO_FATAL_FAILURE(CreateFile(filepath));
-  }
-
-  base::ScopedTempDir temp_dir_;
-  std::vector<std::string> saved_screenshots_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FeedbackUITest);
-};
-
-TEST_F(FeedbackUITest, GetMostRecentScreenshotsNoScreenShot) {
-  // Create a random file.
-  base::FilePath filepath =
-      temp_dir_.path().Append(FILE_PATH_LITERAL("garbage.png"));
-  ASSERT_NO_FATAL_FAILURE(CreateFile(filepath));
-  // Expect getting no screenshot.
-  FeedbackUI::GetMostRecentScreenshots(
-      temp_dir_.path(), &saved_screenshots_, 2);
-  ASSERT_TRUE(saved_screenshots_.empty());
-}
-
-TEST_F(FeedbackUITest, GetMostRecentScreenshotsOneScreenShot) {
-  // Create 1 screenshot.
-  ASSERT_NO_FATAL_FAILURE(CreateScreenshotFile("20120416-152908"));
-  // Expect getting 1 screenshot.
-  FeedbackUI::GetMostRecentScreenshots(
-      temp_dir_.path(), &saved_screenshots_, 2);
-  ASSERT_EQ(1U, saved_screenshots_.size());
-  ASSERT_EQ(GetScreenshotUrl("20120416-152908"), saved_screenshots_[0]);
-}
-
-TEST_F(FeedbackUITest, GetMostRecentScreenshotsManyScreenShots) {
-  // Create 2 screenshots.
-  ASSERT_NO_FATAL_FAILURE(CreateScreenshotFile("20120416-152908"));
-  ASSERT_NO_FATAL_FAILURE(CreateScreenshotFile("20120416-162908"));
-  ASSERT_NO_FATAL_FAILURE(CreateScreenshotFile("20120413-152908"));
-  // Expect getting most recent 2 screenshots.
-  FeedbackUI::GetMostRecentScreenshots(
-      temp_dir_.path(), &saved_screenshots_, 2);
-  ASSERT_EQ(2U, saved_screenshots_.size());
-  ASSERT_EQ(GetScreenshotUrl("20120416-162908"), saved_screenshots_[0]);
-  ASSERT_EQ(GetScreenshotUrl("20120416-152908"), saved_screenshots_[1]);
-}
-
-}  // namespace
diff --git a/chrome/browser/ui/webui/help/help_handler.cc b/chrome/browser/ui/webui/help/help_handler.cc
index c8fe9cd..6fdc04f 100644
--- a/chrome/browser/ui/webui/help/help_handler.cc
+++ b/chrome/browser/ui/webui/help/help_handler.cc
@@ -417,7 +417,7 @@
   bool is_powerwash_allowed;
   if (!args->GetString(0, &channel) ||
       !args->GetBoolean(1, &is_powerwash_allowed)) {
-    LOG(ERROR) << "Can't parse SetReleaseTrack() args";
+    LOG(ERROR) << "Can't parse SetChannel() args";
     return;
   }
 
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.cc b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
index 9532bc4..cf4c1cb 100644
--- a/chrome/browser/ui/webui/help/version_updater_chromeos.cc
+++ b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -129,17 +129,12 @@
 
 void VersionUpdaterCros::SetChannel(const std::string& channel,
                                     bool is_powerwash_allowed) {
-  // On enterprise machines we can only use SetChannel to store the
-  // user choice in the lsb-release file but we can not modify the
-  // policy blob.  Therefore we only call SetString if the device is
-  // locally owned and the currently logged in user is the owner.
-  if (g_browser_process->browser_policy_connector()->IsEnterpriseManaged()) {
-    DBusThreadManager::Get()->GetUpdateEngineClient()->
-        SetChannel(channel, is_powerwash_allowed);
-  } else if (UserManager::Get()->IsCurrentUserOwner()) {
+  if (UserManager::Get()->IsCurrentUserOwner()) {
     // For local owner set the field in the policy blob.
     CrosSettings::Get()->SetString(chromeos::kReleaseChannel, channel);
   }
+  DBusThreadManager::Get()->GetUpdateEngineClient()->
+      SetChannel(channel, is_powerwash_allowed);
 }
 
 void VersionUpdaterCros::GetChannel(bool get_current_channel,
diff --git a/chrome/browser/ui/webui/history_ui.cc b/chrome/browser/ui/webui/history_ui.cc
index 74dde3a..760b436 100644
--- a/chrome/browser/ui/webui/history_ui.cc
+++ b/chrome/browser/ui/webui/history_ui.cc
@@ -180,13 +180,7 @@
   source->SetDefaultResource(IDR_HISTORY_HTML);
   source->SetUseJsonJSFormatV2();
   source->DisableDenyXFrameOptions();
-
-#if defined(OS_ANDROID) || defined(OS_IOS)
-  source->AddBoolean("isManagedProfile", false);
-#else
-  source->AddBoolean("isManagedProfile",
-      ManagedUserServiceFactory::GetForProfile(profile)->ProfileIsManaged());
-#endif
+  source->AddBoolean("isManagedProfile", profile->IsManaged());
 
   return source;
 }
@@ -370,8 +364,7 @@
   result->SetString("deviceType", device_type);
 
 #if defined(ENABLE_MANAGED_USERS)
-  DCHECK(managed_user_service);
-  if (managed_user_service->ProfileIsManaged()) {
+  if (managed_user_service) {
     const ManagedModeURLFilter* url_filter =
         managed_user_service->GetURLFilterForUIThread();
     int filtering_behavior =
@@ -697,7 +690,8 @@
   BookmarkModel* bookmark_model = BookmarkModelFactory::GetForProfile(profile);
   ManagedUserService* managed_user_service = NULL;
 #if defined(ENABLE_MANAGED_USERS)
-  managed_user_service = ManagedUserServiceFactory::GetForProfile(profile);
+  if (profile->IsManaged())
+    managed_user_service = ManagedUserServiceFactory::GetForProfile(profile);
 #endif
   ProfileSyncService* sync_service =
       ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
diff --git a/chrome/browser/ui/webui/inline_login_ui.cc b/chrome/browser/ui/webui/inline_login_ui.cc
index 8683184..967d214 100644
--- a/chrome/browser/ui/webui/inline_login_ui.cc
+++ b/chrome/browser/ui/webui/inline_login_ui.cc
@@ -6,14 +6,15 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/signin/token_service.h"
 #include "chrome/browser/signin/token_service_factory.h"
 #include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/common/url_constants.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
@@ -133,7 +134,8 @@
         OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS,
         true /* force_same_tab_navigation */,
         OneClickSigninSyncStarter::NO_CONFIRMATION,
-        SyncPromoUI::SOURCE_UNKNOWN);
+        signin::SOURCE_UNKNOWN,
+        OneClickSigninSyncStarter::Callback());
     web_ui()->CallJavascriptFunction("inline.login.closeDialog");
 #endif
   }
diff --git a/chrome/browser/ui/webui/inspect_ui.cc b/chrome/browser/ui/webui/inspect_ui.cc
index 32c5846..238dc63 100644
--- a/chrome/browser/ui/webui/inspect_ui.cc
+++ b/chrome/browser/ui/webui/inspect_ui.cc
@@ -83,8 +83,10 @@
 static const char kPidField[]  = "pid";
 static const char kAdbSerialField[] = "adbSerial";
 static const char kAdbModelField[] = "adbModel";
-static const char kAdbPackageField[] = "adbPackage";
+static const char kAdbBrowserNameField[] = "adbBrowserName";
 static const char kAdbPageIdField[] = "adbPageId";
+static const char kAdbBrowsersField[] = "browsers";
+static const char kAdbPagesField[] = "pages";
 
 DictionaryValue* BuildTargetDescriptor(
     const std::string& target_type,
@@ -211,10 +213,7 @@
     std::string page_id;
     if (args->GetSize() == 1 && args->GetDictionary(0, &data) &&
         data->GetString(kAdbPageIdField, &page_id)) {
-      scoped_refptr<DevToolsAdbBridge> adb_bridge =
-          DevToolsAdbBridge::Factory::GetForProfile(profile);
-      if (adb_bridge)
-        adb_bridge->Attach(page_id);
+      inspect_ui_->InspectRemotePage(page_id);
     }
     return;
   }
@@ -369,6 +368,14 @@
   observer_->InitUI();
 }
 
+void InspectUI::InspectRemotePage(const std::string& id) {
+  RemotePages::iterator it = remote_pages_.find(id);
+  if (it != remote_pages_.end()) {
+    Profile* profile = Profile::FromWebUI(web_ui());
+    it->second->Inspect(profile);
+  }
+}
+
 void InspectUI::PopulateLists() {
   std::set<RenderViewHost*> tab_rvhs;
   for (TabContentsIterator it; !it.done(); it.Next())
@@ -443,19 +450,42 @@
   return source;
 }
 
-void InspectUI::RemotePagesChanged(DevToolsAdbBridge::RemotePages* pages) {
-  ListValue targets;
-  for (DevToolsAdbBridge::RemotePages::iterator it = pages->begin();
-       it != pages->end(); ++it) {
-    DevToolsAdbBridge::RemotePage* page = it->get();
-    DictionaryValue* target_data = BuildTargetDescriptor(kAdbTargetType,
-        false, GURL(page->url()), page->title(), GURL(page->favicon_url()), 0,
-        0);
-    target_data->SetString(kAdbSerialField, page->serial());
-    target_data->SetString(kAdbModelField, page->model());
-    target_data->SetString(kAdbPackageField, page->package());
-    target_data->SetString(kAdbPageIdField, page->id());
-    targets.Append(target_data);
+void InspectUI::RemoteDevicesChanged(
+    DevToolsAdbBridge::RemoteDevices* devices) {
+  remote_pages_.clear();
+  ListValue device_list;
+  for (DevToolsAdbBridge::RemoteDevices::iterator dit = devices->begin();
+       dit != devices->end(); ++dit) {
+    DevToolsAdbBridge::RemoteDevice& device = *(dit->get());
+    DictionaryValue* device_data = new DictionaryValue();
+    device_data->SetString(kAdbModelField, device.model());
+    device_data->SetString(kAdbSerialField, device.serial());
+    ListValue* browser_list = new ListValue();
+    device_data->Set(kAdbBrowsersField, browser_list);
+
+    DevToolsAdbBridge::RemoteBrowsers& browsers = device.browsers();
+    for (DevToolsAdbBridge::RemoteBrowsers::iterator bit =
+        browsers.begin(); bit != browsers.end(); ++bit) {
+      DevToolsAdbBridge::RemoteBrowser& browser = *(bit->get());
+      DictionaryValue* browser_data = new DictionaryValue();
+      browser_data->SetString(kAdbBrowserNameField, browser.name());
+      ListValue* page_list = new ListValue();
+      browser_data->Set(kAdbPagesField, page_list);
+
+      DevToolsAdbBridge::RemotePages& pages = browser.pages();
+      for (DevToolsAdbBridge::RemotePages::iterator it =
+          pages.begin(); it != pages.end(); ++it) {
+        DevToolsAdbBridge::RemotePage* page =  it->get();
+        DictionaryValue* page_data = BuildTargetDescriptor(kAdbTargetType,
+            false, GURL(page->url()), page->title(), GURL(page->favicon_url()),
+            0, 0);
+        page_data->SetString(kAdbPageIdField, page->global_id());
+        page_list->Append(page_data);
+        remote_pages_[page->global_id()] = page;
+      }
+      browser_list->Append(browser_data);
+    }
+    device_list.Append(device_data);
   }
-  web_ui()->CallJavascriptFunction("populateDeviceLists", targets);
+  web_ui()->CallJavascriptFunction("populateDeviceLists", device_list);
 }
diff --git a/chrome/browser/ui/webui/inspect_ui.h b/chrome/browser/ui/webui/inspect_ui.h
index b415be8..a0f7952 100644
--- a/chrome/browser/ui/webui/inspect_ui.h
+++ b/chrome/browser/ui/webui/inspect_ui.h
@@ -23,6 +23,7 @@
   virtual ~InspectUI();
 
   void InitUI();
+  void InspectRemotePage(const std::string& id);
 
  private:
   class WorkerCreationDestructionListener;
@@ -40,14 +41,18 @@
   content::WebUIDataSource* CreateInspectUIHTMLSource();
 
   // DevToolsAdbBridge::Listener overrides.
-  virtual void RemotePagesChanged(
-      DevToolsAdbBridge::RemotePages* pages) OVERRIDE;
+  virtual void RemoteDevicesChanged(
+      DevToolsAdbBridge::RemoteDevices* devices) OVERRIDE;
 
   scoped_refptr<WorkerCreationDestructionListener> observer_;
 
   // A scoped container for notification registries.
   content::NotificationRegistrar registrar_;
 
+  typedef std::map<std::string, scoped_refptr<DevToolsAdbBridge::RemotePage> >
+      RemotePages;
+  RemotePages remote_pages_;
+
   DISALLOW_COPY_AND_ASSIGN(InspectUI);
 };
 
diff --git a/chrome/browser/ui/webui/memory_internals/memory_internals_proxy.cc b/chrome/browser/ui/webui/memory_internals/memory_internals_proxy.cc
index 509ccad..7394c2c 100644
--- a/chrome/browser/ui/webui/memory_internals/memory_internals_proxy.cc
+++ b/chrome/browser/ui/webui/memory_internals/memory_internals_proxy.cc
@@ -15,23 +15,36 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/memory_details.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_manager_factory.h"
+#include "chrome/browser/printing/background_printing_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
+#include "chrome/browser/search/instant_service.h"
+#include "chrome/browser/search/instant_service_factory.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_instant_controller.h"
+#include "chrome/browser/ui/browser_iterator.h"
 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
 #include "chrome/browser/ui/webui/memory_internals/memory_internals_handler.h"
 #include "chrome/common/render_messages.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 
 using content::BrowserThread;
 
+class Profile;
+
 namespace {
 
 class ProcessDetails : public MemoryDetails {
@@ -54,6 +67,65 @@
   DISALLOW_COPY_AND_ASSIGN(ProcessDetails);
 };
 
+base::DictionaryValue* FindProcessFromPid(base::ListValue* processes,
+                                          base::ProcessId pid) {
+  const size_t n = processes->GetSize();
+  for (size_t i = 0; i < n; ++i) {
+    base::DictionaryValue* process;
+    if (!processes->GetDictionary(i, &process))
+      return NULL;
+    int id;
+    if (process->GetInteger("pid", &id) && id == static_cast<int>(pid))
+      return process;
+  }
+  return NULL;
+}
+
+void GetAllWebContents(std::set<content::WebContents*>* web_contents) {
+  // Add all the existing WebContentses.
+#if defined(OS_ANDROID)
+  for (TabModelList::const_iterator iter = TabModelList::begin();
+       iter != TabModelList::end(); ++iter) {
+    TabModel* model = *iter;
+    for (int i = 0; i < model->GetTabCount(); ++i)
+      web_contents->insert(model->GetWebContentsAt(i));
+  }
+#else
+  for (TabContentsIterator iter; !iter.done(); iter.Next())
+    web_contents->insert(*iter);
+#endif
+  // Add all the prerender pages.
+  std::vector<Profile*> profiles(
+      g_browser_process->profile_manager()->GetLoadedProfiles());
+  for (size_t i = 0; i < profiles.size(); ++i) {
+    prerender::PrerenderManager* prerender_manager =
+        prerender::PrerenderManagerFactory::GetForProfile(profiles[i]);
+    if (!prerender_manager)
+      continue;
+    const std::vector<content::WebContents*> contentses =
+        prerender_manager->GetAllPrerenderingContents();
+    for (size_t j = 0; j < contentses.size(); ++j)
+      web_contents->insert(contentses[j]);
+  }
+  // Add all the Instant Extended prerendered NTPs.
+  for (size_t i = 0; i < profiles.size(); ++i) {
+    const InstantService* instant_service =
+        InstantServiceFactory::GetForProfile(profiles[i]);
+    if (instant_service && instant_service->GetNTPContents())
+      web_contents->insert(instant_service->GetNTPContents());
+  }
+#if !defined(OS_ANDROID)
+  // Add all the pages being background printed.
+  printing::BackgroundPrintingManager* printing_manager =
+      g_browser_process->background_printing_manager();
+  for (printing::BackgroundPrintingManager::WebContentsSet::const_iterator
+           iter = printing_manager->begin();
+       iter != printing_manager->end(); ++iter) {
+    web_contents->insert(*iter);
+  }
+#endif
+}
+
 }  // namespace
 
 class RendererDetails : public content::NotificationObserver {
@@ -173,20 +245,23 @@
     else
       process_info->Append(process);
 
-    // Information from MemoryDetails.
+    // From MemoryDetails.
     process->SetInteger("pid", iter->pid);
     process->SetString("type",
                        ProcessMemoryInformation::GetFullTypeNameInEnglish(
                            iter->process_type, iter->renderer_type));
     process->SetInteger("memory_private", iter->working_set.priv);
 
-    // TODO(peria): Replace |titles| with navigation histories.
     base::ListValue* titles = new ListValue();
     process->Set("titles", titles);
     for (size_t i = 0; i < iter->titles.size(); ++i)
       titles->AppendString(iter->titles[i]);
   }
 
+  std::set<content::WebContents*> web_contents;
+  GetAllWebContents(&web_contents);
+  ConvertTabsInformation(web_contents, process_info);
+
   RequestRendererDetails();
 }
 
@@ -219,8 +294,49 @@
     FinishCollection();
 }
 
+void MemoryInternalsProxy::ConvertTabsInformation(
+    const std::set<content::WebContents*>& web_contents,
+    base::ListValue* processes) {
+  for (std::set<content::WebContents*>::const_iterator
+           iter = web_contents.begin(); iter != web_contents.end(); ++iter) {
+    content::WebContents* web = *iter;
+    const base::ProcessId pid = base::GetProcId(
+        web->GetRenderProcessHost()->GetHandle());
+
+    // Find which process renders the web contents.
+    base::DictionaryValue* process = FindProcessFromPid(processes, pid);
+    if (!process)
+      continue;
+
+    // Prepare storage to register navigation histories.
+    base::ListValue* tabs;
+    if (!process->GetList("history", &tabs)) {
+      tabs = new base::ListValue();
+      process->Set("history", tabs);
+    }
+
+    base::DictionaryValue* tab = new base::DictionaryValue();
+    tabs->Append(tab);
+
+    base::ListValue* histories = new base::ListValue();
+    tab->Set("history", histories);
+
+    const content::NavigationController& controller = web->GetController();
+    const int entry_size = controller.GetEntryCount();
+    for (int i = 0; i < entry_size; ++i) {
+      content::NavigationEntry *entry = controller.GetEntryAtIndex(i);
+      base::DictionaryValue* history = new base::DictionaryValue();
+      histories->Append(history);
+      history->SetString("url", entry->GetURL().spec());
+      history->SetString("title", entry->GetTitle());
+      history->SetInteger("time", (base::Time::Now() -
+                                   entry->GetTimestamp()).InSeconds());
+    }
+    tab->SetInteger("index", controller.GetCurrentEntryIndex());
+  }
+}
+
 void MemoryInternalsProxy::FinishCollection() {
-  // System information, which is independent from processes.
   information_->SetInteger("uptime", base::SysInfo::Uptime());
   information_->SetString("os", base::SysInfo::OperatingSystemName());
   information_->SetString("os_version",
diff --git a/chrome/browser/ui/webui/memory_internals/memory_internals_proxy.h b/chrome/browser/ui/webui/memory_internals/memory_internals_proxy.h
index 271f639..34b2cf3 100644
--- a/chrome/browser/ui/webui/memory_internals/memory_internals_proxy.h
+++ b/chrome/browser/ui/webui/memory_internals/memory_internals_proxy.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_MEMORY_INTERNALS_MEMORY_INTERNALS_PROXY_H_
 #define CHROME_BROWSER_UI_WEBUI_MEMORY_INTERNALS_MEMORY_INTERNALS_PROXY_H_
 
+#include <set>
 #include <string>
 
 #include "base/memory/ref_counted.h"
@@ -17,13 +18,18 @@
 class RendererDetails;
 
 namespace base {
+class DictionaryValue;
 class ListValue;
 class Value;
 }
 
+namespace content {
+class WebContents;
+}
+
 class MemoryInternalsProxy
     : public base::RefCountedThreadSafe<
-          MemoryInternalsProxy, content::BrowserThread::DeleteOnUIThread> {
+        MemoryInternalsProxy, content::BrowserThread::DeleteOnUIThread> {
  public:
   MemoryInternalsProxy();
 
@@ -33,8 +39,7 @@
   // Unregisters a handler.
   void Detach();
 
-  // Sends a message to an internal client to send all process information it
-  // knows.
+  // Starts fetching memory usages of all processes.
   void StartFetch(const base::ListValue* list);
 
  private:
@@ -46,7 +51,7 @@
 
   virtual ~MemoryInternalsProxy();
 
-  // Enumerates all processes information, appending a few common information.
+  // Enumerates all processes information.
   void OnProcessAvailable(const ProcessData& browser);
 
   // Measures memory usage of V8.
@@ -54,6 +59,13 @@
                            const size_t v8_allocated,
                            const size_t v8_used);
 
+  // Converts information related to each WebContents, which represents contents
+  // in a tab, into Value format and appends it to information of the process
+  // which each tab belongs to.
+  void ConvertTabsInformation(
+      const std::set<content::WebContents*>& web_contents,
+      base::ListValue* processes);
+
   // Requests all renderer processes to get detailed memory information.
   void RequestRendererDetails();
 
diff --git a/chrome/browser/ui/webui/ntp/android/bookmarks_handler.cc b/chrome/browser/ui/webui/ntp/android/bookmarks_handler.cc
index a6a9683..406c8ae 100644
--- a/chrome/browser/ui/webui/ntp/android/bookmarks_handler.cc
+++ b/chrome/browser/ui/webui/ntp/android/bookmarks_handler.cc
@@ -274,10 +274,7 @@
   DictionaryValue result;
   DCHECK(partner_bookmarks_shim_ != NULL);
 
-  if (managed_bookmarks_shim_->HasManagedBookmarks()) {
-    PopulateBookmarksInFolder(
-        managed_bookmarks_shim_->GetManagedBookmarksRoot(), &result);
-  } else if (partner_bookmarks_shim_->HasPartnerBookmarks()) {
+  if (partner_bookmarks_shim_->HasPartnerBookmarks()) {
     PopulateBookmarksInFolder(
         partner_bookmarks_shim_->GetPartnerBookmarksRoot(), &result);
   } else {
diff --git a/chrome/browser/ui/webui/ntp/android/managed_bookmarks_shim.cc b/chrome/browser/ui/webui/ntp/android/managed_bookmarks_shim.cc
index c1f8f27..84fceb4 100644
--- a/chrome/browser/ui/webui/ntp/android/managed_bookmarks_shim.cc
+++ b/chrome/browser/ui/webui/ntp/android/managed_bookmarks_shim.cc
@@ -7,9 +7,11 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/prefs/pref_service.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/bookmarks/bookmark_model.h"
 #include "chrome/browser/policy/configuration_policy_handler_android.h"
 #include "chrome/common/pref_names.h"
+#include "google_apis/gaia/gaia_auth_util.h"
 #include "grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -63,8 +65,21 @@
 }
 
 void ManagedBookmarksShim::Reload() {
+  std::string domain;
+  std::string username = prefs_->GetString(prefs::kGoogleServicesUsername);
+  if (!username.empty())
+    domain = gaia::ExtractDomainName(username);
+  string16 root_node_name;
+  if (domain.empty()) {
+    root_node_name =
+        l10n_util::GetStringUTF16(IDS_POLICY_MANAGED_BOOKMARKS_DEFAULT_NAME);
+  } else {
+    root_node_name = l10n_util::GetStringFUTF16(IDS_POLICY_MANAGED_BOOKMARKS,
+                                                base::UTF8ToUTF16(domain));
+  }
+
   root_.reset(new BookmarkPermanentNode(0));
-  root_->SetTitle(l10n_util::GetStringUTF16(IDS_POLICY_MANAGED_BOOKMARKS));
+  root_->SetTitle(root_node_name);
 
   const base::ListValue* list = prefs_->GetList(prefs::kManagedBookmarks);
   int64 id = 1;
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index ba2a3e4..6d7d9ee 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -47,7 +47,6 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/common/web_application_info.h"
-#include "components/user_prefs/pref_registry_syncable.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/common/favicon_url.h"
@@ -82,6 +81,11 @@
       "Apps.AppLauncherPromo", value, apps::APP_LAUNCHER_PROMO_MAX);
 }
 
+// This is used to avoid a DCHECK due to an unhandled WebUI callback. The
+// JavaScript used to switch between pages sends "pageSelected" which is used
+// in the context of the NTP for recording metrics we don't need here.
+void NoOpCallback(const ListValue* args) {}
+
 }  // namespace
 
 AppLauncherHandler::AppInstallInfo::AppInstallInfo() {}
@@ -124,19 +128,21 @@
 
   bool icon_big_exists = true;
   // Instead of setting grayscale here, we do it in apps_page.js.
-  GURL icon_big =
-      ExtensionIconSource::GetIconURL(extension,
-                                      extension_misc::EXTENSION_ICON_LARGE,
-                                      ExtensionIconSet::MATCH_BIGGER,
-                                      false, &icon_big_exists);
+  GURL icon_big = extensions::ExtensionIconSource::GetIconURL(
+      extension,
+      extension_misc::EXTENSION_ICON_LARGE,
+      ExtensionIconSet::MATCH_BIGGER,
+      false,
+      &icon_big_exists);
   value->SetString("icon_big", icon_big.spec());
   value->SetBoolean("icon_big_exists", icon_big_exists);
   bool icon_small_exists = true;
-  GURL icon_small =
-      ExtensionIconSource::GetIconURL(extension,
-                                      extension_misc::EXTENSION_ICON_BITTY,
-                                      ExtensionIconSet::MATCH_BIGGER,
-                                      false, &icon_small_exists);
+  GURL icon_small = extensions::ExtensionIconSource::GetIconURL(
+      extension,
+      extension_misc::EXTENSION_ICON_BITTY,
+      ExtensionIconSet::MATCH_BIGGER,
+      false,
+      &icon_small_exists);
   value->SetString("icon_small", icon_small.spec());
   value->SetBoolean("icon_small_exists", icon_small_exists);
   value->SetInteger("launch_container",
@@ -222,6 +228,7 @@
   web_ui()->RegisterMessageCallback("onLearnMore",
       base::Bind(&AppLauncherHandler::OnLearnMore,
                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback("pageSelected", base::Bind(&NoOpCallback));
 }
 
 void AppLauncherHandler::Observe(int type,
@@ -491,7 +498,7 @@
     CoreAppLauncherHandler::RecordAppLaunchType(launch_bucket,
                                                 extension->GetType());
   } else {
-    RecordWebStoreLaunch();
+    CoreAppLauncherHandler::RecordWebStoreLaunch();
   }
 
   if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB ||
@@ -749,46 +756,10 @@
           apps::prefs::kShowAppLauncherPromo)));
 }
 
-// static
-void AppLauncherHandler::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterListPref(prefs::kNtpAppPageNames,
-                             user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
-}
-
 void AppLauncherHandler::CleanupAfterUninstall() {
   extension_id_prompting_.clear();
 }
 
-// static
-void AppLauncherHandler::RecordAppListSearchLaunch(const Extension* extension) {
-  extension_misc::AppLaunchBucket bucket =
-      extension_misc::APP_LAUNCH_APP_LIST_SEARCH;
-  if (extension->id() == extension_misc::kWebStoreAppId)
-    bucket = extension_misc::APP_LAUNCH_APP_LIST_SEARCH_WEBSTORE;
-  else if (extension->id() == extension_misc::kChromeAppId)
-    bucket = extension_misc::APP_LAUNCH_APP_LIST_SEARCH_CHROME;
-  CoreAppLauncherHandler::RecordAppLaunchType(bucket, extension->GetType());
-}
-
-// static
-void AppLauncherHandler::RecordAppListMainLaunch(const Extension* extension) {
-  extension_misc::AppLaunchBucket bucket =
-      extension_misc::APP_LAUNCH_APP_LIST_MAIN;
-  if (extension->id() == extension_misc::kWebStoreAppId)
-    bucket = extension_misc::APP_LAUNCH_APP_LIST_MAIN_WEBSTORE;
-  else if (extension->id() == extension_misc::kChromeAppId)
-    bucket = extension_misc::APP_LAUNCH_APP_LIST_MAIN_CHROME;
-  CoreAppLauncherHandler::RecordAppLaunchType(bucket, extension->GetType());
-}
-
-// static
-void AppLauncherHandler::RecordWebStoreLaunch() {
-  CoreAppLauncherHandler::RecordAppLaunchType(
-      extension_misc::APP_LAUNCH_NTP_WEBSTORE,
-      extensions::Manifest::TYPE_HOSTED_APP);
-}
-
 void AppLauncherHandler::PromptToEnableApp(const std::string& extension_id) {
   if (!extension_id_prompting_.empty())
     return;  // Only one prompt at a time.
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.h b/chrome/browser/ui/webui/ntp/app_launcher_handler.h
index cd5c6c7..1cc91fc 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.h
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.h
@@ -31,10 +31,6 @@
 struct FaviconImageResult;
 }
 
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
 // The handler for Javascript messages related to the "apps" view.
 class AppLauncherHandler : public content::WebUIMessageHandler,
                            public ExtensionUninstallDialog::Delegate,
@@ -110,15 +106,6 @@
   void StopShowingAppLauncherPromo(const base::ListValue* args);
   void OnLearnMore(const base::ListValue* args);
 
-  // Register app launcher preferences.
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
-  // Records an app launch from the search view of the app list.
-  static void RecordAppListSearchLaunch(const extensions::Extension* extension);
-
-  // Records an app launch from the main view of the app list.
-  static void RecordAppListMainLaunch(const extensions::Extension* extension);
-
  private:
   struct AppInstallInfo {
     AppInstallInfo();
@@ -133,9 +120,6 @@
   // Reset some instance flags we use to track the currently uninstalling app.
   void CleanupAfterUninstall();
 
-  // Records a web store launch in the appropriate histograms.
-  static void RecordWebStoreLaunch();
-
   // Prompts the user to re-enable the app for |extension_id|.
   void PromptToEnableApp(const std::string& extension_id);
 
diff --git a/chrome/browser/ui/webui/ntp/core_app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/core_app_launcher_handler.cc
index 18b1ae0..cdef973 100644
--- a/chrome/browser/ui/webui/ntp/core_app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/core_app_launcher_handler.cc
@@ -9,6 +9,9 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/pref_names.h"
+#include "components/user_prefs/pref_registry_syncable.h"
 #include "content/public/browser/web_ui.h"
 #include "net/base/escape.h"
 
@@ -37,6 +40,43 @@
   }
 }
 
+// static
+void CoreAppLauncherHandler::RecordAppListSearchLaunch(
+    const extensions::Extension* extension) {
+  extension_misc::AppLaunchBucket bucket =
+      extension_misc::APP_LAUNCH_APP_LIST_SEARCH;
+  if (extension->id() == extension_misc::kWebStoreAppId)
+    bucket = extension_misc::APP_LAUNCH_APP_LIST_SEARCH_WEBSTORE;
+  else if (extension->id() == extension_misc::kChromeAppId)
+    bucket = extension_misc::APP_LAUNCH_APP_LIST_SEARCH_CHROME;
+  RecordAppLaunchType(bucket, extension->GetType());
+}
+
+// static
+void CoreAppLauncherHandler::RecordAppListMainLaunch(
+    const extensions::Extension* extension) {
+  extension_misc::AppLaunchBucket bucket =
+      extension_misc::APP_LAUNCH_APP_LIST_MAIN;
+  if (extension->id() == extension_misc::kWebStoreAppId)
+    bucket = extension_misc::APP_LAUNCH_APP_LIST_MAIN_WEBSTORE;
+  else if (extension->id() == extension_misc::kChromeAppId)
+    bucket = extension_misc::APP_LAUNCH_APP_LIST_MAIN_CHROME;
+  RecordAppLaunchType(bucket, extension->GetType());
+}
+
+// static
+void CoreAppLauncherHandler::RecordWebStoreLaunch() {
+  RecordAppLaunchType(extension_misc::APP_LAUNCH_NTP_WEBSTORE,
+                      extensions::Manifest::TYPE_HOSTED_APP);
+}
+
+// static
+void CoreAppLauncherHandler::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  registry->RegisterListPref(prefs::kNtpAppPageNames,
+                             user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+}
+
 void CoreAppLauncherHandler::HandleRecordAppLaunchByUrl(
     const base::ListValue* args) {
   std::string url;
diff --git a/chrome/browser/ui/webui/ntp/core_app_launcher_handler.h b/chrome/browser/ui/webui/ntp/core_app_launcher_handler.h
index 1e9fdc9..c19f1f0 100644
--- a/chrome/browser/ui/webui/ntp/core_app_launcher_handler.h
+++ b/chrome/browser/ui/webui/ntp/core_app_launcher_handler.h
@@ -14,6 +14,14 @@
 #include "chrome/common/extensions/extension_constants.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
+namespace extensions {
+class Extension;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
 class Profile;
 
 class CoreAppLauncherHandler : public content::WebUIMessageHandler {
@@ -25,6 +33,18 @@
   static void RecordAppLaunchType(extension_misc::AppLaunchBucket bucket,
                                   extensions::Manifest::Type app_type);
 
+  // Register app launcher preferences.
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+  // Records an app launch from the search view of the app list.
+  static void RecordAppListSearchLaunch(const extensions::Extension* extension);
+
+  // Records an app launch from the main view of the app list.
+  static void RecordAppListMainLaunch(const extensions::Extension* extension);
+
+  // Records a web store launch in the appropriate histograms.
+  static void RecordWebStoreLaunch();
+
  private:
   // Callback for the "recordAppLaunchByUrl" message. Takes an escaped URL and
   // a launch source(integer), and if the URL represents an app, records the
diff --git a/chrome/browser/ui/webui/ntp/new_tab_page_sync_handler.cc b/chrome/browser/ui/webui/ntp/new_tab_page_sync_handler.cc
index c1177ca..35dee3a 100644
--- a/chrome/browser/ui/webui/ntp/new_tab_page_sync_handler.cc
+++ b/chrome/browser/ui/webui/ntp/new_tab_page_sync_handler.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -137,7 +138,7 @@
       chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
   if (!browser || browser->IsAttemptingToCloseBrowser())
     return;
-  chrome::ShowBrowserSignin(browser, SyncPromoUI::SOURCE_NTP_LINK);
+  chrome::ShowBrowserSignin(browser, signin::SOURCE_NTP_LINK);
 
   if (sync_service_->HasSyncSetupCompleted()) {
     string16 user = UTF8ToUTF16(SigninManagerFactory::GetForProfile(
diff --git a/chrome/browser/ui/webui/ntp/new_tab_ui.cc b/chrome/browser/ui/webui/ntp/new_tab_ui.cc
index ac163de..4f9c51d 100644
--- a/chrome/browser/ui/webui/ntp/new_tab_ui.cc
+++ b/chrome/browser/ui/webui/ntp/new_tab_ui.cc
@@ -259,7 +259,7 @@
 void NewTabUI::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
 #if !defined(OS_ANDROID)
-  AppLauncherHandler::RegisterProfilePrefs(registry);
+  CoreAppLauncherHandler::RegisterProfilePrefs(registry);
   NewTabPageHandler::RegisterProfilePrefs(registry);
   if (NewTabUI::IsDiscoveryInNTPEnabled())
     SuggestionsHandler::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/ui/webui/ntp/ntp_login_handler.cc b/chrome/browser/ui/webui/ntp/ntp_login_handler.cc
index f20351e..4774a9d 100644
--- a/chrome/browser/ui/webui/ntp/ntp_login_handler.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_login_handler.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -29,7 +30,6 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
 #include "chrome/browser/web_resource/promo_resource_service.h"
 #include "chrome/common/pref_names.h"
@@ -139,11 +139,12 @@
 
   if (username.empty()) {
 #if !defined(OS_ANDROID)
-    // The user isn't signed in, show the sync promo.
-    if (SyncPromoUI::ShouldShowSyncPromo(profile)) {
-      SyncPromoUI::Source source =
+    // The user isn't signed in, show the sign in promo.
+    if (signin::ShouldShowPromo(profile)) {
+      signin::Source source =
           (web_contents->GetURL().spec() == chrome::kChromeUIAppsURL) ?
-              SyncPromoUI::SOURCE_APPS_PAGE_LINK : SyncPromoUI::SOURCE_NTP_LINK;
+              signin::SOURCE_APPS_PAGE_LINK :
+              signin::SOURCE_NTP_LINK;
       chrome::ShowBrowserSignin(browser, source);
       RecordInHistogram(NTP_SIGN_IN_PROMO_CLICKED);
     }
@@ -196,7 +197,7 @@
   Browser* browser =
       chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
   if (browser)
-    chrome::ShowBrowserSignin(browser, SyncPromoUI::SOURCE_NTP_LINK);
+    chrome::ShowBrowserSignin(browser, signin::SOURCE_NTP_LINK);
 }
 
 void NTPLoginHandler::UpdateLogin() {
@@ -228,8 +229,8 @@
     }
   } else {
 #if !defined(OS_ANDROID)
-    // Android uses a custom sync promo
-    if (SyncPromoUI::ShouldShowSyncPromo(profile)) {
+    // Android uses a custom sign in promo.
+    if (signin::ShouldShowPromo(profile)) {
       string16 signed_in_link = l10n_util::GetStringUTF16(
           IDS_SYNC_PROMO_NOT_SIGNED_IN_STATUS_LINK);
       signed_in_link = CreateSpanWithClass(signed_in_link, "link-span");
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
index 5eb0b11..42a5eab 100644
--- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -436,12 +436,10 @@
   // feature is enabled.
   load_time_data.SetBoolean("isSwipeTrackingFromScrollEventsEnabled",
                             is_swipe_tracking_from_scroll_events_enabled_);
-  #if defined(ENABLE_MANAGED_USERS)
-    // Managed users can not have apps installed currently so there's no need to
-    // show the app cards.
-    if (ManagedUserService::ProfileIsManaged(profile_))
-      should_show_apps_page_ = false;
-  #endif
+  // Managed users can not have apps installed currently so there's no need to
+  // show the app cards.
+  if (profile_->IsManaged())
+    should_show_apps_page_ = false;
   load_time_data.SetBoolean("showApps", should_show_apps_page_);
   load_time_data.SetBoolean("showWebStoreIcon",
                             !prefs->GetBoolean(prefs::kHideWebStoreIcon));
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index da78bb0..8831a38 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -85,8 +85,7 @@
 #include "ui/webui/web_ui_util.h"
 
 #if defined(ENABLE_MANAGED_USERS)
-#include "chrome/browser/managed_mode/managed_user_registration_service.h"
-#include "chrome/browser/managed_mode/managed_user_registration_service_factory.h"
+#include "chrome/browser/managed_mode/managed_user_registration_utility.h"
 #include "chrome/browser/managed_mode/managed_user_service.h"
 #include "chrome/browser/managed_mode/managed_user_service_factory.h"
 #endif
@@ -509,7 +508,7 @@
 
   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
   values->SetBoolean("enableStickyKeys",
-                     command_line.HasSwitch(switches::kEnableStickyKeys));
+                     !command_line.HasSwitch(switches::kDisableStickyKeys));
 #endif
 
 #if defined(OS_MACOSX)
@@ -522,10 +521,8 @@
   if (ShouldShowMultiProfilesUserList(GetDesktopType()))
     values->Set("profilesInfo", GetProfilesInfoList().release());
 
-#if defined(ENABLE_MANAGED_USERS)
   values->SetBoolean("profileIsManaged",
-      ManagedUserService::ProfileIsManaged(Profile::FromWebUI(web_ui())));
-#endif
+                     Profile::FromWebUI(web_ui())->IsManaged());
 
 #if !defined(OS_CHROMEOS)
   values->SetBoolean(
@@ -1006,6 +1003,12 @@
       break;
 #endif
     case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED:
+      // If the browser shuts down during supervised-profile creation, deleting
+      // the unregistered supervised-user profile triggers this notification,
+      // but the RenderViewHost the profile info would be sent to has already
+      // been destroyed.
+      if (!web_ui()->GetWebContents()->GetRenderViewHost())
+        return;
       SendProfilesInfo();
       break;
     case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL:
@@ -1098,13 +1101,11 @@
 }
 
 void BrowserOptionsHandler::CreateProfile(const ListValue* args) {
-#if defined(ENABLE_MANAGED_USERS)
   // This handler could have been called in managed mode, for example because
   // the user fiddled with the web inspector. Silently return in this case.
   Profile* current_profile = Profile::FromWebUI(web_ui());
-  if (ManagedUserService::ProfileIsManaged(current_profile))
+  if (current_profile->IsManaged())
     return;
-#endif
 
   if (!profiles::IsMultipleProfilesEnabled())
     return;
@@ -1133,13 +1134,9 @@
                  managed_user);
 
   if (managed_user && ManagedUserService::AreManagedUsersEnabled()) {
-#if defined(ENABLE_MANAGED_USERS)
     callbacks.push_back(
         base::Bind(&BrowserOptionsHandler::RegisterNewManagedUser,
                    weak_ptr_factory_.GetWeakPtr(), show_user_feedback));
-#else
-    NOTREACHED();
-#endif
   } else {
     callbacks.push_back(show_user_feedback);
   }
@@ -1161,11 +1158,14 @@
 
   ManagedUserService* managed_user_service =
       ManagedUserServiceFactory::GetForProfile(new_profile);
-  DCHECK(managed_user_service->ProfileIsManaged());
 
   // Register the managed user using the profile of the custodian.
-  managed_user_service->RegisterAndInitSync(Profile::FromWebUI(web_ui()),
-                                            callback);
+  managed_user_registration_utility_ =
+      ManagedUserRegistrationUtility::Create(Profile::FromWebUI(web_ui()));
+  managed_user_service->RegisterAndInitSync(
+      managed_user_registration_utility_.get(),
+      Profile::FromWebUI(web_ui()),
+      callback);
 }
 
 void BrowserOptionsHandler::RecordProfileCreationMetrics(
@@ -1253,12 +1253,10 @@
 }
 
 void BrowserOptionsHandler::DeleteProfileAtPath(base::FilePath file_path) {
-#if defined(ENABLE_MANAGED_USERS)
   // This handler could have been called in managed mode, for example because
   // the user fiddled with the web inspector. Silently return in this case.
-  if (ManagedUserService::ProfileIsManaged(Profile::FromWebUI(web_ui())))
+  if (Profile::FromWebUI(web_ui())->IsManaged())
     return;
-#endif
 
   if (!profiles::IsMultipleProfilesEnabled())
     return;
@@ -1286,7 +1284,7 @@
   // Non-managed user creation cannot be canceled. (Creating a non-managed
   // profile shouldn't take significant time, and it can easily be deleted
   // afterward.)
-  if (!ManagedUserService::ProfileIsManaged(new_profile))
+  if (!new_profile->IsManaged())
     return;
 
   if (user_initiated) {
@@ -1298,10 +1296,8 @@
     RecordProfileCreationMetrics(Profile::CREATE_STATUS_CANCELED);
   }
 
-  ManagedUserRegistrationService* registration_service =
-      ManagedUserRegistrationServiceFactory::GetForProfile(
-          Profile::FromWebUI(web_ui()));
-  registration_service->CancelPendingRegistration();
+  DCHECK(managed_user_registration_utility_.get());
+  managed_user_registration_utility_.reset();
 
   // Cancelling registration means the callback passed into
   // RegisterAndInitSync() won't be called, so the cleanup must be done here.
@@ -1315,7 +1311,7 @@
   bool is_native_theme = false;
 
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-  bool profile_is_managed = ManagedUserService::ProfileIsManaged(profile);
+  bool profile_is_managed = profile->IsManaged();
   is_native_theme = theme_service->UsingNativeTheme();
   base::FundamentalValue native_theme_enabled(!is_native_theme &&
                                               !profile_is_managed);
@@ -1359,7 +1355,7 @@
 scoped_ptr<DictionaryValue> BrowserOptionsHandler::GetSyncStateDictionary() {
   scoped_ptr<DictionaryValue> sync_status(new DictionaryValue);
   Profile* profile = Profile::FromWebUI(web_ui());
-  if (ManagedUserService::ProfileIsManaged(profile)) {
+  if (profile->IsManaged()) {
     sync_status->SetBoolean("supervisedUser", true);
     sync_status->SetBoolean("signinAllowed", false);
     return sync_status.Pass();
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.h b/chrome/browser/ui/webui/options/browser_options_handler.h
index 06199f0..8c81951 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.h
+++ b/chrome/browser/ui/webui/options/browser_options_handler.h
@@ -30,6 +30,7 @@
 class AutocompleteController;
 class CloudPrintSetupHandler;
 class CustomHomePagesTableModel;
+class ManagedUserRegistrationUtility;
 class TemplateURLService;
 
 namespace options {
@@ -345,6 +346,8 @@
 
   PrefChangeRegistrar profile_pref_registrar_;
 
+  scoped_ptr<ManagedUserRegistrationUtility> managed_user_registration_utility_;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserOptionsHandler);
 };
 
diff --git a/chrome/browser/ui/webui/options/certificate_manager_browsertest.js b/chrome/browser/ui/webui/options/certificate_manager_browsertest.js
index 35320c1..112ca7f 100644
--- a/chrome/browser/ui/webui/options/certificate_manager_browsertest.js
+++ b/chrome/browser/ui/webui/options/certificate_manager_browsertest.js
@@ -187,7 +187,6 @@
   $('personalCertsTab-view').click();
 
   Mock4JS.verifyAllMocks();
-  Mock4JS.clearMocksToVerify();
 
   this.mockHandler.expects(once()).deleteCertificate(['c1']).will(callFunction(
       function() {
diff --git a/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc
index 9dad157..8861b50 100644
--- a/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc
@@ -23,8 +23,8 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/display.h"
 #include "ui/gfx/rect.h"
-#include "ui/gfx/safe_integer_conversions.h"
 #include "ui/gfx/screen.h"
+#include "ui/gfx/size_conversions.h"
 
 using ash::internal::DisplayManager;
 
@@ -53,6 +53,11 @@
   return display_id;
 }
 
+bool CompareResolution(ash::internal::Resolution r1,
+                       ash::internal::Resolution r2) {
+  return r1.size.GetArea() < r2.size.GetArea();
+}
+
 }  // namespace
 
 DisplayOptionsHandler::DisplayOptionsHandler() {
@@ -131,6 +136,10 @@
       base::Bind(&DisplayOptionsHandler::HandleSetUIScale,
                  base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
+      "setResolution",
+      base::Bind(&DisplayOptionsHandler::HandleSetResolution,
+                 base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       "setOrientation",
       base::Bind(&DisplayOptionsHandler::HandleSetOrientation,
                  base::Unretained(this)));
@@ -179,30 +188,54 @@
     js_display->SetBoolean("isInternal", display.IsInternal());
     js_display->SetInteger("orientation",
                            static_cast<int>(display_info.rotation()));
-    std::vector<float> ui_scales = DisplayManager::GetScalesForDisplay(
-        display_info);
-    base::ListValue* js_scales = new base::ListValue();
-    gfx::SizeF base_size = display_info.bounds_in_pixel().size();
-    base_size.Scale(1.0f / display.device_scale_factor());
-    if (display_info.rotation() == gfx::Display::ROTATE_90 ||
-        display_info.rotation() == gfx::Display::ROTATE_270) {
-      float tmp = base_size.width();
-      base_size.set_width(base_size.height());
-      base_size.set_height(tmp);
+    std::vector<ash::internal::Resolution> resolutions;
+    std::vector<float> ui_scales;
+    if (display.IsInternal()) {
+      ui_scales = DisplayManager::GetScalesForDisplay(display_info);
+      gfx::SizeF base_size = display_info.bounds_in_pixel().size();
+      base_size.Scale(1.0f / display.device_scale_factor());
+      if (display_info.rotation() == gfx::Display::ROTATE_90 ||
+          display_info.rotation() == gfx::Display::ROTATE_270) {
+        float tmp = base_size.width();
+        base_size.set_width(base_size.height());
+        base_size.set_height(tmp);
+      }
+      for (size_t i = 0; i < ui_scales.size(); ++i) {
+        gfx::SizeF new_size = base_size;
+        new_size.Scale(ui_scales[i]);
+        resolutions.push_back(ash::internal::Resolution(
+            gfx::ToFlooredSize(new_size), false /* interlaced */));
+      }
+    } else {
+      for (size_t i = 0; i < display_info.resolutions().size(); ++i)
+        resolutions.push_back(display_info.resolutions()[i]);
     }
+    std::sort(resolutions.begin(), resolutions.end(), CompareResolution);
 
-    for (size_t i = 0; i < ui_scales.size(); ++i) {
-      base::DictionaryValue* scale_info = new base::DictionaryValue();
-      scale_info->SetDouble("scale", ui_scales[i]);
-      scale_info->SetInteger(
-          "width", gfx::ToFlooredInt(base_size.width() * ui_scales[i]));
-      scale_info->SetInteger(
-          "height", gfx::ToFlooredInt(base_size.height() * ui_scales[i]));
-      scale_info->SetBoolean("selected",
-                             display_info.ui_scale() == ui_scales[i]);
-      js_scales->Append(scale_info);
+    base::ListValue* js_resolutions = new base::ListValue();
+    gfx::Size current_size(bounds.width() * display.device_scale_factor(),
+                           bounds.height() * display.device_scale_factor());
+    for (size_t i = 0; i < resolutions.size(); ++i) {
+      base::DictionaryValue* resolution_info = new base::DictionaryValue();
+      if (!ui_scales.empty()) {
+        resolution_info->SetDouble("scale", ui_scales[i]);
+        if (ui_scales[i] == 1.0f)
+          resolution_info->SetBoolean("isBest", true);
+        resolution_info->SetBoolean(
+            "selected", display_info.ui_scale() == ui_scales[i]);
+      } else {
+        // Picks the largest one as the "best", which is the last element
+        // because |resolutions| is sorted by its area.
+        if (i == resolutions.size() - 1)
+          resolution_info->SetBoolean("isBest", true);
+        resolution_info->SetBoolean(
+            "selected", (resolutions[i].size == current_size));
+      }
+      resolution_info->SetInteger("width", resolutions[i].size.width());
+      resolution_info->SetInteger("height",resolutions[i].size.height());
+      js_resolutions->Append(resolution_info);
     }
-    js_display->Set("uiScales", js_scales);
+    js_display->Set("resolutions", js_resolutions);
     js_displays.Append(js_display);
   }
 
@@ -284,20 +317,37 @@
   if (display_id == gfx::Display::kInvalidDisplayID)
     return;
 
-  std::string ui_scale_value;
   double ui_scale = 0.0f;
-  if (!args->GetString(1, &ui_scale_value)) {
-    LOG(ERROR) << "Ca't find new ui_scale";
-    return;
-  }
-  if (!base::StringToDouble(ui_scale_value, &ui_scale)) {
-    LOG(ERROR) << "Invalid ui_scale: " << ui_scale_value;
+  if (!args->GetDouble(1, &ui_scale) || ui_scale == 0.0f) {
+    LOG(ERROR) << "Can't find new ui_scale";
     return;
   }
 
   GetDisplayManager()->SetDisplayUIScale(display_id, ui_scale);
 }
 
+void DisplayOptionsHandler::HandleSetResolution(const base::ListValue* args) {
+  DCHECK(!args->empty());
+  int64 display_id = GetDisplayId(args);
+  if (display_id == gfx::Display::kInvalidDisplayID)
+    return;
+
+  double width = 0.0f;
+  double height = 0.0f;
+  if (!args->GetDouble(1, &width) || width == 0.0f) {
+    LOG(ERROR) << "Can't find new width";
+    return;
+  }
+  if (!args->GetDouble(2, &height) || height == 0.0f) {
+    LOG(ERROR) << "Can't find new height";
+    return;
+  }
+
+  // TODO(mukai): creates a confirmation dialog.
+  GetDisplayManager()->SetDisplayResolution(
+      display_id, gfx::ToFlooredSize(gfx::SizeF(width, height)));
+}
+
 void DisplayOptionsHandler::HandleSetOrientation(const base::ListValue* args) {
   DCHECK(!args->empty());
 
diff --git a/chrome/browser/ui/webui/options/chromeos/display_options_handler.h b/chrome/browser/ui/webui/options/chromeos/display_options_handler.h
index e34617b..1606c72 100644
--- a/chrome/browser/ui/webui/options/chromeos/display_options_handler.h
+++ b/chrome/browser/ui/webui/options/chromeos/display_options_handler.h
@@ -59,6 +59,7 @@
   void HandleSetPrimary(const base::ListValue* args);
   void HandleDisplayLayout(const base::ListValue* args);
   void HandleSetUIScale(const base::ListValue* args);
+  void HandleSetResolution(const base::ListValue* args);
   void HandleSetOrientation(const base::ListValue* args);
 
   DISALLOW_COPY_AND_ASSIGN(DisplayOptionsHandler);
diff --git a/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc
index 7c69089..5610fa0 100644
--- a/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc
@@ -40,7 +40,6 @@
 #include "chrome/browser/chromeos/options/network_connect.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/sim_dialog_delegate.h"
-#include "chrome/browser/chromeos/status/network_menu_icon.h"
 #include "chrome/browser/chromeos/ui_proxy_config_service.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
@@ -459,7 +458,7 @@
       connectable_(false),
       connection_type_(favorite->type()),
       remembered_(true),
-      shared_(favorite->IsShared()),
+      shared_(!favorite->IsPrivate()),
       policy_managed_(favorite->IsManaged()) {
   if (favorite->type() == flimflam::kTypeEthernet)
     name_ = l10n_util::GetStringUTF8(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET);
@@ -1562,7 +1561,7 @@
                         ash::network_connect::ErrorString(network->error()));
 
   dictionary->SetBoolean(kTagRemembered, !network->profile_path().empty());
-  bool shared = network->IsShared();
+  bool shared = !network->IsPrivate();
   dictionary->SetBoolean(kTagShared, shared);
 
   const std::string& type = network->type();
@@ -1633,7 +1632,7 @@
                          CommandLine::ForCurrentProcess()->HasSwitch(
                              chromeos::switches::kEnableCarrierSwitching));
   // Cellular network / connection settings.
-  dictionary->SetString(kTagNetworkTechnology, cellular->technology());
+  dictionary->SetString(kTagNetworkTechnology, cellular->network_technology());
   dictionary->SetString(kTagActivationState,
                         ActivationStateString(cellular->activation_state()));
   dictionary->SetString(kTagRoamingState,
@@ -1645,10 +1644,15 @@
                             IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL) :
                         l10n_util::GetStringUTF8(
                             IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
-  CopyStringFromDictionary(shill_properties, flimflam::kOperatorNameProperty,
-                          kTagOperatorName, dictionary);
-  CopyStringFromDictionary(shill_properties, flimflam::kOperatorCodeProperty,
-                          kTagOperatorCode, dictionary);
+
+  const base::DictionaryValue* serving_operator = NULL;
+  if (shill_properties.GetDictionaryWithoutPathExpansion(
+          flimflam::kServingOperatorProperty, &serving_operator)) {
+    CopyStringFromDictionary(*serving_operator, flimflam::kOperatorNameKey,
+                             kTagOperatorName, dictionary);
+    CopyStringFromDictionary(*serving_operator, flimflam::kOperatorCodeKey,
+                             kTagOperatorCode, dictionary);
+  }
 
   const base::DictionaryValue* olp = NULL;
   if (shill_properties.GetDictionaryWithoutPathExpansion(
@@ -1785,9 +1789,10 @@
       // because the network's proper portal url cannot be generated without it
       const NetworkState* default_network =
           NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
+      const std::string& technology = cellular->network_technology();
       bool force_show_view_account_button =
-          (cellular->technology() == flimflam::kNetworkTechnologyLte ||
-           cellular->technology() == flimflam::kNetworkTechnologyLteAdvanced) &&
+          (technology == flimflam::kNetworkTechnologyLte ||
+           technology == flimflam::kNetworkTechnologyLteAdvanced) &&
           default_network &&
           !mdn.empty();
 
diff --git a/chrome/browser/ui/webui/options/chromeos/user_image_source.cc b/chrome/browser/ui/webui/options/chromeos/user_image_source.cc
index 1a681d0..5d56278 100644
--- a/chrome/browser/ui/webui/options/chromeos/user_image_source.cc
+++ b/chrome/browser/ui/webui/options/chromeos/user_image_source.cc
@@ -11,9 +11,9 @@
 #include "chrome/browser/chromeos/login/user_manager.h"
 #include "chrome/common/url_constants.h"
 #include "grit/theme_resources.h"
+#include "net/base/escape.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/codec/png_codec.h"
-#include "ui/webui/web_ui_util.h"
 #include "url/url_parse.h"
 
 namespace {
@@ -24,14 +24,15 @@
 const char kKeyAnimated[] = "animated";
 
 // Parses the user image URL, which looks like
-// "chrome://userimage/user@host?key1=value1&...&key_n=value_n@<scale>x",
-// to user email, optional parameters and scale factor.
+// "chrome://userimage/user@host?key1=value1&...&key_n=value_n",
+// to user email and optional parameters.
 void ParseRequest(const GURL& url,
                   std::string* email,
-                  bool* is_image_animated,
-                  ui::ScaleFactor* scale_factor) {
+                  bool* is_image_animated) {
   DCHECK(url.is_valid());
-  webui::ParsePathAndScale(url, email, scale_factor);
+  *email = net::UnescapeURLComponent(url.path().substr(1),
+                                    (net::UnescapeRule::URL_SPECIAL_CHARS |
+                                     net::UnescapeRule::SPACES));
   std::string url_spec = url.possibly_invalid_spec();
   url_parse::Component query = url.parsed_for_possibly_invalid_spec().query;
   url_parse::Component key, value;
@@ -92,10 +93,9 @@
     const content::URLDataSource::GotDataCallback& callback) {
   std::string email;
   bool is_image_animated = false;
-  ui::ScaleFactor scale_factor;
   GURL url(chrome::kChromeUIUserImageURL + path);
-  ParseRequest(url, &email, &is_image_animated, &scale_factor);
-  callback.Run(GetUserImage(email, is_image_animated, scale_factor));
+  ParseRequest(url, &email, &is_image_animated);
+  callback.Run(GetUserImage(email, is_image_animated, ui::SCALE_FACTOR_100P));
 }
 
 std::string UserImageSource::GetMimeType(const std::string& path) const {
@@ -103,10 +103,9 @@
   // drag the image they get no extension.
   std::string email;
   bool is_image_animated = false;
-  ui::ScaleFactor scale_factor;
 
   GURL url(chrome::kChromeUIUserImageURL + path);
-  ParseRequest(url, &email, &is_image_animated, &scale_factor);
+  ParseRequest(url, &email, &is_image_animated);
 
   if (is_image_animated) {
     const chromeos::User* user = chromeos::UserManager::Get()->FindUser(email);
diff --git a/chrome/browser/ui/webui/options/content_settings_handler.cc b/chrome/browser/ui/webui/options/content_settings_handler.cc
index a904b46..e978f79 100644
--- a/chrome/browser/ui/webui/options/content_settings_handler.cc
+++ b/chrome/browser/ui/webui/options/content_settings_handler.cc
@@ -391,6 +391,11 @@
       IDS_AUTOMATIC_DOWNLOADS_ASK_RADIO },
     { "multiple-automatic-downloads_block",
       IDS_AUTOMATIC_DOWNLOADS_BLOCK_RADIO },
+    // MIDI system exclusive messages
+    { "midi-sysex_header", IDS_MIDI_SYSEX_TAB_LABEL },
+    { "midiSysExAllow", IDS_MIDI_SYSEX_ALLOW_RADIO },
+    { "midiSysExAsk", IDS_MIDI_SYSEX_ASK_RADIO },
+    { "midiSysExBlock", IDS_MIDI_SYSEX_BLOCK_RADIO },
   };
 
   RegisterStrings(localized_strings, resources, arraysize(resources));
@@ -423,6 +428,8 @@
                 IDS_PPAPI_BROKER_TAB_LABEL);
   RegisterTitle(localized_strings, "multiple-automatic-downloads",
                 IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL);
+  RegisterTitle(localized_strings, "midi-sysex",
+                IDS_MIDI_SYSEX_TAB_LABEL);
 
   localized_strings->SetBoolean("newContentSettings",
       CommandLine::ForCurrentProcess()->HasSwitch(switches::kContentSettings2));
@@ -696,7 +703,7 @@
       // The RPH settings are retrieved separately.
       break;
     case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
-      // TODO(toyoshim): The content settings for MIDI sysex is not implemented.
+      UpdateMIDISysExExceptionsView();
       break;
 #if defined(OS_WIN)
     case CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP:
@@ -722,6 +729,7 @@
     case CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
     case CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
     case CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS:
+    case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
       break;
     default:
       UpdateExceptionsViewFromOTRHostContentSettingsMap(type);
@@ -927,6 +935,18 @@
   UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_MEDIASTREAM);
 }
 
+void ContentSettingsHandler::UpdateMIDISysExExceptionsView() {
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableWebMIDI)) {
+    web_ui()->CallJavascriptFunction(
+        "ContentSettings.showExperimentalWebMIDISettings",
+        base::FundamentalValue(true));
+  }
+
+  UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
+  UpdateExceptionsViewFromHostContentSettingsMap(
+      CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
+}
+
 void ContentSettingsHandler::UpdateExceptionsViewFromHostContentSettingsMap(
     ContentSettingsType type) {
   ListValue exceptions;
@@ -1230,6 +1250,10 @@
       content::RecordAction(UserMetricsAction(
           "Options_DefaultMultipleAutomaticDownloadsSettingChanged"));
       break;
+    case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
+      content::RecordAction(
+          UserMetricsAction("Options_DefaultMIDISysExSettingChanged"));
+      break;
     default:
       break;
   }
diff --git a/chrome/browser/ui/webui/options/content_settings_handler.h b/chrome/browser/ui/webui/options/content_settings_handler.h
index 6d4f3b1..924227b 100644
--- a/chrome/browser/ui/webui/options/content_settings_handler.h
+++ b/chrome/browser/ui/webui/options/content_settings_handler.h
@@ -106,6 +106,8 @@
   void UpdateNotificationExceptionsView();
   // Clobbers and rebuilds just the Media device exception table.
   void UpdateMediaExceptionsView();
+  // Clobbers and rebuilds just the MIDI SysEx exception table.
+  void UpdateMIDISysExExceptionsView();
   // Clobbers and rebuilds an exception table that's managed by the host content
   // settings map.
   void UpdateExceptionsViewFromHostContentSettingsMap(ContentSettingsType type);
diff --git a/chrome/browser/ui/webui/options/manage_profile_browsertest.js b/chrome/browser/ui/webui/options/manage_profile_browsertest.js
new file mode 100644
index 0000000..70c494a
--- /dev/null
+++ b/chrome/browser/ui/webui/options/manage_profile_browsertest.js
@@ -0,0 +1,363 @@
+// Copyright 2013 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.
+
+// None of these tests is relevant for Chrome OS.
+GEN('#if !defined(OS_CHROMEOS)');
+
+GEN('#include "base/command_line.h"');
+GEN('#include "chrome/common/chrome_switches.h"');
+
+/**
+ * TestFixture for ManageProfileOverlay and CreateProfileOverlay WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function ManageProfileUITest() {}
+
+ManageProfileUITest.prototype = {
+  __proto__: testing.Test.prototype,
+
+  /** @override */
+  browsePreload: 'chrome://settings-frame/manageProfile',
+
+  /**
+   * No need to run these for every OptionsPage test, since they'll cover the
+   * whole consolidated page each time.
+   * @override
+   */
+  runAccessibilityChecks: false,
+
+  /** @override */
+  testGenPreamble: function() {
+    GEN('CommandLine::ForCurrentProcess()->' +
+        'AppendSwitch(switches::kEnableManagedUsers);');
+  },
+
+  /**
+   * Returns a test profile-info object with configurable "managed" status.
+   * @param {boolean} managed If true, the test profile will be marked as
+   *     managed.
+   * @return {Object} A test profile-info object.
+   */
+  testProfileInfo_: function(managed) {
+    return {
+      name: 'Test Profile',
+      iconURL: 'chrome://path/to/icon/image',
+      filePath: '/path/to/profile/data/on/disk',
+      isCurrentProfile: true,
+      isManaged: managed
+    };
+  },
+
+  /**
+   * Overrides WebUI methods that provide profile info, making them return a
+   * test profile-info object.
+   * @param {boolean} managed Whether the test profile should be marked managed.
+   */
+  setProfileManaged_: function(managed) {
+    // Override the BrowserOptions method to return the fake info.
+    BrowserOptions.getCurrentProfile = function() {
+      return this.testProfileInfo_(managed);
+    }.bind(this);
+    // Set the profile info in the overlay.
+    ManageProfileOverlay.setProfileInfo(this.testProfileInfo_(managed),
+                                        'manage');
+  },
+};
+
+// The default options should be reset each time the creation overlay is shown.
+TEST_F('ManageProfileUITest', 'DefaultCreateOptions', function() {
+  OptionsPage.showPageByName('createProfile');
+  var shortcutsAllowed = loadTimeData.getBoolean('profileShortcutsEnabled');
+  var createShortcut = $('create-shortcut');
+  var createManaged = $('create-profile-managed');
+  assertEquals(shortcutsAllowed, createShortcut.checked);
+  assertFalse(createManaged.checked);
+
+  createShortcut.checked = !shortcutsAllowed;
+  createManaged.checked = true;
+  OptionsPage.closeOverlay();
+  OptionsPage.showPageByName('createProfile');
+  assertEquals(shortcutsAllowed, createShortcut.checked);
+  assertFalse(createManaged.checked);
+});
+
+// Creating managed users should be disallowed when they are not enabled.
+TEST_F('ManageProfileUITest', 'CreateManagedUserAllowed', function() {
+  var container = $('create-profile-managed-container');
+
+  ManageProfileOverlay.getInstance().initializePage();
+  assertFalse(container.hidden);
+
+  loadTimeData.overrideValues({'managedUsersEnabled': false});
+  ManageProfileOverlay.getInstance().initializePage();
+  assertTrue(container.hidden);
+});
+
+// The checkbox label should change depending on whether the user is signed in.
+TEST_F('ManageProfileUITest', 'CreateManagedUserText', function() {
+  var signedInText =  $('create-profile-managed-signed-in');
+  var notSignedInText = $('create-profile-managed-not-signed-in');
+
+  ManageProfileOverlay.getInstance().initializePage();
+
+  var custodianEmail = 'chrome.playpen.test@gmail.com';
+  CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+  assertEquals(custodianEmail,
+               CreateProfileOverlay.getInstance().signedInEmail_);
+  assertFalse(signedInText.hidden);
+  assertTrue(notSignedInText.hidden);
+  // Make sure the email is in the string somewhere, without depending on the
+  // exact details of the message.
+  assertNotEquals(-1, signedInText.textContent.indexOf(custodianEmail));
+
+  CreateProfileOverlay.updateSignedInStatus('');
+  assertEquals('', CreateProfileOverlay.getInstance().signedInEmail_);
+  assertTrue(signedInText.hidden);
+  assertFalse(notSignedInText.hidden);
+  assertFalse($('create-profile-managed').checked);
+  assertTrue($('create-profile-managed').disabled);
+});
+
+// Managed users should not be able to edit their profile names.
+TEST_F('ManageProfileUITest', 'EditManagedUserNameAllowed', function() {
+  var nameField = $('manage-profile-name');
+
+  this.setProfileManaged_(false);
+  ManageProfileOverlay.showManageDialog();
+  assertFalse(nameField.disabled);
+
+  this.setProfileManaged_(true);
+  ManageProfileOverlay.showManageDialog();
+  assertTrue(nameField.disabled);
+});
+
+// Setting profile information should allow the confirmation to be shown.
+TEST_F('ManageProfileUITest', 'ShowCreateConfirmation', function() {
+  var testProfile = this.testProfileInfo_(true);
+  testProfile.custodianEmail = 'foo@bar.example.com';
+  ManagedUserCreateConfirmOverlay.setProfileInfo(testProfile);
+  assertTrue(ManagedUserCreateConfirmOverlay.getInstance().canShowPage());
+  OptionsPage.showPageByName('managedUserCreateConfirm', false);
+  assertEquals('managedUserCreateConfirm',
+               OptionsPage.getTopmostVisiblePage().name);
+});
+
+// Trying to show a confirmation dialog with no profile information should fall
+// back to the default (main) settings page.
+TEST_F('ManageProfileUITest', 'NoEmptyConfirmation', function() {
+  assertEquals('manageProfile', OptionsPage.getTopmostVisiblePage().name);
+  assertFalse(ManagedUserCreateConfirmOverlay.getInstance().canShowPage());
+  OptionsPage.showPageByName('managedUserCreateConfirm', true);
+  assertEquals('settings', OptionsPage.getTopmostVisiblePage().name);
+});
+
+// A confirmation dialog should be shown after creating a new managed user.
+TEST_F('ManageProfileUITest', 'ShowCreateConfirmationOnSuccess', function() {
+  OptionsPage.showPageByName('createProfile');
+  assertEquals('createProfile', OptionsPage.getTopmostVisiblePage().name);
+  CreateProfileOverlay.onSuccess(this.testProfileInfo_(false));
+  assertEquals('settings', OptionsPage.getTopmostVisiblePage().name);
+
+  OptionsPage.showPageByName('createProfile');
+  assertEquals('createProfile', OptionsPage.getTopmostVisiblePage().name);
+  CreateProfileOverlay.onSuccess(this.testProfileInfo_(true));
+  assertEquals('managedUserCreateConfirm',
+               OptionsPage.getTopmostVisiblePage().name);
+});
+
+// An error should be shown if creating a new managed user fails.
+TEST_F('ManageProfileUITest', 'NoCreateConfirmationOnError', function() {
+  OptionsPage.showPageByName('createProfile');
+  assertEquals('createProfile', OptionsPage.getTopmostVisiblePage().name);
+  var errorBubble = $('create-profile-error-bubble');
+  assertTrue(errorBubble.hidden);
+
+  CreateProfileOverlay.onLocalError();
+  assertEquals('createProfile', OptionsPage.getTopmostVisiblePage().name);
+  assertFalse(errorBubble.hidden);
+
+  errorBubble.hidden = true;
+  CreateProfileOverlay.onRemoteError();
+  assertEquals('createProfile', OptionsPage.getTopmostVisiblePage().name);
+  assertFalse(errorBubble.hidden);
+});
+
+// The name and email sould be inserted into the confirmation dialog.
+TEST_F('ManageProfileUITest', 'CreateConfirmationText', function () {
+  var self = this;
+  var custodianEmail = 'foo@example.com';
+
+  // Checks the strings in the confirmation dialog. If |expectedNameText| is
+  // given, it should be present in the dialog's textContent; otherwise the name
+  // is expected. If |expectedNameHtml| is given, it should be present in the
+  // dialog's innerHTML; otherwise the expected text is expected in the HTML
+  // too.
+  function checkDialog(name, expectedNameText, expectedNameHtml) {
+    var expectedText = expectedNameText || name;
+    var expectedHtml = expectedNameHtml || expectedText;
+
+    // Configure the test profile and show the confirmation dialog.
+    var testProfile = self.testProfileInfo_(true);
+    testProfile.name = name;
+    CreateProfileOverlay.onSuccess(testProfile);
+    assertEquals('managedUserCreateConfirm',
+                 OptionsPage.getTopmostVisiblePage().name);
+
+    // Check for the presence of the name and email in the UI, without depending
+    // on the details of the messsages.
+    assertNotEquals(-1,
+        $('managed-user-created-title').textContent.indexOf(expectedText));
+    assertNotEquals(-1,
+        $('managed-user-created-switch').textContent.indexOf(expectedText));
+    var message = $('managed-user-created-text');
+    assertNotEquals(-1, message.textContent.indexOf(expectedText));
+    assertNotEquals(-1, message.textContent.indexOf(custodianEmail));
+
+    // The name should be properly HTML-escaped.
+    assertNotEquals(-1, message.innerHTML.indexOf(expectedHtml));
+
+    OptionsPage.closeOverlay();
+    assertEquals('settings', OptionsPage.getTopmostVisiblePage().name, name);
+  }
+
+  // Show and configure the create-profile dialog.
+  OptionsPage.showPageByName('createProfile');
+  CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+  assertEquals('createProfile', OptionsPage.getTopmostVisiblePage().name);
+
+  checkDialog('OneWord');
+  checkDialog('Multiple Words');
+  checkDialog('It\'s "<HTML> injection" & more!',
+              'It\'s "<HTML> injection" & more!',
+              // The innerHTML getter doesn't escape quotation marks,
+              // independent of whether they were escaped in the setter.
+              'It\'s "&lt;HTML&gt; injection" &amp; more!');
+
+  // Test elision. MAX_LENGTH = 50, minus 3 for the ellipsis.
+  var name47Characters = '01234567890123456789012345678901234567890123456';
+  var name60Characters = name47Characters + '0123456789012';
+  checkDialog(name60Characters, name47Characters + '...');
+
+  // Test both elision and HTML escaping. The allowed string length is the
+  // visible length, not the length including the entity names.
+  name47Characters = name47Characters.replace('0', '&').replace('1', '>');
+  name60Characters = name60Characters.replace('0', '&').replace('1', '>');
+  var escaped = name47Characters.replace('&', '&amp;').replace('>', '&gt;');
+  checkDialog(name60Characters, name47Characters + '...', escaped + '...');
+});
+
+// An additional warning should be shown when deleting a managed user.
+TEST_F('ManageProfileUITest', 'DeleteManagedUserWarning', function() {
+  var addendum = $('delete-managed-profile-addendum');
+
+  ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(true));
+  assertFalse(addendum.hidden);
+
+  ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(false));
+  assertTrue(addendum.hidden);
+});
+
+// The policy prohibiting managed users should update the UI dynamically.
+TEST_F('ManageProfileUITest', 'PolicyDynamicRefresh', function() {
+  ManageProfileOverlay.getInstance().initializePage();
+
+  var custodianEmail = 'chrome.playpen.test@gmail.com';
+  CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+  CreateProfileOverlay.updateManagedUsersAllowed(true);
+  var checkbox = $('create-profile-managed');
+  var link = $('create-profile-managed-not-signed-in-link');
+  var indicator = $('create-profile-managed-indicator');
+
+  assertFalse(checkbox.disabled, 'allowed and signed in');
+  assertFalse(link.hidden, 'allowed and signed in');
+  assertEquals('none', window.getComputedStyle(indicator, null).display,
+               'allowed and signed in');
+
+  CreateProfileOverlay.updateSignedInStatus('');
+  CreateProfileOverlay.updateManagedUsersAllowed(true);
+  assertTrue(checkbox.disabled, 'allowed, not signed in');
+  assertFalse(link.hidden, 'allowed, not signed in');
+  assertEquals('none', window.getComputedStyle(indicator, null).display,
+               'allowed, not signed in');
+
+  CreateProfileOverlay.updateSignedInStatus('');
+  CreateProfileOverlay.updateManagedUsersAllowed(false);
+  assertTrue(checkbox.disabled, 'disallowed, not signed in');
+  assertTrue(link.hidden, 'disallowed, not signed in');
+  assertEquals('inline-block', window.getComputedStyle(indicator, null).display,
+               'disallowed, not signed in');
+  assertEquals('policy', indicator.getAttribute('controlled-by'));
+
+  CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+  CreateProfileOverlay.updateManagedUsersAllowed(false);
+  assertTrue(checkbox.disabled, 'disallowed, signed in');
+  assertTrue(link.hidden, 'disallowed, signed in');
+  assertEquals('inline-block', window.getComputedStyle(indicator, null).display,
+               'disallowed, signed in');
+  assertEquals('policy', indicator.getAttribute('controlled-by'));
+
+  CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+  CreateProfileOverlay.updateManagedUsersAllowed(true);
+  assertFalse(checkbox.disabled, 're-allowed and signed in');
+  assertFalse(link.hidden, 're-allowed and signed in');
+  assertEquals('none', window.getComputedStyle(indicator, null).display,
+               're-allowed and signed in');
+});
+
+// Managed users shouldn't be able to open the delete or create dialogs.
+TEST_F('ManageProfileUITest', 'ManagedShowDeleteAndCreate', function() {
+  this.setProfileManaged_(false);
+
+  ManageProfileOverlay.showCreateDialog();
+  assertEquals('createProfile', OptionsPage.getTopmostVisiblePage().name);
+  OptionsPage.closeOverlay();
+  assertEquals('settings', OptionsPage.getTopmostVisiblePage().name);
+  ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(false));
+  assertEquals('manageProfile', OptionsPage.getTopmostVisiblePage().name);
+  assertFalse($('manage-profile-overlay-delete').hidden);
+  OptionsPage.closeOverlay();
+  assertEquals('settings', OptionsPage.getTopmostVisiblePage().name);
+
+  this.setProfileManaged_(true);
+  ManageProfileOverlay.showCreateDialog();
+  assertEquals('settings', OptionsPage.getTopmostVisiblePage().name);
+  ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(false));
+  assertEquals('settings', OptionsPage.getTopmostVisiblePage().name);
+});
+
+// Only non-managed users should be able to delete profiles.
+TEST_F('ManageProfileUITest', 'ManagedDelete', function() {
+  ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(false));
+  assertEquals('manageProfile', OptionsPage.getTopmostVisiblePage().name);
+  assertFalse($('manage-profile-overlay-delete').hidden);
+
+  // Clicks the "Delete" button, after overriding chrome.send to record what
+  // messages were sent.
+  function clickAndListen() {
+    var originalChromeSend = chrome.send;
+    var chromeSendMessages = [];
+    chrome.send = function(message) {
+      chromeSendMessages.push(message);
+    };
+    $('delete-profile-ok').onclick();
+    // Restore the original function so the test framework can use it.
+    chrome.send = originalChromeSend;
+    return chromeSendMessages;
+  }
+
+  this.setProfileManaged_(false);
+  var messages = clickAndListen();
+  assertEquals(1, messages.length);
+  assertEquals('deleteProfile', messages[0]);
+  assertEquals('settings', OptionsPage.getTopmostVisiblePage().name);
+
+  ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(false));
+  this.setProfileManaged_(true);
+  messages = clickAndListen();
+  assertEquals(0, messages.length);
+  assertEquals('settings', OptionsPage.getTopmostVisiblePage().name);
+});
+
+GEN('#endif  // OS_CHROMEOS');
diff --git a/chrome/browser/ui/webui/options/manage_profile_handler.cc b/chrome/browser/ui/webui/options/manage_profile_handler.cc
index d684067..8a9f871 100644
--- a/chrome/browser/ui/webui/options/manage_profile_handler.cc
+++ b/chrome/browser/ui/webui/options/manage_profile_handler.cc
@@ -36,11 +36,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/webui/web_ui_util.h"
 
-#if defined(ENABLE_MANAGED_USERS)
-#include "chrome/browser/managed_mode/managed_user_service.h"
-#include "chrome/browser/managed_mode/managed_user_service_factory.h"
-#endif
-
 #if defined(ENABLE_SETTINGS_APP)
 #include "chrome/browser/ui/app_list/app_list_service.h"
 #include "content/public/browser/web_contents.h"
@@ -180,6 +175,12 @@
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
   if (type == chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED) {
+    // If the browser shuts down during supervised-profile creation, deleting
+    // the unregistered supervised-user profile triggers this notification,
+    // but the RenderViewHost the profile info would be sent to has already been
+    // destroyed.
+    if (!web_ui()->GetWebContents()->GetRenderViewHost())
+      return;
     SendProfileNames();
     base::StringValue value(kManageProfileIconGridName);
     SendProfileIcons(value);
@@ -295,7 +296,7 @@
   }
   ProfileMetrics::LogProfileUpdate(profile_file_path);
 
-  if (ManagedUserService::ProfileIsManaged(profile))
+  if (profile->IsManaged())
     return;
 
   string16 new_profile_name;
diff --git a/chrome/browser/ui/webui/screenshot_source.cc b/chrome/browser/ui/webui/screenshot_source.cc
deleted file mode 100644
index 2c09777..0000000
--- a/chrome/browser/ui/webui/screenshot_source.cc
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/ui/webui/screenshot_source.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/file_util.h"
-#include "base/files/file_path.h"
-#include "base/i18n/time_formatting.h"
-#include "base/memory/ref_counted_memory.h"
-#include "base/message_loop/message_loop.h"
-#include "base/path_service.h"
-#include "base/prefs/pref_service.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/download/download_prefs.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
-#include "url/url_canon.h"
-#include "url/url_util.h"
-
-#if defined(USE_ASH)
-#include "ash/shell.h"
-#include "ash/shell_delegate.h"
-#endif
-
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/drive/drive_integration_service.h"
-#include "chrome/browser/chromeos/drive/file_system_interface.h"
-#include "chrome/browser/chromeos/drive/file_system_util.h"
-#include "chromeos/login/login_state.h"
-#include "content/public/browser/browser_thread.h"
-#endif
-
-// static
-const char ScreenshotSource::kScreenshotUrlRoot[] = "chrome://screenshots/";
-// static
-const char ScreenshotSource::kScreenshotCurrent[] = "current";
-// static
-const char ScreenshotSource::kScreenshotSaved[] = "saved/";
-#if defined(OS_CHROMEOS)
-// static
-const char ScreenshotSource::kScreenshotPrefix[] = "Screenshot ";
-// static
-const char ScreenshotSource::kScreenshotSuffix[] = ".png";
-#endif
-
-bool ShouldUse24HourClock() {
-#if defined(OS_CHROMEOS)
-  Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
-  if (profile) {
-    return profile->GetPrefs()->GetBoolean(prefs::kUse24HourClock);
-  }
-#endif
-  return base::GetHourClockType() == base::k24HourClock;
-}
-
-ScreenshotSource::ScreenshotSource(
-    std::vector<unsigned char>* current_screenshot,
-    Profile* profile)
-    : profile_(profile) {
-  // Setup the last screenshot taken.
-  if (current_screenshot)
-    current_screenshot_.reset(new ScreenshotData(*current_screenshot));
-  else
-    current_screenshot_.reset(new ScreenshotData());
-}
-
-ScreenshotSource::~ScreenshotSource() {}
-
-// static
-std::string ScreenshotSource::GetScreenshotBaseFilename() {
-  base::Time::Exploded now;
-  base::Time::Now().LocalExplode(&now);
-
-  // We don't use base/i18n/time_formatting.h here because it doesn't
-  // support our format.  Don't use ICU either to avoid i18n file names
-  // for non-English locales.
-  // TODO(mukai): integrate this logic somewhere time_formatting.h
-  std::string file_name = base::StringPrintf(
-      "Screenshot %d-%02d-%02d at ", now.year, now.month, now.day_of_month);
-
-  if (ShouldUse24HourClock()) {
-    file_name.append(base::StringPrintf(
-        "%02d.%02d.%02d", now.hour, now.minute, now.second));
-  } else {
-    int hour = now.hour;
-    if (hour > 12) {
-      hour -= 12;
-    } else if (hour == 0) {
-      hour = 12;
-    }
-    file_name.append(base::StringPrintf(
-        "%d.%02d.%02d ", hour, now.minute, now.second));
-    file_name.append((now.hour >= 12) ? "PM" : "AM");
-  }
-
-  return file_name;
-}
-
-#if defined(USE_ASH)
-
-// static
-bool ScreenshotSource::AreScreenshotsDisabled() {
-  return g_browser_process->local_state()->GetBoolean(
-      prefs::kDisableScreenshots);
-}
-
-// static
-bool ScreenshotSource::GetScreenshotDirectory(base::FilePath* directory) {
-  if (ScreenshotSource::AreScreenshotsDisabled())
-    return false;
-
-  bool is_logged_in = true;
-
-#if defined(OS_CHROMEOS)
-  is_logged_in = chromeos::LoginState::Get()->IsUserLoggedIn();
-#endif
-
-  if (is_logged_in) {
-    DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext(
-        ash::Shell::GetInstance()->delegate()->GetCurrentBrowserContext());
-    *directory = download_prefs->DownloadPath();
-  } else  {
-    if (!file_util::GetTempDir(directory)) {
-      LOG(ERROR) << "Failed to find temporary directory.";
-      return false;
-    }
-  }
-  return true;
-}
-
-#endif
-
-std::string ScreenshotSource::GetSource() const {
-  return chrome::kChromeUIScreenshotPath;
-}
-
-void ScreenshotSource::StartDataRequest(
-  const std::string& path,
-  int render_process_id,
-  int render_view_id,
-  const content::URLDataSource::GotDataCallback& callback) {
-  SendScreenshot(path, callback);
-}
-
-std::string ScreenshotSource::GetMimeType(const std::string&) const {
-  // We need to explicitly return a mime type, otherwise if the user tries to
-  // drag the image they get no extension.
-  return "image/png";
-}
-
-ScreenshotDataPtr ScreenshotSource::GetCachedScreenshot(
-    const std::string& screenshot_path) {
-  std::map<std::string, ScreenshotDataPtr>::iterator pos;
-  std::string path = screenshot_path.substr(
-      0, screenshot_path.find_first_of("?"));
-  if ((pos = cached_screenshots_.find(path)) != cached_screenshots_.end()) {
-    return pos->second;
-  } else {
-    return ScreenshotDataPtr(new ScreenshotData);
-  }
-}
-
-void ScreenshotSource::SendScreenshot(
-    const std::string& screenshot_path,
-    const content::URLDataSource::GotDataCallback& callback) {
-  // Strip the query param value - we only use it as a hack to ensure our
-  // image gets reloaded instead of being pulled from the browser cache
-  std::string path = screenshot_path.substr(
-      0, screenshot_path.find_first_of("?"));
-  if (path == ScreenshotSource::kScreenshotCurrent) {
-    CacheAndSendScreenshot(path, callback, current_screenshot_);
-#if defined(OS_CHROMEOS)
-  } else if (path.compare(0, strlen(ScreenshotSource::kScreenshotSaved),
-             ScreenshotSource::kScreenshotSaved) == 0) {
-    using content::BrowserThread;
-
-    std::string filename =
-        path.substr(strlen(ScreenshotSource::kScreenshotSaved));
-
-    url_canon::RawCanonOutputT<char16> decoded;
-    url_util::DecodeURLEscapeSequences(
-        filename.data(), filename.size(), &decoded);
-    // Screenshot filenames don't use non-ascii characters.
-    std::string decoded_filename = UTF16ToASCII(string16(
-        decoded.data(), decoded.length()));
-
-    base::FilePath download_path;
-    GetScreenshotDirectory(&download_path);
-    if (drive::util::IsUnderDriveMountPoint(download_path)) {
-      drive::FileSystemInterface* file_system =
-          drive::DriveIntegrationServiceFactory::GetForProfile(
-              profile_)->file_system();
-      file_system->GetFileByPath(
-          drive::util::ExtractDrivePath(download_path).Append(decoded_filename),
-          base::Bind(&ScreenshotSource::GetSavedScreenshotCallback,
-                     base::Unretained(this), screenshot_path, callback));
-    } else {
-      BrowserThread::PostTask(
-          BrowserThread::FILE, FROM_HERE,
-          base::Bind(&ScreenshotSource::SendSavedScreenshot,
-                     base::Unretained(this),
-                     screenshot_path,
-                     callback, download_path.Append(decoded_filename)));
-    }
-#endif
-  } else {
-    CacheAndSendScreenshot(
-        path, callback, ScreenshotDataPtr(new ScreenshotData()));
-  }
-}
-
-#if defined(OS_CHROMEOS)
-void ScreenshotSource::SendSavedScreenshot(
-    const std::string& screenshot_path,
-    const content::URLDataSource::GotDataCallback& callback,
-    const base::FilePath& file) {
-  ScreenshotDataPtr read_bytes(new ScreenshotData);
-  int64 file_size = 0;
-
-  if (!file_util::GetFileSize(file, &file_size)) {
-    CacheAndSendScreenshot(screenshot_path, callback, read_bytes);
-    return;
-  }
-
-  read_bytes->resize(file_size);
-  if (!file_util::ReadFile(file, reinterpret_cast<char*>(&read_bytes->front()),
-                           static_cast<int>(file_size)))
-    read_bytes->clear();
-
-  CacheAndSendScreenshot(screenshot_path, callback, read_bytes);
-}
-
-void ScreenshotSource::GetSavedScreenshotCallback(
-    const std::string& screenshot_path,
-    const content::URLDataSource::GotDataCallback& callback,
-    drive::FileError error,
-    const base::FilePath& file,
-    scoped_ptr<drive::ResourceEntry> entry) {
-  if (error != drive::FILE_ERROR_OK) {
-    ScreenshotDataPtr read_bytes(new ScreenshotData);
-    CacheAndSendScreenshot(screenshot_path, callback, read_bytes);
-    return;
-  }
-
-  content::BrowserThread::PostTask(
-      content::BrowserThread::FILE, FROM_HERE,
-      base::Bind(&ScreenshotSource::SendSavedScreenshot,
-                 base::Unretained(this), screenshot_path, callback, file));
-}
-#endif
-
-void ScreenshotSource::CacheAndSendScreenshot(
-    const std::string& screenshot_path,
-    const content::URLDataSource::GotDataCallback& callback,
-    ScreenshotDataPtr bytes) {
-  // Strip the query from the screenshot path.
-  std::string path = screenshot_path.substr(
-      0, screenshot_path.find_first_of("?"));
-  cached_screenshots_[path] = bytes;
-  callback.Run(new base::RefCountedBytes(*bytes));
-}
diff --git a/chrome/browser/ui/webui/screenshot_source.h b/chrome/browser/ui/webui/screenshot_source.h
deleted file mode 100644
index fdc76ae..0000000
--- a/chrome/browser/ui/webui/screenshot_source.h
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2011 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.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_SCREENSHOT_SOURCE_H_
-#define CHROME_BROWSER_UI_WEBUI_SCREENSHOT_SOURCE_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/memory/linked_ptr.h"
-#include "content/public/browser/url_data_source.h"
-
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/drive/file_system_interface.h"
-#include "chrome/browser/chromeos/login/user_manager.h"
-#include "chrome/browser/google_apis/gdata_errorcode.h"
-#endif
-
-typedef std::vector<unsigned char> ScreenshotData;
-typedef linked_ptr<ScreenshotData> ScreenshotDataPtr;
-
-class Profile;
-
-namespace base {
-class FilePath;
-}
-
-// ScreenshotSource is the data source that serves screenshots (saved
-// or current) to the bug report html ui.
-class ScreenshotSource : public content::URLDataSource {
- public:
-  explicit ScreenshotSource(
-      std::vector<unsigned char>* current_screenshot,
-      Profile* profile);
-
-#if defined(USE_ASH)
-  // Queries the browser process to determine if screenshots are disabled.
-  static bool AreScreenshotsDisabled();
-
-  // Common access for the screenshot directory, parameter is set to the
-  // requested directory and return value of true is given upon success.
-  static bool GetScreenshotDirectory(base::FilePath* directory);
-#endif
-
-  // Get the basefilename for screenshots
-  static std::string GetScreenshotBaseFilename();
-
-  // content::URLDataSource implementation.
-  virtual std::string GetSource() const OVERRIDE;
-  virtual void StartDataRequest(
-      const std::string& path,
-      int render_process_id,
-      int render_view_id,
-      const content::URLDataSource::GotDataCallback& callback) OVERRIDE;
-  virtual std::string GetMimeType(const std::string&) const OVERRIDE;
-
-  // Get the screenshot specified by the given relative path that we've cached
-  // from a previous request to the screenshots source.
-  // Note: This method strips the query string from the given path.
-  ScreenshotDataPtr GetCachedScreenshot(const std::string& screenshot_path);
-
-  // Url that represents the base directory for screenshots.
-  static const char kScreenshotUrlRoot[];
-  // Identifier for the current screenshot
-  // (relative to screenshot base directory).
-  static const char kScreenshotCurrent[];
-  // Path for directory where screenshots are saved
-  // (relative to screenshot base directory).
-  static const char kScreenshotSaved[];
-#if defined(OS_CHROMEOS)
-  // Common prefix to screenshot filenames.
-  static const char kScreenshotPrefix[];
-  // Common suffix to screenshot filenames.
-  static const char kScreenshotSuffix[];
-#endif
-
- private:
-  virtual ~ScreenshotSource();
-
-  // Send the screenshot specified by the given relative path to the requestor.
-  // This is the ancestor for SendSavedScreenshot and CacheAndSendScreenshot.
-  // All calls to send a screenshot should only call this method.
-  // Note: This method strips the query string from the given path.
-  void SendScreenshot(const std::string& screenshot_path,
-                      const content::URLDataSource::GotDataCallback& callback);
-#if defined(OS_CHROMEOS)
-  // Send a saved screenshot image file specified by the given screenshot path
-  // to the requestor.
-  void SendSavedScreenshot(
-      const std::string& screenshot_path,
-      const content::URLDataSource::GotDataCallback& callback,
-      const base::FilePath& file);
-
-  // The callback for Drive's getting file method.
-  void GetSavedScreenshotCallback(
-      const std::string& screenshot_path,
-      const content::URLDataSource::GotDataCallback& callback,
-      drive::FileError error,
-      const base::FilePath& file,
-      scoped_ptr<drive::ResourceEntry> entry);
-
-#endif
-  // Sends the screenshot data to the requestor while caching it locally to the
-  // class instance, indexed by path.
-  void CacheAndSendScreenshot(
-      const std::string& screenshot_path,
-      const content::URLDataSource::GotDataCallback& callback,
-      ScreenshotDataPtr bytes);
-
-  // Pointer to the screenshot data for the current screenshot.
-  ScreenshotDataPtr current_screenshot_;
-
-  Profile* profile_;
-
-  // Key: Relative path to the screenshot (including filename)
-  // Value: Pointer to the screenshot data associated with the path.
-  std::map<std::string, ScreenshotDataPtr> cached_screenshots_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScreenshotSource);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_SCREENSHOT_SOURCE_H_
diff --git a/chrome/browser/ui/webui/signin/login_ui_service.cc b/chrome/browser/ui/webui/signin/login_ui_service.cc
index cae530f..72fdbf6 100644
--- a/chrome/browser/ui/webui/signin/login_ui_service.cc
+++ b/chrome/browser/ui/webui/signin/login_ui_service.cc
@@ -5,13 +5,13 @@
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/host_desktop.h"
 #include "chrome/browser/ui/sync/inline_login_dialog.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
 #include "chrome/common/url_constants.h"
 
@@ -54,6 +54,6 @@
 #else
   Browser* browser = FindOrCreateTabbedBrowser(profile_,
                                                chrome::GetActiveDesktop());
-  chrome::ShowBrowserSignin(browser, SyncPromoUI::SOURCE_APP_LAUNCHER);
+  chrome::ShowBrowserSignin(browser, signin::SOURCE_APP_LAUNCHER);
 #endif
 }
diff --git a/chrome/browser/ui/webui/signin/user_chooser_screen_handler.cc b/chrome/browser/ui/webui/signin/user_chooser_screen_handler.cc
index c46a843..264f27f 100644
--- a/chrome/browser/ui/webui/signin/user_chooser_screen_handler.cc
+++ b/chrome/browser/ui/webui/signin/user_chooser_screen_handler.cc
@@ -5,13 +5,16 @@
 #include "chrome/browser/ui/webui/signin/user_chooser_screen_handler.h"
 
 #include "base/bind.h"
+#include "base/value_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_info_cache.h"
+#include "chrome/browser/profiles/profile_info_cache_observer.h"
 #include "chrome/browser/profiles/profile_info_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/singleton_tabs.h"
@@ -25,11 +28,16 @@
 #include "ui/gfx/image/image_util.h"
 #include "ui/webui/web_ui_util.h"
 
+#if defined(ENABLE_MANAGED_USERS)
+#include "chrome/browser/managed_mode/managed_user_service.h"
+#endif
+
 namespace {
 // User dictionary keys.
 const char kKeyUsername[] = "username";
 const char kKeyDisplayName[]= "displayName";
 const char kKeyEmailAddress[] = "emailAddress";
+const char kKeyProfilePath[] = "profilePath";
 const char kKeyPublicAccount[] = "publicAccount";
 const char kKeyLocallyManagedUser[] = "locallyManagedUser";
 const char kKeySignedIn[] = "signedIn";
@@ -57,9 +65,85 @@
 void HandleAndDoNothing(const base::ListValue* args) {
 }
 
+// This callback is run if the only profile has been deleted, and a new
+// profile has been created to replace it.
+void OpenNewWindowForProfile(
+    chrome::HostDesktopType desktop_type,
+    Profile* profile,
+    Profile::CreateStatus status) {
+  if (status != Profile::CREATE_STATUS_INITIALIZED)
+    return;
+  profiles::FindOrCreateNewWindowForProfile(
+    profile,
+    chrome::startup::IS_PROCESS_STARTUP,
+    chrome::startup::IS_FIRST_RUN,
+    desktop_type,
+    false);
+}
+
 } // namespace
 
+// ProfileUpdateObserver ------------------------------------------------------
+
+class UserChooserScreenHandler::ProfileUpdateObserver
+    : public ProfileInfoCacheObserver {
+ public:
+  ProfileUpdateObserver(
+      ProfileManager* profile_manager, UserChooserScreenHandler* handler)
+      : profile_manager_(profile_manager),
+        user_chooser_handler_(handler) {
+    DCHECK(profile_manager_);
+    DCHECK(user_chooser_handler_);
+    profile_manager_->GetProfileInfoCache().AddObserver(this);
+  }
+
+  virtual ~ProfileUpdateObserver() {
+    DCHECK(profile_manager_);
+    profile_manager_->GetProfileInfoCache().RemoveObserver(this);
+  }
+
+ private:
+  // ProfileInfoCacheObserver implementation:
+  // If any change has been made to a profile, propagate it to all the
+  // visible user manager screens.
+  virtual void OnProfileAdded(const base::FilePath& profile_path) OVERRIDE {
+    user_chooser_handler_->SendUserList();
+  }
+
+  virtual void OnProfileWasRemoved(const base::FilePath& profile_path,
+                                   const string16& profile_name) OVERRIDE {
+    user_chooser_handler_->SendUserList();
+  }
+
+  virtual void OnProfileWillBeRemoved(
+      const base::FilePath& profile_path) OVERRIDE {
+    // No-op. When the profile is actually removed, OnProfileWasRemoved
+    // will be called.
+  }
+
+  virtual void OnProfileNameChanged(const base::FilePath& profile_path,
+                                    const string16& old_profile_name) OVERRIDE {
+    user_chooser_handler_->SendUserList();
+  }
+
+  virtual void OnProfileAvatarChanged(
+      const base::FilePath& profile_path) OVERRIDE {
+    user_chooser_handler_->SendUserList();
+  }
+
+  ProfileManager* profile_manager_;
+
+  UserChooserScreenHandler* user_chooser_handler_;  // Weak; owns us.
+
+  DISALLOW_COPY_AND_ASSIGN(ProfileUpdateObserver);
+};
+
+// UserChooserScreenHandler ---------------------------------------------------
+
 UserChooserScreenHandler::UserChooserScreenHandler() {
+  profileInfoCacheObserver_.reset(
+      new UserChooserScreenHandler::ProfileUpdateObserver(
+          g_browser_process->profile_manager(), this));
 }
 
 UserChooserScreenHandler::~UserChooserScreenHandler() {
@@ -78,10 +162,31 @@
 }
 
 void UserChooserScreenHandler::HandleRemoveUser(const base::ListValue* args) {
-  // TODO(noms): Should delete the user.
-  chrome::ShowSingletonTab(chrome::FindBrowserWithWebContents(
-      web_ui()->GetWebContents()),
-      GURL("chrome://settings/search#Users"));
+  DCHECK(args);
+  const Value* profile_path_value;
+  if (!args->Get(0, &profile_path_value))
+    return;
+
+  base::FilePath profile_path;
+  if (!base::GetValueAsFilePath(*profile_path_value, &profile_path))
+    return;
+
+  // This handler could have been called in managed mode, for example because
+  // the user fiddled with the web inspector. Silently return in this case.
+  if (Profile::FromWebUI(web_ui())->IsManaged())
+    return;
+
+  if (!profiles::IsMultipleProfilesEnabled())
+    return;
+
+  Browser* browser =
+      chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+  DCHECK(browser);
+
+  chrome::HostDesktopType desktop_type = browser->host_desktop_type();
+  g_browser_process->profile_manager()->ScheduleProfileForDeletion(
+      profile_path,
+      base::Bind(&OpenNewWindowForProfile, desktop_type));
 }
 
 void UserChooserScreenHandler::HandleLaunchGuest(const base::ListValue* args) {
@@ -174,16 +279,28 @@
       l10n_util::GetStringUTF16(
           IDS_LOGIN_POD_MENU_REMOVE_ITEM_ACCESSIBLE_NAME));
   localized_strings->SetString("removeUser",
-      l10n_util::GetStringUTF16(IDS_LOGIN_POD_REMOVE_USER));
+      l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON));
   localized_strings->SetString("passwordFieldAccessibleName",
       l10n_util::GetStringUTF16(IDS_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME));
   localized_strings->SetString("bootIntoWallpaper", "off");
 
+  // For AccountPickerScreen, the remove user warning overlay.
+  localized_strings->SetString("removeUserWarningButtonTitle",
+      l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON));
+  localized_strings->SetString("removeUserWarningText",
+      l10n_util::GetStringUTF16(
+           IDS_LOGIN_POD_USER_REMOVE_WARNING));
+
+  // Strings needed for the user_pod_template public account div, but not ever
+  // actually displayed for desktop users.
+  localized_strings->SetString("publicAccountReminder", string16());
+  localized_strings->SetString("publicAccountEnter", string16());
+  localized_strings->SetString("publicAccountEnterAccessibleName", string16());
  }
 
 void UserChooserScreenHandler::SendUserList() {
   ListValue users_list;
-  base::FilePath current_profile_path =
+  base::FilePath active_profile_path =
       web_ui()->GetWebContents()->GetBrowserContext()->GetPath();
   const ProfileInfoCache& info_cache =
       g_browser_process->profile_manager()->GetProfileInfoCache();
@@ -192,7 +309,7 @@
     DictionaryValue* profile_value = new DictionaryValue();
 
     base::FilePath profile_path = info_cache.GetPathOfProfileAtIndex(i);
-    bool is_active_user = (profile_path == current_profile_path);
+    bool is_active_user = (profile_path == active_profile_path);
     bool needs_signin = info_cache.ProfileIsSigninRequiredAtIndex(i);
 
     profile_value->SetString(
@@ -201,6 +318,7 @@
         kKeyEmailAddress, info_cache.GetUserNameOfProfileAtIndex(i));
     profile_value->SetString(
         kKeyDisplayName, info_cache.GetNameOfProfileAtIndex(i));
+    profile_value->SetString(kKeyProfilePath, profile_path.MaybeAsASCII());
     profile_value->SetBoolean(kKeyPublicAccount, false);
     profile_value->SetBoolean(kKeyLocallyManagedUser, false);
     profile_value->SetBoolean(kKeySignedIn, is_active_user);
diff --git a/chrome/browser/ui/webui/signin/user_chooser_screen_handler.h b/chrome/browser/ui/webui/signin/user_chooser_screen_handler.h
index 3dbf1f4..8169d8c 100644
--- a/chrome/browser/ui/webui/signin/user_chooser_screen_handler.h
+++ b/chrome/browser/ui/webui/signin/user_chooser_screen_handler.h
@@ -6,10 +6,12 @@
 #define CHROME_BROWSER_UI_WEBUI_SIGNIN_USER_CHOOSER_SCREEN_HANDLER_H_
 
 #include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
 namespace base {
 class DictionaryValue;
+class FilePath;
 class ListValue;
 }
 
@@ -24,6 +26,10 @@
   void GetLocalizedValues(base::DictionaryValue* localized_strings);
 
  private:
+  // An observer for any changes to Profiles in the ProfileInfoCache so that
+  // all the visible user manager screens can be updated.
+  class ProfileUpdateObserver;
+
   void HandleInitialize(const base::ListValue* args);
   void HandleAddUser(const base::ListValue* args);
   void HandleLaunchGuest(const base::ListValue* args);
@@ -33,6 +39,10 @@
   // Sends user list to account chooser.
   void SendUserList();
 
+  // Observes the ProfileInfoCache and gets notified when a profile has been
+  // modified, so that the displayed user pods can be updated.
+  scoped_ptr<ProfileUpdateObserver> profileInfoCacheObserver_;
+
   DISALLOW_COPY_AND_ASSIGN(UserChooserScreenHandler);
 };
 
diff --git a/chrome/browser/ui/webui/sync_promo/sync_promo_trial.cc b/chrome/browser/ui/webui/sync_promo/sync_promo_trial.cc
index 5b51aaa..4f8afac 100644
--- a/chrome/browser/ui/webui/sync_promo/sync_promo_trial.cc
+++ b/chrome/browser/ui/webui/sync_promo/sync_promo_trial.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/metrics/metrics_service.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 
@@ -41,45 +41,45 @@
 namespace sync_promo_trial {
 
 void RecordUserShownPromo(content::WebUI* web_ui) {
-  SyncPromoUI::Source source = SyncPromoUI::GetSourceForSyncPromoURL(
-        web_ui->GetWebContents()->GetURL());
+  signin::Source source =
+      signin::GetSourceForPromoURL(web_ui->GetWebContents()->GetURL());
   int uma = 0;
   switch (source) {
-    case SyncPromoUI::SOURCE_START_PAGE:
+    case signin::SOURCE_START_PAGE:
       uma = UMA_START_PAGE_SHOWN;
       break;
-    case SyncPromoUI::SOURCE_NTP_LINK:
+    case signin::SOURCE_NTP_LINK:
       uma = UMA_NTP_LINK_SHOWN;
       break;
-    case SyncPromoUI::SOURCE_MENU:
+    case signin::SOURCE_MENU:
       uma = UMA_MENU_SHOWN;
       break;
-    case SyncPromoUI::SOURCE_SETTINGS:
+    case signin::SOURCE_SETTINGS:
       uma = UMA_SETTINGS_SHOWN;
       break;
-    case SyncPromoUI::SOURCE_EXTENSION_INSTALL_BUBBLE:
+    case signin::SOURCE_EXTENSION_INSTALL_BUBBLE:
       uma = UMA_EXTENSION_INSTALL_BUBBLE_SHOWN;
       break;
-    case SyncPromoUI::SOURCE_WEBSTORE_INSTALL:
+    case signin::SOURCE_WEBSTORE_INSTALL:
       uma = UMA_WEBSTORE_INSTALL_SHOWN;
       break;
-    case SyncPromoUI::SOURCE_APP_LAUNCHER:
+    case signin::SOURCE_APP_LAUNCHER:
       uma = UMA_APP_LAUNCHER_SHOWN;
       break;
-    case SyncPromoUI::SOURCE_APPS_PAGE_LINK:
+    case signin::SOURCE_APPS_PAGE_LINK:
       uma = UMA_APPS_PAGE_LINK_SHOWN;
       break;
-    case SyncPromoUI::SOURCE_BOOKMARK_BUBBLE:
+    case signin::SOURCE_BOOKMARK_BUBBLE:
       uma = UMA_BOOKMARK_BUBBLE_SHOWN;
       break;
-    case SyncPromoUI::SOURCE_UNKNOWN:
+    case signin::SOURCE_UNKNOWN:
       uma = UMA_UNKNOWN_SHOWN;
       break;
     default:
-      // If this assert hits, then the SyncPromoUI::Source enum has changed and
-      // the UMA enum above, this switch statement and histograms.xml all need
-      // to be updated to reflect that.
-      COMPILE_ASSERT(SyncPromoUI::SOURCE_UNKNOWN == 9,
+      // If this assert hits, then the signin::Source enum has
+      // changed and the UMA enum above, this switch statement and
+      // histograms.xml all need to be updated to reflect that.
+      COMPILE_ASSERT(signin::SOURCE_UNKNOWN == 9,
                      kSourceEnumHasChangedButNotThisSwitchStatement);
       NOTREACHED();
       break;
@@ -88,43 +88,43 @@
 }
 
 void RecordUserSignedIn(content::WebUI* web_ui) {
-  SyncPromoUI::Source source = SyncPromoUI::GetSourceForSyncPromoURL(
-        web_ui->GetWebContents()->GetURL());
+  signin::Source source =
+      signin::GetSourceForPromoURL(web_ui->GetWebContents()->GetURL());
   int uma = 0;
   switch (source) {
-    case SyncPromoUI::SOURCE_START_PAGE:
+    case signin::SOURCE_START_PAGE:
       uma = UMA_START_PAGE_SIGNED_IN;
       break;
-    case SyncPromoUI::SOURCE_NTP_LINK:
+    case signin::SOURCE_NTP_LINK:
       uma = UMA_NTP_LINK_SIGNED_IN;
       break;
-    case SyncPromoUI::SOURCE_MENU:
+    case signin::SOURCE_MENU:
       uma = UMA_MENU_SIGNED_IN;
       break;
-    case SyncPromoUI::SOURCE_SETTINGS:
+    case signin::SOURCE_SETTINGS:
       uma = UMA_SETTINGS_SIGNED_IN;
       break;
-    case SyncPromoUI::SOURCE_EXTENSION_INSTALL_BUBBLE:
+    case signin::SOURCE_EXTENSION_INSTALL_BUBBLE:
       uma = UMA_EXTENSION_INSTALL_BUBBLE_SIGNED_IN;
       break;
-    case SyncPromoUI::SOURCE_WEBSTORE_INSTALL:
+    case signin::SOURCE_WEBSTORE_INSTALL:
       uma = UMA_WEBSTORE_INSTALL_SIGNED_IN;
       break;
-    case SyncPromoUI::SOURCE_APP_LAUNCHER:
+    case signin::SOURCE_APP_LAUNCHER:
       uma = UMA_APP_LAUNCHER_SIGNED_IN;
       break;
-    case SyncPromoUI::SOURCE_APPS_PAGE_LINK:
+    case signin::SOURCE_APPS_PAGE_LINK:
       uma = UMA_APPS_PAGE_LINK_SIGNED_IN;
       break;
-    case SyncPromoUI::SOURCE_BOOKMARK_BUBBLE:
+    case signin::SOURCE_BOOKMARK_BUBBLE:
       uma = UMA_BOOKMARK_BUBBLE_SIGNED_IN;
       break;
-    case SyncPromoUI::SOURCE_UNKNOWN:
+    case signin::SOURCE_UNKNOWN:
       uma = UMA_UNKNOWN_SIGNED_IN;
       break;
     default:
       // This switch statement needs to be updated when the enum Source changes.
-      COMPILE_ASSERT(SyncPromoUI::SOURCE_UNKNOWN == 9,
+      COMPILE_ASSERT(signin::SOURCE_UNKNOWN == 9,
                      kSourceEnumHasChangedButNotThisSwitchStatement);
       NOTREACHED();
       break;
diff --git a/chrome/browser/ui/webui/sync_promo/sync_promo_trial.h b/chrome/browser/ui/webui/sync_promo/sync_promo_trial.h
index f522ba9..de0820e 100644
--- a/chrome/browser/ui/webui/sync_promo/sync_promo_trial.h
+++ b/chrome/browser/ui/webui/sync_promo/sync_promo_trial.h
@@ -6,13 +6,13 @@
 #define CHROME_BROWSER_UI_WEBUI_SYNC_PROMO_SYNC_PROMO_TRIAL_H_
 
 #include "base/basictypes.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 
 class Profile;
 namespace content {
 class WebUI;
 }
 
+// TODO(fdoray): This file should be moved to chrome/browser/signin.
 namespace sync_promo_trial {
 
 // Records that the user was shown the sync promo for any currently running sync
diff --git a/chrome/browser/ui/webui/sync_setup_handler.cc b/chrome/browser/ui/webui/sync_setup_handler.cc
index a7c9978..364c609 100644
--- a/chrome/browser/ui/webui/sync_setup_handler.cc
+++ b/chrome/browser/ui/webui/sync_setup_handler.cc
@@ -25,12 +25,12 @@
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/signin/signin_global_error.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/sync/signin_histogram.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
 #include "chrome/common/chrome_switches.h"
@@ -418,9 +418,9 @@
   web_ui()->CallJavascriptFunction(
       "SyncSetupOverlay.showSyncSetupPage", page);
 
-  // Suppress the sync promo once the user signs into sync. This way the user
-  // doesn't see the sync promo even if they sign out of sync later on.
-  SyncPromoUI::SetUserSkippedSyncPromo(GetProfile());
+  // Suppress the sign in promo once the user starts sync. This way the user
+  // doesn't see the sign in promo even if they sign out later on.
+  signin::SetUserSkippedPromo(GetProfile());
 
   ProfileSyncService* service = GetSyncService();
   DCHECK(service);
@@ -485,8 +485,8 @@
 }
 
 void SyncSetupHandler::DisplayGaiaLoginInNewTabOrWindow() {
-  GURL url(SyncPromoUI::GetSyncPromoURL(SyncPromoUI::SOURCE_SETTINGS,
-                                        true));  // auto close after success.
+  GURL url(signin::GetPromoURL(signin::SOURCE_SETTINGS,
+                               true));  // auto close after success.
   Browser* browser = chrome::FindBrowserWithWebContents(
       web_ui()->GetWebContents());
   if (!browser) {
@@ -729,9 +729,12 @@
     // on the settings page. So if we get here, it must be due to the user
     // cancelling signin (by reloading the sync settings page during initial
     // signin) or by directly navigating to settings/syncSetup
-    // (http://crbug.com/229836). So just exit.
+    // (http://crbug.com/229836). So just exit and go back to the settings page.
     DLOG(WARNING) << "Cannot display sync setup UI when not signed in";
     CloseSyncSetup();
+    StringValue page("done");
+    web_ui()->CallJavascriptFunction(
+        "SyncSetupOverlay.showSyncSetupPage", page);
     return;
   }
 
diff --git a/chrome/browser/ui/webui/sync_setup_handler_unittest.cc b/chrome/browser/ui/webui/sync_setup_handler_unittest.cc
index 6a01ec6..22b2d28 100644
--- a/chrome/browser/ui/webui/sync_setup_handler_unittest.cc
+++ b/chrome/browser/ui/webui/sync_setup_handler_unittest.cc
@@ -450,7 +450,11 @@
       .WillRepeatedly(Return(false));
   handler_->HandleShowSetupUI(NULL);
 
-  ASSERT_EQ(0U, web_ui_.call_data().size());
+  // We expect a call to SyncSetupOverlay.showSyncSetupPage.
+  ASSERT_EQ(1U, web_ui_.call_data().size());
+  const TestWebUI::CallData& data = web_ui_.call_data()[0];
+  EXPECT_EQ("SyncSetupOverlay.showSyncSetupPage", data.function_name);
+
   ASSERT_FALSE(handler_->is_configuring_sync());
   EXPECT_EQ(NULL,
             LoginUIServiceFactory::GetForProfile(
diff --git a/chrome/browser/ui/webui/tab_modal_confirm_dialog_webui.cc b/chrome/browser/ui/webui/tab_modal_confirm_dialog_webui.cc
deleted file mode 100644
index d5b8ed9..0000000
--- a/chrome/browser/ui/webui/tab_modal_confirm_dialog_webui.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/browser/ui/webui/tab_modal_confirm_dialog_webui.h"
-
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
-#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
-#include "chrome/common/url_constants.h"
-#include "components/web_modal/web_contents_modal_dialog_manager.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "grit/browser_resources.h"
-#include "grit/generated_resources.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/size.h"
-#include "ui/webui/web_ui_util.h"
-
-using content::WebContents;
-using content::WebUIMessageHandler;
-using web_modal::WebContentsModalDialogManager;
-
-// static
-TabModalConfirmDialog* TabModalConfirmDialog::Create(
-    TabModalConfirmDialogDelegate* delegate,
-    content::WebContents* web_contents) {
-  return new TabModalConfirmDialogWebUI(delegate, web_contents);
-}
-
-const int kDialogWidth = 400;
-const int kDialogHeight = 120;
-
-TabModalConfirmDialogWebUI::TabModalConfirmDialogWebUI(
-    TabModalConfirmDialogDelegate* delegate,
-    WebContents* web_contents)
-    : web_contents_(web_contents),
-      delegate_(delegate) {
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  content::WebUIDataSource* data_source = content::WebUIDataSource::Create(
-      chrome::kChromeUITabModalConfirmDialogHost);
-  data_source->SetDefaultResource(IDR_TAB_MODAL_CONFIRM_DIALOG_HTML);
-  data_source->DisableContentSecurityPolicy();
-  content::WebUIDataSource::Add(profile, data_source);
-
-  constrained_web_dialog_delegate_ =
-      CreateConstrainedWebDialog(profile, this, NULL, web_contents);
-  delegate_->set_operations_delegate(this);
-}
-
-ui::ModalType TabModalConfirmDialogWebUI::GetDialogModalType() const {
-  return ui::MODAL_TYPE_WINDOW;
-}
-
-string16 TabModalConfirmDialogWebUI::GetDialogTitle() const {
-  return delegate_->GetTitle();
-}
-
-GURL TabModalConfirmDialogWebUI::GetDialogContentURL() const {
-  return GURL(chrome::kChromeUITabModalConfirmDialogURL);
-}
-
-void TabModalConfirmDialogWebUI::GetWebUIMessageHandlers(
-    std::vector<WebUIMessageHandler*>* handlers) const {}
-
-void TabModalConfirmDialogWebUI::GetDialogSize(gfx::Size* size) const {
-  size->SetSize(kDialogWidth, kDialogHeight);
-}
-
-std::string TabModalConfirmDialogWebUI::GetDialogArgs() const {
-  DictionaryValue dict;
-  dict.SetString("message", delegate_->GetMessage());
-  dict.SetString("accept", delegate_->GetAcceptButtonTitle());
-  dict.SetString("cancel", delegate_->GetCancelButtonTitle());
-  webui::SetFontAndTextDirection(&dict);
-  std::string json;
-  base::JSONWriter::Write(&dict, &json);
-  return json;
-}
-
-void TabModalConfirmDialogWebUI::OnDialogClosed(
-    const std::string& json_retval) {
-  bool accepted = false;
-  if (!json_retval.empty()) {
-    scoped_ptr<Value> value(base::JSONReader::Read(json_retval));
-    if (!value.get() || !value->GetAsBoolean(&accepted))
-      NOTREACHED() << "Missing or unreadable response from dialog";
-  }
-
-  delegate_->set_operations_delegate(NULL);
-  if (accepted)
-    delegate_->Accept();
-  else
-    delegate_->Cancel();
-}
-
-void TabModalConfirmDialogWebUI::OnCloseContents(WebContents* source,
-                                                 bool* out_close_dialog) {}
-
-bool TabModalConfirmDialogWebUI::ShouldShowDialogTitle() const {
-  return true;
-}
-
-TabModalConfirmDialogWebUI::~TabModalConfirmDialogWebUI() {}
-
-void TabModalConfirmDialogWebUI::AcceptTabModalDialog() {
-}
-
-void TabModalConfirmDialogWebUI::CancelTabModalDialog() {
-}
-
-void TabModalConfirmDialogWebUI::CloseDialog() {
-  constrained_web_dialog_delegate_->OnDialogCloseFromWebUI();
-}
-
-void TabModalConfirmDialogWebUI::SetPreventCloseOnLoadStart(bool prevent) {
-  web_modal::WebContentsModalDialogManager* web_contents_modal_dialog_manager =
-      WebContentsModalDialogManager::FromWebContents(web_contents_);
-  web_contents_modal_dialog_manager->SetPreventCloseOnLoadStart(
-      constrained_web_dialog_delegate_->GetNativeDialog(),
-      prevent);
-}
diff --git a/chrome/browser/ui/webui/tab_modal_confirm_dialog_webui.h b/chrome/browser/ui/webui/tab_modal_confirm_dialog_webui.h
deleted file mode 100644
index 592a9f2..0000000
--- a/chrome/browser/ui/webui/tab_modal_confirm_dialog_webui.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_TAB_MODAL_CONFIRM_DIALOG_WEBUI_H_
-#define CHROME_BROWSER_UI_WEBUI_TAB_MODAL_CONFIRM_DIALOG_WEBUI_H_
-
-#if !(defined(USE_AURA) || defined(OS_CHROMEOS))
-#error Tab-modal confirm dialog should be shown with native UI.
-#endif
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
-#include "ui/web_dialogs/web_dialog_delegate.h"
-
-class ConstrainedWebDialogDelegate;
-
-namespace content {
-class WebContents;
-}
-
-// Displays a tab-modal dialog, i.e. a dialog that will block the current page
-// but still allow the user to switch to a different page.
-// To display the dialog, allocate this object on the heap. It will open the
-// dialog from its constructor and then delete itself when the user dismisses
-// the dialog.
-class TabModalConfirmDialogWebUI : public TabModalConfirmDialog,
-                                   public ui::WebDialogDelegate {
- public:
-  TabModalConfirmDialogWebUI(
-      TabModalConfirmDialogDelegate* dialog_delegate,
-      content::WebContents* web_contents);
-
-  // ui::WebDialogDelegate implementation.
-  virtual ui::ModalType GetDialogModalType() const OVERRIDE;
-  virtual string16 GetDialogTitle() const OVERRIDE;
-  virtual GURL GetDialogContentURL() const OVERRIDE;
-  virtual void GetWebUIMessageHandlers(
-      std::vector<content::WebUIMessageHandler*>* handlers) const OVERRIDE;
-  virtual void GetDialogSize(gfx::Size* size) const OVERRIDE;
-  virtual std::string GetDialogArgs() const OVERRIDE;
-  virtual void OnDialogClosed(const std::string& json_retval) OVERRIDE;
-  virtual void OnCloseContents(content::WebContents* source,
-                               bool* out_close_dialog) OVERRIDE;
-  virtual bool ShouldShowDialogTitle() const OVERRIDE;
-
-  ConstrainedWebDialogDelegate* constrained_web_dialog_delegate() {
-    return constrained_web_dialog_delegate_;
-  }
-
- private:
-  virtual ~TabModalConfirmDialogWebUI();
-
-  // TabModalConfirmDialog:
-  virtual void AcceptTabModalDialog() OVERRIDE;
-  virtual void CancelTabModalDialog() OVERRIDE;
-
-  // TabModalConfirmDialogCloseDelegate:
-  virtual void CloseDialog() OVERRIDE;
-  virtual void SetPreventCloseOnLoadStart(bool prevent) OVERRIDE;
-
-  content::WebContents* web_contents_;
-
-  scoped_ptr<TabModalConfirmDialogDelegate> delegate_;
-
-  // Deletes itself.
-  ConstrainedWebDialogDelegate* constrained_web_dialog_delegate_;
-
-  DISALLOW_COPY_AND_ASSIGN(TabModalConfirmDialogWebUI);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_TAB_MODAL_CONFIRM_DIALOG_WEBUI_H_
diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash.cc b/chrome/browser/ui/window_sizer/window_sizer_ash.cc
index 254d047..a4ee1d1 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_ash.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_ash.cc
@@ -6,7 +6,7 @@
 
 #include "ash/ash_switches.h"
 #include "ash/shell.h"
-#include "ash/wm/window_cycle_controller.h"
+#include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
@@ -72,7 +72,7 @@
 
   // Get a list of all windows.
   const std::vector<aura::Window*> windows =
-      ash::WindowCycleController::BuildWindowList(NULL, false);
+      ash::MruWindowTracker::BuildWindowList(false);
 
   if (windows.empty())
     return NULL;
@@ -135,15 +135,6 @@
   return false;
 }
 
-// Adjust the |target_in_screen| rectangle so it moves as much as possible into
-// the |work_area| .
-void AdjustTargetRectVerticallyAgainstWorkspace(const gfx::Rect& work_area,
-                                                gfx::Rect* target_in_screen) {
-  if (target_in_screen->bottom() > work_area.bottom())
-    target_in_screen->set_y(std::max(work_area.y(),
-        work_area.bottom() - target_in_screen->height()));
-}
-
 }  // namespace
 
 // static
@@ -205,8 +196,7 @@
     if ((!count || !top_window)) {
       if (has_saved_bounds) {
         // Restore to previous state - if there is one.
-        AdjustTargetRectVerticallyAgainstWorkspace(work_area,
-                                                     bounds_in_screen);
+        bounds_in_screen->AdjustToFit(work_area);
         return true;
       }
       // When using "small screens" we want to always open in full screen mode.
@@ -239,7 +229,7 @@
         bounds_in_screen->CenterPoint().x() < work_area.CenterPoint().x();
 
     MoveRect(work_area, *bounds_in_screen, move_right);
-    AdjustTargetRectVerticallyAgainstWorkspace(work_area, bounds_in_screen);
+    bounds_in_screen->AdjustToFit(work_area);
     return true;
   }
 
diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc b/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc
new file mode 100644
index 0000000..61f702d
--- /dev/null
+++ b/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc
@@ -0,0 +1,199 @@
+// Copyright 2013 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.
+
+#include "ash/launcher/launcher.h"
+#include "ash/launcher/launcher_view.h"
+#include "ash/shell.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/base/test/ui_controls.h"
+#include "ui/views/controls/menu/menu_controller.h"
+#include "ui/views/view.h"
+#include "ui/views/view_model.h"
+
+namespace {
+
+class WindowSizerTest : public InProcessBrowserTest {
+ public:
+  WindowSizerTest() {}
+  virtual ~WindowSizerTest() {}
+
+  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+    InProcessBrowserTest::SetUpCommandLine(command_line);
+    // Make screens sufficiently wide to host 2 browsers side by side.
+    command_line->AppendSwitchASCII("ash-host-window-bounds",
+                                    "600x600,601+0-600x600");
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WindowSizerTest);
+};
+
+void CloseBrowser(Browser* browser) {
+  browser->window()->Close();
+  base::MessageLoop::current()->RunUntilIdle();
+}
+
+gfx::Rect GetChromeIconBoundsForRootWindow(aura::RootWindow* root_window) {
+  ash::Launcher* launcher = ash::Launcher::ForWindow(root_window);
+  const ash::internal::LauncherView* launcher_view =
+      launcher->GetLauncherViewForTest();
+  const views::ViewModel* view_model = launcher_view->view_model_for_test();
+
+  EXPECT_EQ(2, view_model->view_size());
+  return view_model->view_at(0)->GetBoundsInScreen();
+}
+
+void OpenBrowserUsingShelfOnRootWindow(aura::RootWindow* root_window) {
+  aura::test::EventGenerator generator(root_window);
+  gfx::Point center =
+      GetChromeIconBoundsForRootWindow(root_window).CenterPoint();
+  gfx::Display display =
+      ash::Shell::GetScreen()->GetDisplayNearestWindow(root_window);
+  const gfx::Point& origin = display.bounds().origin();
+  center.Offset(- origin.x(), - origin.y());
+  generator.MoveMouseTo(center);
+  generator.ClickLeftButton();
+}
+
+}  // namespace
+
+#if defined(OS_WIN)
+#define MAYBE_OpenBrowserUsingShelfOnOhterDisplay DISABLED_OpenBrowserUsingShelfOnOhterDisplay
+#define MAYBE_OpenBrowserUsingContextMenuOnOhterDisplay DISABLED_OpenBrowserUsingContextMenuOnOhterDisplay
+#else
+#define MAYBE_OpenBrowserUsingShelfOnOhterDisplay OpenBrowserUsingShelfOnOhterDisplay
+#define MAYBE_OpenBrowserUsingContextMenuOnOhterDisplay OpenBrowserUsingContextMenuOnOhterDisplay
+#endif
+
+IN_PROC_BROWSER_TEST_F(WindowSizerTest,
+                       MAYBE_OpenBrowserUsingShelfOnOhterDisplay) {
+  // Don't shutdown when closing the last browser window.
+  chrome::StartKeepAlive();
+
+  ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
+
+  BrowserList* browser_list =
+      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
+
+  EXPECT_EQ(1u, browser_list->size());
+  // Close the browser window so that clicking icon will create a new window.
+  CloseBrowser(browser_list->get(0));
+  EXPECT_EQ(0u, browser_list->size());
+  EXPECT_EQ(root_windows[0], ash::Shell::GetActiveRootWindow());
+
+  OpenBrowserUsingShelfOnRootWindow(root_windows[1]);
+
+  // A new browser must be created on 2nd display.
+  EXPECT_EQ(1u, browser_list->size());
+  EXPECT_EQ(root_windows[1],
+            browser_list->get(0)->window()->GetNativeWindow()->GetRootWindow());
+  EXPECT_EQ(root_windows[1], ash::Shell::GetActiveRootWindow());
+
+  // Close the browser window so that clicking icon will create a new window.
+  CloseBrowser(browser_list->get(0));
+  EXPECT_EQ(0u, browser_list->size());
+
+  OpenBrowserUsingShelfOnRootWindow(root_windows[0]);
+
+  // A new browser must be created on 1st display.
+  EXPECT_EQ(1u, browser_list->size());
+  EXPECT_EQ(root_windows[0],
+            browser_list->get(0)->window()->GetNativeWindow()->GetRootWindow());
+  EXPECT_EQ(root_windows[0], ash::Shell::GetActiveRootWindow());
+
+  // Balanced with the chrome::StartKeepAlive above.
+  chrome::EndKeepAlive();
+}
+
+namespace {
+
+class WindowSizerContextMenuTest : public WindowSizerTest {
+ public:
+  WindowSizerContextMenuTest() {}
+  virtual ~WindowSizerContextMenuTest() {}
+
+  static void Step1(gfx::Point release_point) {
+    ui_controls::SendMouseEventsNotifyWhenDone(
+        ui_controls::RIGHT, ui_controls::DOWN,
+        base::Bind(&WindowSizerContextMenuTest::Step2, release_point));
+  }
+
+  static void Step2(gfx::Point release_point) {
+    ui_controls::SendMouseMoveNotifyWhenDone(
+        release_point.x(), release_point.y(),
+        base::Bind(&WindowSizerContextMenuTest::Step3));
+  }
+
+  static void Step3() {
+    ui_controls::SendMouseEventsNotifyWhenDone(
+        ui_controls::RIGHT, ui_controls::UP,
+        base::Bind(&WindowSizerContextMenuTest::QuitLoop));
+  }
+
+  static void QuitLoop() {
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::MessageLoop::QuitWhenIdleClosure());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WindowSizerContextMenuTest);
+};
+
+void OpenBrowserUsingContextMenuOnRootWindow(aura::RootWindow* root_window) {
+  gfx::Point chrome_icon =
+      GetChromeIconBoundsForRootWindow(root_window).CenterPoint();
+  gfx::Point release_point = chrome_icon;
+  release_point.Offset(50, -120);
+  ui_controls::SendMouseMoveNotifyWhenDone(
+      chrome_icon.x(), chrome_icon.y(),
+      base::Bind(&WindowSizerContextMenuTest::Step1, release_point));
+  base::MessageLoop::current()->Run();
+}
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(WindowSizerContextMenuTest,
+                       MAYBE_OpenBrowserUsingContextMenuOnOhterDisplay) {
+  // Don't shutdown when closing the last browser window.
+  chrome::StartKeepAlive();
+
+  views::MenuController::TurnOffContextMenuSelectionHoldForTest();
+
+  ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
+
+  BrowserList* browser_list =
+      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
+
+  EXPECT_EQ(1u, browser_list->size());
+  EXPECT_EQ(root_windows[0], ash::Shell::GetActiveRootWindow());
+  CloseBrowser(browser_list->get(0));
+
+  OpenBrowserUsingContextMenuOnRootWindow(root_windows[1]);
+
+  // A new browser must be created on 2nd display.
+  EXPECT_EQ(1u, browser_list->size());
+  EXPECT_EQ(root_windows[1],
+            browser_list->get(0)->window()->GetNativeWindow()->GetRootWindow());
+  EXPECT_EQ(root_windows[1], ash::Shell::GetActiveRootWindow());
+
+  OpenBrowserUsingContextMenuOnRootWindow(root_windows[0]);
+
+  // Next new browser must be created on 1st display.
+  EXPECT_EQ(2u, browser_list->size());
+  EXPECT_EQ(root_windows[0],
+            browser_list->get(1)->window()->GetNativeWindow()->GetRootWindow());
+  EXPECT_EQ(root_windows[0], ash::Shell::GetActiveRootWindow());
+
+  // Balanced with the chrome::StartKeepAlive above.
+  chrome::EndKeepAlive();
+}
diff --git a/chrome/browser/undo/undo_manager.cc b/chrome/browser/undo/undo_manager.cc
index 6b1c3de..590fb08 100644
--- a/chrome/browser/undo/undo_manager.cc
+++ b/chrome/browser/undo/undo_manager.cc
@@ -8,6 +8,13 @@
 #include "base/logging.h"
 #include "chrome/browser/undo/undo_operation.h"
 
+namespace {
+
+// Maximum number of changes that can be undone.
+const size_t kMaxUndoGroups = 100;
+
+}  // namespace
+
 // UndoGroup ------------------------------------------------------------------
 
 UndoGroup::UndoGroup() {
@@ -53,7 +60,7 @@
 
 void UndoManager::AddUndoOperation(scoped_ptr<UndoOperation> operation) {
   if (IsUndoTrakingSuspended()) {
-    RemoveAllActions();
+    RemoveAllOperations();
     operation.reset();
     return;
   }
@@ -63,10 +70,7 @@
   } else {
     UndoGroup* new_action = new UndoGroup();
     new_action->AddOperation(operation.Pass());
-    GetActiveUndoGroup()->insert(GetActiveUndoGroup()->end(), new_action);
-
-    // A new user action invalidates any available redo actions.
-    RemoveAllRedoActions();
+    AddUndoGroup(new_action);
   }
 }
 
@@ -85,11 +89,8 @@
   DCHECK_GE(group_actions_count_, 0);
 
   bool is_user_action = !performing_undo_ && !performing_redo_;
-  if (pending_grouped_action_->has_operations()) {
-    GetActiveUndoGroup()->push_back(pending_grouped_action_.release());
-    // User actions invalidate any available redo actions.
-    if (is_user_action)
-      RemoveAllRedoActions();
+  if (!pending_grouped_action_->undo_operations().empty()) {
+    AddUndoGroup(pending_grouped_action_.release());
   } else {
     // No changes were executed since we started grouping actions, so the
     // pending UndoGroup should be discarded.
@@ -114,6 +115,28 @@
   return undo_suspended_count_ > 0;
 }
 
+std::vector<UndoOperation*> UndoManager::GetAllUndoOperations() const {
+  std::vector<UndoOperation*> result;
+  for (size_t i = 0; i < undo_actions_.size(); ++i) {
+    const std::vector<UndoOperation*>& operations =
+        undo_actions_[i]->undo_operations();
+    result.insert(result.end(), operations.begin(), operations.end());
+  }
+  for (size_t i = 0; i < redo_actions_.size(); ++i) {
+    const std::vector<UndoOperation*>& operations =
+        redo_actions_[i]->undo_operations();
+    result.insert(result.end(), operations.begin(), operations.end());
+  }
+
+  return result;
+}
+
+void UndoManager::RemoveAllOperations() {
+  DCHECK(!group_actions_count_);
+  undo_actions_.clear();
+  redo_actions_.clear();
+}
+
 void UndoManager::Undo(bool* performing_indicator,
                        ScopedVector<UndoGroup>* active_undo_group) {
   // Check that action grouping has been correctly ended.
@@ -132,13 +155,16 @@
   EndGroupingActions();
 }
 
-void UndoManager::RemoveAllActions() {
-  undo_actions_.clear();
-  RemoveAllRedoActions();
-}
+void UndoManager::AddUndoGroup(UndoGroup* new_undo_group) {
+  GetActiveUndoGroup()->push_back(new_undo_group);
 
-void UndoManager::RemoveAllRedoActions() {
-  redo_actions_.clear();
+  // User actions invalidate any available redo actions.
+  if (is_user_action())
+    redo_actions_.clear();
+
+  // Limit the number of undo levels so the undo stack does not grow unbounded.
+  if (GetActiveUndoGroup()->size() > kMaxUndoGroups)
+    GetActiveUndoGroup()->erase(GetActiveUndoGroup()->begin());
 }
 
 ScopedVector<UndoGroup>* UndoManager::GetActiveUndoGroup() {
diff --git a/chrome/browser/undo/undo_manager.h b/chrome/browser/undo/undo_manager.h
index 4031cba..739d151 100644
--- a/chrome/browser/undo/undo_manager.h
+++ b/chrome/browser/undo/undo_manager.h
@@ -21,10 +21,9 @@
   ~UndoGroup();
 
   void AddOperation(scoped_ptr<UndoOperation> operation);
-  bool has_operations() const {
-    return !operations_.empty();
+  const std::vector<UndoOperation*>& undo_operations() {
+    return operations_.get();
   }
-
   void Undo();
 
  private:
@@ -61,13 +60,24 @@
   void ResumeUndoTracking();
   bool IsUndoTrakingSuspended() const;
 
+  // Returns all UndoOperations that are awaiting Undo or Redo. Note that
+  // ownership of the UndoOperations is retained by UndoManager.
+  std::vector<UndoOperation*> GetAllUndoOperations() const;
+
+  // Remove all undo and redo operations. Note that grouping of actions and
+  // suspension of undo tracking states are left unchanged.
+  void RemoveAllOperations();
+
  private:
   void Undo(bool* performing_indicator,
             ScopedVector<UndoGroup>* active_undo_group);
+  bool is_user_action() const { return !performing_undo_ && !performing_redo_; }
 
-  void RemoveAllActions();
-  void RemoveAllRedoActions();
+  // Handle the addition of |new_undo_group| to the active undo group container.
+  void AddUndoGroup(UndoGroup* new_undo_group);
 
+  // Returns the undo or redo UndoGroup container that should store the next
+  // change taking into account if an undo or redo is being executed.
   ScopedVector<UndoGroup>* GetActiveUndoGroup();
 
   // Containers of user actions ready for an undo or redo treated as a stack.
diff --git a/chrome/browser/undo/undo_manager_test.cc b/chrome/browser/undo/undo_manager_test.cc
index 173bcad..f4cad09 100644
--- a/chrome/browser/undo/undo_manager_test.cc
+++ b/chrome/browser/undo/undo_manager_test.cc
@@ -11,28 +11,16 @@
 
 class TestUndoOperation;
 
+// TestUndoService -------------------------------------------------------------
+
 class TestUndoService {
  public:
-  TestUndoService()
-      : performing_redo_(false),
-        undo_operation_count_(0),
-        redo_operation_count_(0) {
-  }
-  ~TestUndoService() {}
+  TestUndoService();
+  ~TestUndoService();
 
-  void Redo() {
-    base::AutoReset<bool> incoming_changes(&performing_redo_, true);
-    undo_manager_.Redo();
-  }
-
+  void Redo();
   void TriggerOperation();
-
-  void RecordUndoCall() {
-    if (performing_redo_)
-      ++redo_operation_count_;
-    else
-      ++undo_operation_count_;
-  }
+  void RecordUndoCall();
 
   UndoManager undo_manager_;
 
@@ -42,17 +30,15 @@
   int redo_operation_count_;
 };
 
+// TestUndoOperation -----------------------------------------------------------
+
 class TestUndoOperation : public UndoOperation {
  public:
-  explicit TestUndoOperation(TestUndoService* undo_service)
-      : undo_service_(undo_service) {
-  }
-  virtual ~TestUndoOperation() {}
+  explicit TestUndoOperation(TestUndoService* undo_service);
+  virtual ~TestUndoOperation();
 
-  virtual void Undo() OVERRIDE {
-    undo_service_->TriggerOperation();
-    undo_service_->RecordUndoCall();
-  }
+  // UndoOperation:
+  virtual void Undo() OVERRIDE;
 
  private:
   TestUndoService* undo_service_;
@@ -60,11 +46,47 @@
   DISALLOW_COPY_AND_ASSIGN(TestUndoOperation);
 };
 
+TestUndoOperation::TestUndoOperation(TestUndoService* undo_service)
+      : undo_service_(undo_service) {
+}
+
+TestUndoOperation::~TestUndoOperation() {
+}
+
+void TestUndoOperation::Undo() {
+  undo_service_->TriggerOperation();
+  undo_service_->RecordUndoCall();
+}
+
+// TestUndoService -------------------------------------------------------------
+
+TestUndoService::TestUndoService() : performing_redo_(false),
+                                     undo_operation_count_(0),
+                                     redo_operation_count_(0) {
+}
+
+TestUndoService::~TestUndoService() {
+}
+
+void TestUndoService::Redo() {
+  base::AutoReset<bool> incoming_changes(&performing_redo_, true);
+  undo_manager_.Redo();
+}
+
 void TestUndoService::TriggerOperation() {
   scoped_ptr<TestUndoOperation> op(new TestUndoOperation(this));
   undo_manager_.AddUndoOperation(op.PassAs<UndoOperation>());
 }
 
+void TestUndoService::RecordUndoCall() {
+  if (performing_redo_)
+    ++redo_operation_count_;
+  else
+    ++undo_operation_count_;
+}
+
+// Tests -----------------------------------------------------------------------
+
 TEST(UndoServiceTest, AddUndoActions) {
   TestUndoService undo_service;
 
@@ -163,4 +185,25 @@
   EXPECT_EQ(0U, undo_service.undo_manager_.redo_count());
 }
 
+TEST(UndoServiceTest, GetAllUndoOperations) {
+  TestUndoService undo_service;
+
+  undo_service.TriggerOperation();
+
+  undo_service.undo_manager_.StartGroupingActions();
+  undo_service.TriggerOperation();
+  undo_service.TriggerOperation();
+  undo_service.undo_manager_.EndGroupingActions();
+
+  undo_service.TriggerOperation();
+
+  undo_service.undo_manager_.Undo();
+  ASSERT_EQ(2U, undo_service.undo_manager_.undo_count());
+  ASSERT_EQ(1U, undo_service.undo_manager_.redo_count());
+
+  std::vector<UndoOperation*> all_operations =
+      undo_service.undo_manager_.GetAllUndoOperations();
+  EXPECT_EQ(4U, all_operations.size());
+}
+
 } // namespace
diff --git a/chrome/browser/undo/undo_manager_utils.cc b/chrome/browser/undo/undo_manager_utils.cc
new file mode 100644
index 0000000..48c1d0e
--- /dev/null
+++ b/chrome/browser/undo/undo_manager_utils.cc
@@ -0,0 +1,29 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/undo/undo_manager_utils.h"
+
+#include "chrome/browser/undo/undo_manager.h"
+
+// ScopedSuspendUndoTracking --------------------------------------------------
+
+ScopedSuspendUndoTracking::ScopedSuspendUndoTracking(UndoManager* undo_manager)
+    : undo_manager_(undo_manager) {
+  undo_manager_->SuspendUndoTracking();
+}
+
+ScopedSuspendUndoTracking::~ScopedSuspendUndoTracking() {
+  undo_manager_->ResumeUndoTracking();
+}
+
+// ScopedGroupingAction -------------------------------------------------------
+
+ScopedGroupingAction::ScopedGroupingAction(UndoManager* undo_manager)
+    : undo_manager_(undo_manager) {
+  undo_manager_->StartGroupingActions();
+}
+
+ScopedGroupingAction::~ScopedGroupingAction() {
+  undo_manager_->EndGroupingActions();
+}
diff --git a/chrome/browser/undo/undo_manager_utils.h b/chrome/browser/undo/undo_manager_utils.h
new file mode 100644
index 0000000..b99817a
--- /dev/null
+++ b/chrome/browser/undo/undo_manager_utils.h
@@ -0,0 +1,41 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_UNDO_UNDO_MANAGER_UTILS_H_
+#define CHROME_BROWSER_UNDO_UNDO_MANAGER_UTILS_H_
+
+#include "base/basictypes.h"
+
+class UndoManager;
+
+// ScopedSuspendUndoTracking ---------------------------------------------------
+
+// Scopes the suspension of the undo tracking for non-user initiated changes
+// such as those occuring during profile synchronization.
+class ScopedSuspendUndoTracking {
+ public:
+  explicit ScopedSuspendUndoTracking(UndoManager* undo_manager);
+  ~ScopedSuspendUndoTracking();
+
+ private:
+  UndoManager* undo_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedSuspendUndoTracking);
+};
+
+// ScopedGroupingAction --------------------------------------------------------
+
+// Scopes the grouping of a set of changes into one undoable action.
+class ScopedGroupingAction {
+ public:
+  explicit ScopedGroupingAction(UndoManager* undo_manager);
+  ~ScopedGroupingAction();
+
+ private:
+  UndoManager* undo_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedGroupingAction);
+};
+
+#endif  // CHROME_BROWSER_UNDO_UNDO_MANAGER_UTILS_H_
diff --git a/chrome/browser/unload_browsertest.cc b/chrome/browser/unload_browsertest.cc
index c6f38ad..1ed5cee 100644
--- a/chrome/browser/unload_browsertest.cc
+++ b/chrome/browser/unload_browsertest.cc
@@ -27,6 +27,11 @@
 #include "content/test/net/url_request_mock_http_job.h"
 #include "net/url_request/url_request_test_util.h"
 
+#if defined(OS_WIN)
+// For version specific disabled tests below (http://crbug.com/267597).
+#include "base/win/windows_version.h"
+#endif
+
 using base::TimeDelta;
 using content::BrowserThread;
 
@@ -477,6 +482,11 @@
 // Test that fast-tab-close works when closing a tab with an unload handler
 // (http://crbug.com/142458).
 IN_PROC_BROWSER_TEST_F(FastUnloadTest, UnloadHidden) {
+#if defined(OS_WIN)
+  // Flaky on Win7+ bots (http://crbug.com/267597).
+  if (base::win::GetVersion() >= base::win::VERSION_WIN7)
+    return;
+#endif
   NavigateToPage("no_listeners");
   NavigateToPageInNewTab("unload_sets_cookie");
   EXPECT_EQ("", GetCookies("no_listeners"));
@@ -522,6 +532,11 @@
   window_observer.Wait();
 }
 IN_PROC_BROWSER_TEST_F(FastUnloadTest, ClosingLastTabFinishesUnload) {
+#if defined(OS_WIN)
+  // Flaky on Win7+ bots (http://crbug.com/267597).
+  if (base::win::GetVersion() >= base::win::VERSION_WIN7)
+    return;
+#endif
   // Check for cookie set in unload handler of PRE_ test.
   NavigateToPage("no_listeners");
   EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
@@ -546,6 +561,11 @@
   window_observer.Wait();
 }
 IN_PROC_BROWSER_TEST_F(FastUnloadTest, WindowCloseFinishesUnload) {
+#if defined(OS_WIN)
+  // Flaky on Win7+ bots (http://crbug.com/267597).
+  if (base::win::GetVersion() >= base::win::VERSION_WIN7)
+    return;
+#endif
   // Check for cookie set in unload during PRE_ test.
   NavigateToPage("no_listeners");
   EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
diff --git a/chrome/browser/usb/usb_context.cc b/chrome/browser/usb/usb_context.cc
new file mode 100644
index 0000000..f4e55ba
--- /dev/null
+++ b/chrome/browser/usb/usb_context.cc
@@ -0,0 +1,75 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/usb/usb_context.h"
+
+#include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "content/public/browser/browser_thread.h"
+#include "third_party/libusb/src/libusb/interrupt.h"
+#include "third_party/libusb/src/libusb/libusb.h"
+
+// The UsbEventHandler works around a design flaw in the libusb interface. There
+// is currently no way to signal to libusb that any caller into one of the event
+// handler calls should return without handling any events.
+class UsbContext::UsbEventHandler : public base::PlatformThread::Delegate {
+ public:
+  explicit UsbEventHandler(libusb_context* context);
+  virtual ~UsbEventHandler();
+
+  // base::PlatformThread::Delegate
+  virtual void ThreadMain() OVERRIDE;
+
+ private:
+  volatile bool running_;
+  libusb_context* context_;
+  base::PlatformThreadHandle thread_handle_;
+  base::WaitableEvent start_polling_;
+  DISALLOW_COPY_AND_ASSIGN(UsbEventHandler);
+};
+
+UsbContext::UsbEventHandler::UsbEventHandler(libusb_context* context)
+    : running_(true),
+      context_(context),
+      thread_handle_(0),
+      start_polling_(false, false) {
+  bool success = base::PlatformThread::Create(0, this, &thread_handle_);
+  DCHECK(success) << "Failed to create USB IO handling thread.";
+  start_polling_.Wait();
+}
+
+UsbContext::UsbEventHandler::~UsbEventHandler() {
+  running_ = false;
+  // Spreading running_ to the UsbEventHandler thread.
+  base::subtle::MemoryBarrier();
+  libusb_interrupt_handle_event(context_);
+  base::PlatformThread::Join(thread_handle_);
+}
+
+void UsbContext::UsbEventHandler::ThreadMain() {
+  base::PlatformThread::SetName("UsbEventHandler");
+  VLOG(1) << "UsbEventHandler started.";
+  if (running_) {
+    start_polling_.Signal();
+    libusb_handle_events(context_);
+  }
+  while (running_)
+    libusb_handle_events(context_);
+  VLOG(1) << "UsbEventHandler shutting down.";
+}
+
+UsbContext::UsbContext() : context_(NULL) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  CHECK_EQ(0, libusb_init(&context_)) << "Cannot initialize libusb";
+  event_handler_ = new UsbEventHandler(context_);
+}
+
+UsbContext::~UsbContext() {
+  // destruction of UsbEventHandler is a blocking operation.
+  DCHECK(thread_checker_.CalledOnValidThread());
+  delete event_handler_;
+  event_handler_ = NULL;
+  libusb_exit(context_);
+}
diff --git a/chrome/browser/usb/usb_context.h b/chrome/browser/usb/usb_context.h
new file mode 100644
index 0000000..6cdb946
--- /dev/null
+++ b/chrome/browser/usb/usb_context.h
@@ -0,0 +1,42 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_USB_USB_CONTEXT_H_
+#define CHROME_BROWSER_USB_USB_CONTEXT_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "content/public/browser/browser_thread.h"
+
+struct libusb_context;
+
+typedef libusb_context* PlatformUsbContext;
+
+// Ref-counted wrapper for libusb_context*.
+// It also manages the life-cycle of UsbEventHandler.
+// It is a blocking operation to delete UsbContext.
+// Destructor must be called on FILE thread.
+class UsbContext : public base::RefCountedThreadSafe<UsbContext> {
+ public:
+  PlatformUsbContext context() const { return context_; }
+
+ protected:
+  friend class UsbService;
+  friend class base::RefCountedThreadSafe<UsbContext>;
+
+  UsbContext();
+  virtual ~UsbContext();
+
+ private:
+  class UsbEventHandler;
+  PlatformUsbContext context_;
+  UsbEventHandler* event_handler_;
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(UsbContext);
+};
+
+#endif  // CHROME_BROWSER_USB_USB_CONTEXT_H_
diff --git a/chrome/browser/usb/usb_service_unittest.cc b/chrome/browser/usb/usb_context_unittest.cc
similarity index 63%
rename from chrome/browser/usb/usb_service_unittest.cc
rename to chrome/browser/usb/usb_context_unittest.cc
index 44864d8..79c7c84 100644
--- a/chrome/browser/usb/usb_service_unittest.cc
+++ b/chrome/browser/usb/usb_context_unittest.cc
@@ -2,18 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/usb/usb_service.h"
+#include "chrome/browser/usb/usb_context.h"
 
+#include "base/threading/platform_thread.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
-class UsbServiceTest : public testing::Test {
+class UsbContextTest : public testing::Test {
  protected:
-  class UsbServiceForTest : public UsbService {};
+  class UsbContextForTest : public UsbContext {
+   public:
+    UsbContextForTest() : UsbContext() {}
+   private:
+    virtual ~UsbContextForTest() {}
+    DISALLOW_COPY_AND_ASSIGN(UsbContextForTest);
+  };
 };
 
+}  // namespace
+
 #if defined(OS_LINUX)
 // Linux trybot does not support usb.
 #define MAYBE_GracefulShutdown DISABLED_GracefulShutdown
@@ -24,15 +33,13 @@
 #define MAYBE_GracefulShutdown GracefulShutdown
 #endif
 
-TEST_F(UsbServiceTest, MAYBE_GracefulShutdown) {
+TEST_F(UsbContextTest, MAYBE_GracefulShutdown) {
   base::TimeTicks start = base::TimeTicks::Now();
   {
-    scoped_ptr<UsbServiceForTest> service(new UsbServiceForTest());
+    scoped_refptr<UsbContextForTest> context(new UsbContextForTest());
   }
   base::TimeDelta elapse = base::TimeTicks::Now() - start;
   if (elapse > base::TimeDelta::FromSeconds(2)) {
     FAIL();
   }
 }
-
-}  // namespace
diff --git a/chrome/browser/usb/usb_device_handle.cc b/chrome/browser/usb/usb_device_handle.cc
index b6e54f4..56c3dc7 100644
--- a/chrome/browser/usb/usb_device_handle.cc
+++ b/chrome/browser/usb/usb_device_handle.cc
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright (c) 2013 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.
 
@@ -127,7 +127,7 @@
   DCHECK(ContainsKey(transfers_, handle)) << "Missing transfer completed";
   Transfer* const transfer = &transfers_[handle];
 
-  DCHECK(handle->actual_length >= 0) << "Negative actual length received";
+  DCHECK_GE(handle->actual_length, 0) << "Negative actual length received";
   size_t actual_length =
       static_cast<size_t>(std::max(handle->actual_length, 0));
 
@@ -272,9 +272,15 @@
     size = libusb_get_string_descriptor(
         handle_, desc.iSerialNumber, langid[i],
         reinterpret_cast<unsigned char*>(&text[0]), sizeof(text));
-    if (size < 0)
+    if (size <= 2)
       continue;
-    *serial = base::string16(text, size / 2);
+    if ((text[0] >> 8) != LIBUSB_DT_STRING)
+      continue;
+    if ((text[0] & 255) > size)
+      continue;
+
+    size = size / 2 - 1;
+    *serial = base::string16(text + 1, size);
     return true;
   }
   return false;
diff --git a/chrome/browser/usb/usb_device_handle.h b/chrome/browser/usb/usb_device_handle.h
index ef059d7..0659955 100644
--- a/chrome/browser/usb/usb_device_handle.h
+++ b/chrome/browser/usb/usb_device_handle.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright (c) 2013 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.
 
diff --git a/chrome/browser/usb/usb_service.cc b/chrome/browser/usb/usb_service.cc
index 9cba20c..d850488 100644
--- a/chrome/browser/usb/usb_service.cc
+++ b/chrome/browser/usb/usb_service.cc
@@ -10,9 +10,13 @@
 #include "base/bind_helpers.h"
 #include "base/logging.h"
 #include "base/stl_util.h"
-#include "base/synchronization/waitable_event.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/usb/usb_context.h"
 #include "chrome/browser/usb/usb_device_handle.h"
-#include "third_party/libusb/src/libusb/interrupt.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
 #include "third_party/libusb/src/libusb/libusb.h"
 
 #if defined(OS_CHROMEOS)
@@ -21,70 +25,64 @@
 #include "chromeos/dbus/permission_broker_client.h"
 #endif  // defined(OS_CHROMEOS)
 
+namespace content {
+
+class NotificationDetails;
+class NotificationSource;
+
+}  // namespace content
+
+using content::BrowserThread;
 using std::vector;
 
-// The UsbEventHandler works around a design flaw in the libusb interface. There
-// is currently no way to signal to libusb that any caller into one of the event
-// handler calls should return without handling any events.
-class UsbEventHandler : public base::PlatformThread::Delegate {
+namespace {
+
+class ExitObserver : public content::NotificationObserver {
  public:
-  explicit UsbEventHandler(PlatformUsbContext context)
-      : running_(true),
-        context_(context),
-        thread_handle_(0),
-        started_event_(false, false) {
-    base::PlatformThread::Create(0, this, &thread_handle_);
-    started_event_.Wait();
-  }
-
-  virtual ~UsbEventHandler() {}
-
-  virtual void ThreadMain() OVERRIDE {
-    base::PlatformThread::SetName("UsbEventHandler");
-    VLOG(1) << "UsbEventHandler started.";
-    started_event_.Signal();
-    while (running_)
-      libusb_handle_events(context_);
-    VLOG(1) << "UsbEventHandler shutting down.";
-  }
-
-  void Stop() {
-    running_ = false;
-    base::subtle::MemoryBarrier();
-    libusb_interrupt_handle_event(context_);
-    base::PlatformThread::Join(thread_handle_);
+  explicit ExitObserver(UsbService* service) : service_(service) {
+    registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
+                   content::NotificationService::AllSources());
   }
 
  private:
-  volatile bool running_;
-  PlatformUsbContext context_;
-  base::PlatformThreadHandle thread_handle_;
-  base::WaitableEvent started_event_;
-  DISALLOW_COPY_AND_ASSIGN(UsbEventHandler);
+  // content::NotificationObserver
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE {
+    if (type == chrome::NOTIFICATION_APP_TERMINATING) {
+      registrar_.RemoveAll();
+      BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, service_);
+    }
+  }
+  UsbService* service_;
+  content::NotificationRegistrar registrar_;
 };
 
-UsbService::UsbService() {
-  libusb_init(&context_);
-  event_handler_ = new UsbEventHandler(context_);
+}  // namespace
+
+UsbService::UsbService() : context_(new UsbContext()) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
 }
 
 UsbService::~UsbService() {
-  event_handler_->Stop();
-  delete event_handler_;
-  libusb_exit(context_);
-  context_ = NULL;
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  // UsbDeviceHandle::Close removes itself from devices_.
+  while (devices_.size())
+    devices_.begin()->second->Close();
 }
 
 UsbService* UsbService::GetInstance() {
-  return Singleton<UsbService>::get();
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+  // UsbService deletes itself upon APP_TERMINATING.
+  return Singleton<UsbService, LeakySingletonTraits<UsbService> >::get();
 }
 
-void UsbService::FindDevices(const uint16 vendor_id,
-                             const uint16 product_id,
-                             int interface_id,
-                             vector<scoped_refptr<UsbDeviceHandle> >* devices,
-                             const base::Callback<void()>& callback) {
-  DCHECK(event_handler_) << "FindDevices called after event handler stopped.";
+void UsbService::FindDevices(
+    const uint16 vendor_id,
+    const uint16 product_id,
+    int interface_id,
+    const base::Callback<void(ScopedDeviceVector vector)>& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
 #if defined(OS_CHROMEOS)
   // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to
   // use permission broker.
@@ -93,29 +91,33 @@
         chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
     DCHECK(client) << "Could not get permission broker client.";
     if (!client) {
-      callback.Run();
+      callback.Run(ScopedDeviceVector());
       return;
     }
 
-    client->RequestUsbAccess(vendor_id,
-                             product_id,
-                             interface_id,
-                             base::Bind(&UsbService::FindDevicesImpl,
-                                        base::Unretained(this),
-                                        vendor_id,
-                                        product_id,
-                                        devices,
-                                        callback));
+    BrowserThread::PostTask(
+        BrowserThread::UI,
+        FROM_HERE,
+        base::Bind(&chromeos::PermissionBrokerClient::RequestUsbAccess,
+                   base::Unretained(client),
+                   vendor_id,
+                   product_id,
+                   interface_id,
+                   base::Bind(&UsbService::OnRequestUsbAccessReplied,
+                              base::Unretained(this),
+                              vendor_id,
+                              product_id,
+                              callback)));
   } else {
-    FindDevicesImpl(vendor_id, product_id, devices, callback, true);
+    FindDevicesImpl(vendor_id, product_id, callback, true);
   }
 #else
-  FindDevicesImpl(vendor_id, product_id, devices, callback, true);
+  FindDevicesImpl(vendor_id, product_id, callback, true);
 #endif  // defined(OS_CHROMEOS)
 }
 
 void UsbService::EnumerateDevices(
-    std::vector<scoped_refptr<UsbDeviceHandle> >* devices) {
+    vector<scoped_refptr<UsbDeviceHandle> >* devices) {
   devices->clear();
 
   DeviceVector enumerated_devices;
@@ -130,21 +132,36 @@
   }
 }
 
+void UsbService::OnRequestUsbAccessReplied(
+    const uint16 vendor_id,
+    const uint16 product_id,
+    const base::Callback<void(ScopedDeviceVector vectors)>& callback,
+    bool success) {
+  BrowserThread::PostTask(
+      BrowserThread::FILE,
+      FROM_HERE,
+      base::Bind(&UsbService::FindDevicesImpl,
+                 base::Unretained(this),
+                 vendor_id,
+                 product_id,
+                 callback,
+                 success));
+}
+
 void UsbService::FindDevicesImpl(
     const uint16 vendor_id,
     const uint16 product_id,
-    vector<scoped_refptr<UsbDeviceHandle> >* devices,
-    const base::Callback<void()>& callback,
+    const base::Callback<void(ScopedDeviceVector vectors)>& callback,
     bool success) {
-  base::ScopedClosureRunner run_callback(callback);
-
-  devices->clear();
+  ScopedDeviceVector devices(new vector<scoped_refptr<UsbDeviceHandle> >());
 
   // If the permission broker was unable to obtain permission for the specified
   // devices then there is no point in attempting to enumerate the devices. On
   // platforms without a permission broker, we assume permission is granted.
-  if (!success)
+  if (!success) {
+    callback.Run(devices.Pass());
     return;
+  }
 
   DeviceVector enumerated_devices;
   EnumerateDevicesImpl(&enumerated_devices);
@@ -155,14 +172,13 @@
     if (DeviceMatches(device, vendor_id, product_id)) {
       UsbDeviceHandle* const wrapper = LookupOrCreateDevice(device);
       if (wrapper)
-        devices->push_back(wrapper);
+        devices->push_back(make_scoped_refptr(wrapper));
     }
   }
+  callback.Run(devices.Pass());
 }
 
 void UsbService::CloseDevice(scoped_refptr<UsbDeviceHandle> device) {
-  DCHECK(event_handler_) << "CloseDevice called after event handler stopped.";
-
   PlatformUsbDevice platform_device = libusb_get_device(device->handle());
   if (!ContainsKey(devices_, platform_device)) {
     LOG(WARNING) << "CloseDevice called for device we're not tracking!";
@@ -195,7 +211,9 @@
   STLClearObject(output);
 
   libusb_device** devices = NULL;
-  const ssize_t device_count = libusb_get_device_list(context_, &devices);
+  const ssize_t device_count = libusb_get_device_list(
+      context_->context(),
+      &devices);
   if (device_count < 0)
     return;
 
diff --git a/chrome/browser/usb/usb_service.h b/chrome/browser/usb/usb_service.h
index 5588364..dcd6e28 100644
--- a/chrome/browser/usb/usb_service.h
+++ b/chrome/browser/usb/usb_service.h
@@ -11,15 +11,16 @@
 
 #include "base/basictypes.h"
 #include "base/memory/singleton.h"
-#include "base/threading/platform_thread.h"
 #include "chrome/browser/usb/usb_device_handle.h"
-#include "third_party/libusb/src/libusb/libusb.h"
 
-class UsbEventHandler;
+namespace base {
+
+template <class T> class DeleteHelper;
+
+}  // namespace base
+
 template <typename T> struct DefaultSingletonTraits;
-struct libusb_context;
-
-typedef struct libusb_context* PlatformUsbContext;
+class UsbContext;
 
 // The USB service handles creating and managing an event handler thread that is
 // used to manage and dispatch USB events. It is also responsbile for device
@@ -27,16 +28,19 @@
 // competition for the same USB device.
 class UsbService {
  public:
+  typedef scoped_ptr<std::vector<scoped_refptr<UsbDeviceHandle> > >
+      ScopedDeviceVector;
+  // Must be called on FILE thread.
   static UsbService* GetInstance();
 
   // Find all of the devices attached to the system that are identified by
   // |vendor_id| and |product_id|, inserting them into |devices|. Clears
   // |devices| before use. Calls |callback| once |devices| is populated.
-  void FindDevices(const uint16 vendor_id,
-                   const uint16 product_id,
-                   int interface_id,
-                   std::vector<scoped_refptr<UsbDeviceHandle> >* devices,
-                   const base::Callback<void()>& callback);
+  void FindDevices(
+      const uint16 vendor_id,
+      const uint16 product_id,
+      int interface_id,
+      const base::Callback<void(ScopedDeviceVector vector)>& callback);
 
   // Find all of the devices attached to the system, inserting them into
   // |devices|. Clears |devices| before use.
@@ -46,13 +50,11 @@
   // UsbDevice's Close function and disposes of the associated platform handle.
   void CloseDevice(scoped_refptr<UsbDeviceHandle> device);
 
- protected:
+ private:
   UsbService();
   virtual ~UsbService();
-
- private:
   friend struct DefaultSingletonTraits<UsbService>;
-
+  friend class base::DeleteHelper<UsbService>;
 
   // RefCountedPlatformUsbDevice takes care of managing the underlying reference
   // count of a single PlatformUsbDevice. This allows us to construct things
@@ -77,15 +79,23 @@
                             const uint16 vendor_id,
                             const uint16 product_id);
 
+  // This method is called when permission broker replied our request.
+  // We will simply relay it to FILE thread.
+  void OnRequestUsbAccessReplied(
+      const uint16 vendor_id,
+      const uint16 product_id,
+      const base::Callback<void(ScopedDeviceVector vector)>& callback,
+      bool success);
+
   // FindDevicesImpl is called by FindDevices on ChromeOS after the permission
-  // broker has signalled that permission has been granted to access the
+  // broker has signaled that permission has been granted to access the
   // underlying device nodes. On other platforms, it is called directly by
   // FindDevices.
-  void FindDevicesImpl(const uint16 vendor_id,
-                       const uint16 product_id,
-                       std::vector<scoped_refptr<UsbDeviceHandle> >* devices,
-                       const base::Callback<void()>& callback,
-                       bool success);
+  void FindDevicesImpl(
+      const uint16 vendor_id,
+      const uint16 product_id,
+      const base::Callback<void(ScopedDeviceVector vector)>& callback,
+      bool success);
 
   // Populates |output| with the result of enumerating all attached USB devices.
   void EnumerateDevicesImpl(DeviceVector* output);
@@ -95,8 +105,7 @@
   // the wrapper with the device internally.
   UsbDeviceHandle* LookupOrCreateDevice(PlatformUsbDevice device);
 
-  PlatformUsbContext context_;
-  UsbEventHandler* event_handler_;
+  scoped_refptr<UsbContext> context_;
 
   // The devices_ map contains scoped_refptrs to all open devices, indicated by
   // their vendor and product id. This allows for reusing an open device without
diff --git a/chrome/browser/web_applications/web_app_mac.h b/chrome/browser/web_applications/web_app_mac.h
index 96cc37b..e469aed 100644
--- a/chrome/browser/web_applications/web_app_mac.h
+++ b/chrome/browser/web_applications/web_app_mac.h
@@ -11,6 +11,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/shell_integration.h"
+#include "chrome/browser/web_applications/web_app.h"
 
 #ifdef __OBJC__
 @class NSDictionary;
@@ -51,7 +52,7 @@
   // folder. E.g. ~/Applications or /Applications.
   virtual base::FilePath GetDestinationPath() const;
 
-  bool CreateShortcuts();
+  bool CreateShortcuts(ShortcutCreationReason creation_reason);
   void DeleteShortcuts();
   bool UpdateShortcuts();
 
diff --git a/chrome/browser/web_applications/web_app_mac.mm b/chrome/browser/web_applications/web_app_mac.mm
index 9f13069..aa2aa4d 100644
--- a/chrome/browser/web_applications/web_app_mac.mm
+++ b/chrome/browser/web_applications/web_app_mac.mm
@@ -7,6 +7,7 @@
 #import <Carbon/Carbon.h>
 #import <Cocoa/Cocoa.h>
 
+#include "apps/app_shim/app_shim_mac.h"
 #include "base/command_line.h"
 #include "base/file_util.h"
 #include "base/files/file_enumerator.h"
@@ -414,7 +415,8 @@
   return succeeded;
 }
 
-bool WebAppShortcutCreator::CreateShortcuts() {
+bool WebAppShortcutCreator::CreateShortcuts(
+    ShortcutCreationReason creation_reason) {
   base::FilePath dst_path = GetDestinationPath();
   if (dst_path.empty() || !base::DirectoryExists(dst_path.DirName())) {
     LOG(ERROR) << "Couldn't find an Applications directory to copy app to.";
@@ -452,7 +454,9 @@
     dock::AddIcon(internal_app_list_app_path, nil);
   }
 
-  RevealAppShimInFinder();
+  if (creation_reason == SHORTCUT_CREATION_BY_USER)
+    RevealAppShimInFinder();
+
   return true;
 }
 
@@ -691,7 +695,7 @@
 }
 
 void MaybeLaunchShortcut(const ShellIntegration::ShortcutInfo& shortcut_info) {
-  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppShims))
+  if (!apps::IsAppShimsEnabled())
     return;
 
   content::BrowserThread::PostTask(
@@ -705,11 +709,11 @@
     const base::FilePath& app_data_path,
     const ShellIntegration::ShortcutInfo& shortcut_info,
     const ShellIntegration::ShortcutLocations& creation_locations,
-    ShortcutCreationReason /*creation_reason*/) {
+    ShortcutCreationReason creation_reason) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
   WebAppShortcutCreator shortcut_creator(
       app_data_path, shortcut_info, base::mac::BaseBundleID());
-  return shortcut_creator.CreateShortcuts();
+  return shortcut_creator.CreateShortcuts(creation_reason);
 }
 
 void DeletePlatformShortcuts(
diff --git a/chrome/browser/web_applications/web_app_mac_unittest.mm b/chrome/browser/web_applications/web_app_mac_unittest.mm
index 1fddb43..817de70 100644
--- a/chrome/browser/web_applications/web_app_mac_unittest.mm
+++ b/chrome/browser/web_applications/web_app_mac_unittest.mm
@@ -101,9 +101,8 @@
   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(app_data_path_, info_);
   EXPECT_CALL(shortcut_creator, GetDestinationPath())
       .WillRepeatedly(Return(destination_path_));
-  EXPECT_CALL(shortcut_creator, RevealAppShimInFinder());
 
-  EXPECT_TRUE(shortcut_creator.CreateShortcuts());
+  EXPECT_TRUE(shortcut_creator.CreateShortcuts(SHORTCUT_CREATION_AUTOMATED));
   EXPECT_TRUE(base::PathExists(shim_path_));
   EXPECT_TRUE(base::PathExists(destination_path_));
   EXPECT_EQ(shim_base_name_, shortcut_creator.GetShortcutName());
@@ -180,14 +179,13 @@
   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(app_data_path_, info_);
   EXPECT_CALL(shortcut_creator, GetDestinationPath())
       .WillRepeatedly(Return(destination_path_));
-  EXPECT_CALL(shortcut_creator, RevealAppShimInFinder());
 
   std::string expected_bundle_id = kFakeChromeBundleId;
   expected_bundle_id += ".app.Profile-1-" + info_.extension_id;
   EXPECT_CALL(shortcut_creator, GetAppBundleById(expected_bundle_id))
       .WillOnce(Return(other_shim_path));
 
-  EXPECT_TRUE(shortcut_creator.CreateShortcuts());
+  EXPECT_TRUE(shortcut_creator.CreateShortcuts(SHORTCUT_CREATION_AUTOMATED));
   EXPECT_TRUE(base::PathExists(internal_shim_path_));
   EXPECT_TRUE(base::PathExists(shim_path_));
 
@@ -231,9 +229,8 @@
   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(app_data_path_, info_);
   EXPECT_CALL(shortcut_creator, GetDestinationPath())
       .WillRepeatedly(Return(destination_path_));
-  EXPECT_CALL(shortcut_creator, RevealAppShimInFinder());
 
-  EXPECT_TRUE(shortcut_creator.CreateShortcuts());
+  EXPECT_TRUE(shortcut_creator.CreateShortcuts(SHORTCUT_CREATION_AUTOMATED));
   EXPECT_TRUE(base::PathExists(shim_path_));
 
   ssize_t status = getxattr(
@@ -249,7 +246,7 @@
   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(app_data_path_, info_);
   EXPECT_CALL(shortcut_creator, GetDestinationPath())
       .WillRepeatedly(Return(non_existent_path));
-  EXPECT_FALSE(shortcut_creator.CreateShortcuts());
+  EXPECT_FALSE(shortcut_creator.CreateShortcuts(SHORTCUT_CREATION_AUTOMATED));
 }
 
 TEST_F(WebAppShortcutCreatorTest, UpdateIcon) {
@@ -270,4 +267,17 @@
   EXPECT_EQ(product_logo.Height(), [image size].height);
 }
 
+TEST_F(WebAppShortcutCreatorTest, RevealAppShimInFinder) {
+  WebAppShortcutCreatorMock shortcut_creator(app_data_path_, info_);
+  EXPECT_CALL(shortcut_creator, GetDestinationPath())
+      .WillRepeatedly(Return(destination_path_));
+
+  EXPECT_CALL(shortcut_creator, RevealAppShimInFinder())
+      .Times(0);
+  EXPECT_TRUE(shortcut_creator.CreateShortcuts(SHORTCUT_CREATION_AUTOMATED));
+
+  EXPECT_CALL(shortcut_creator, RevealAppShimInFinder());
+  EXPECT_TRUE(shortcut_creator.CreateShortcuts(SHORTCUT_CREATION_BY_USER));
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/webdata/keyword_table.cc b/chrome/browser/webdata/keyword_table.cc
index 640985e..b6816d1 100644
--- a/chrome/browser/webdata/keyword_table.cc
+++ b/chrome/browser/webdata/keyword_table.cc
@@ -68,6 +68,14 @@
     // Column added in version 49.
     columns.push_back("search_terms_replacement_key");
   }
+  if (version >= 52) {
+    // Column added in version 52.
+    columns.push_back("image_url");
+    columns.push_back("search_url_post_params");
+    columns.push_back("suggest_url_post_params");
+    columns.push_back("instant_url_post_params");
+    columns.push_back("image_url_post_params");
+  }
 
   return JoinString(columns, std::string(concatenated ? " || " : ", "));
 }
@@ -114,6 +122,11 @@
   s->BindString(starting_column + 15, data.sync_guid);
   s->BindString(starting_column + 16, alternate_urls);
   s->BindString(starting_column + 17, data.search_terms_replacement_key);
+  s->BindString(starting_column + 18, data.image_url);
+  s->BindString(starting_column + 19, data.search_url_post_params);
+  s->BindString(starting_column + 20, data.suggestions_url_post_params);
+  s->BindString(starting_column + 21, data.instant_url_post_params);
+  s->BindString(starting_column + 22, data.image_url_post_params);
 }
 
 WebDatabaseTable::TypeKey GetKey() {
@@ -160,7 +173,12 @@
                    "last_modified INTEGER DEFAULT 0,"
                    "sync_guid VARCHAR,"
                    "alternate_urls VARCHAR,"
-                   "search_terms_replacement_key VARCHAR)");
+                   "search_terms_replacement_key VARCHAR,"
+                   "image_url VARCHAR,"
+                   "search_url_post_params VARCHAR,"
+                   "suggest_url_post_params VARCHAR,"
+                   "instant_url_post_params VARCHAR,"
+                   "image_url_post_params VARCHAR)");
 }
 
 bool KeywordTable::IsSyncable() {
@@ -207,6 +225,9 @@
     case 49:
       *update_compatible_version = true;
       return MigrateToVersion49AddSearchTermsReplacementKeyColumn();
+    case 52:
+      *update_compatible_version = true;
+      return MigrateToVersion52AddImageSearchAndPOSTSupport();
   }
 
   return true;
@@ -214,8 +235,8 @@
 
 bool KeywordTable::AddKeyword(const TemplateURLData& data) {
   DCHECK(data.id);
-  std::string query("INSERT INTO keywords (" + GetKeywordColumns() +
-                    ") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
+  std::string query("INSERT INTO keywords (" + GetKeywordColumns() + ") "
+                    "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
   sql::Statement s(db_->GetUniqueStatement(query.c_str()));
   BindURLToStatement(data, &s, 0, 1);
 
@@ -258,8 +279,10 @@
       "originating_url=?, date_created=?, usage_count=?, input_encodings=?, "
       "show_in_default_list=?, suggest_url=?, prepopulate_id=?, "
       "created_by_policy=?, instant_url=?, last_modified=?, sync_guid=?, "
-      "alternate_urls=?, search_terms_replacement_key=? WHERE id=?"));
-  BindURLToStatement(data, &s, 18, 0);  // "18" binds id() as the last item.
+      "alternate_urls=?, search_terms_replacement_key=?, image_url=?,"
+      "search_url_post_params=?, suggest_url_post_params=?, "
+      "instant_url_post_params=?, image_url_post_params=? WHERE id=?"));
+  BindURLToStatement(data, &s, 23, 0);  // "23" binds id() as the last item.
 
   return s.Run();
 }
@@ -444,6 +467,24 @@
   return transaction.Commit();
 }
 
+bool KeywordTable::MigrateToVersion52AddImageSearchAndPOSTSupport() {
+  sql::Transaction transaction(db_);
+
+  // Fill the |image_url| and other four post params columns with empty strings;
+  return transaction.Begin() &&
+      db_->Execute("ALTER TABLE keywords ADD COLUMN image_url "
+                    "VARCHAR DEFAULT ''") &&
+      db_->Execute("ALTER TABLE keywords ADD COLUMN search_url_post_params "
+                    "VARCHAR DEFAULT ''") &&
+      db_->Execute("ALTER TABLE keywords ADD COLUMN suggest_url_post_params "
+                    "VARCHAR DEFAULT ''") &&
+      db_->Execute("ALTER TABLE keywords ADD COLUMN instant_url_post_params "
+                    "VARCHAR DEFAULT ''") &&
+      db_->Execute("ALTER TABLE keywords ADD COLUMN image_url_post_params "
+                    "VARCHAR DEFAULT ''") &&
+      transaction.Commit();
+}
+
 // static
 bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s,
                                                TemplateURLData* data) {
@@ -460,6 +501,11 @@
   data->SetURL(s.ColumnString(4));
   data->suggestions_url = s.ColumnString(11);
   data->instant_url = s.ColumnString(14);
+  data->image_url = s.ColumnString(19);
+  data->search_url_post_params = s.ColumnString(20);
+  data->suggestions_url_post_params = s.ColumnString(21);
+  data->instant_url_post_params = s.ColumnString(22);
+  data->image_url_post_params = s.ColumnString(23);
   data->favicon_url = GURL(s.ColumnString(3));
   data->originating_url = GURL(s.ColumnString(6));
   data->show_in_default_list = s.ColumnBool(10);
diff --git a/chrome/browser/webdata/keyword_table.h b/chrome/browser/webdata/keyword_table.h
index 0d6737c..7634846 100644
--- a/chrome/browser/webdata/keyword_table.h
+++ b/chrome/browser/webdata/keyword_table.h
@@ -57,6 +57,16 @@
 //   search_terms_replacement_key
 //                          See TemplateURLData::search_terms_replacement_key.
 //                          This was added in version 49.
+//   image_url              See TemplateURLData::image_url. This was added in
+//                          version 52.
+//   search_url_post_params See TemplateURLData::search_url_post_params. This
+//                          was added in version 52.
+//   suggest_url_post_params See TemplateURLData::suggestions_url_post_params.
+//                          This was added in version 52.
+//   instant_url_post_params See TemplateURLData::instant_url_post_params. This
+//                          was added in version 52.
+//   image_url_post_params  See TemplateURLData::image_url_post_params. This
+//                          was added in version 52.
 //
 //
 // This class also manages some fields in the |meta| table:
@@ -126,6 +136,7 @@
   bool MigrateToVersion47AddAlternateURLsColumn();
   bool MigrateToVersion48RemoveKeywordsBackup();
   bool MigrateToVersion49AddSearchTermsReplacementKeyColumn();
+  bool MigrateToVersion52AddImageSearchAndPOSTSupport();
 
  private:
   FRIEND_TEST_ALL_PREFIXES(KeywordTableTest, GetTableContents);
diff --git a/chrome/browser/webdata/keyword_table_unittest.cc b/chrome/browser/webdata/keyword_table_unittest.cc
index 7f15aa1..760166e 100644
--- a/chrome/browser/webdata/keyword_table_unittest.cc
+++ b/chrome/browser/webdata/keyword_table_unittest.cc
@@ -136,6 +136,7 @@
   keyword.SetKeyword(ASCIIToUTF16("keyword"));
   keyword.SetURL("http://url/");
   keyword.suggestions_url = "url2";
+  keyword.image_url = "http://image-search-url/";
   keyword.favicon_url = GURL("http://favicon.url/");
   keyword.show_in_default_list = true;
   keyword.safe_for_autoreplace = true;
@@ -150,6 +151,7 @@
 
   keyword.SetKeyword(ASCIIToUTF16("url"));
   keyword.instant_url = "http://instant2/";
+  keyword.image_url.clear();
   keyword.originating_url = GURL("http://originating.url/");
   keyword.input_encodings.push_back("Shift_JIS");
   keyword.id = 2;
@@ -161,8 +163,9 @@
 
   const char kTestContents[] = "1short_namekeywordhttp://favicon.url/"
       "http://url/1001url20001234-5678-90AB-CDEF[\"a_url1\",\"a_url2\"]espv"
-      "2short_nameurlhttp://favicon.url/http://url/1http://originating.url/00"
-      "Shift_JIS1url250http://instant2/0FEDC-BA09-8765-4321[]";
+      "http://image-search-url/2short_nameurlhttp://favicon.url/http://url/1"
+      "http://originating.url/00Shift_JIS1url250http://instant2/0"
+      "FEDC-BA09-8765-4321[]";
 
   std::string contents;
   EXPECT_TRUE(table_->GetTableContents("keywords",
@@ -186,6 +189,9 @@
   keyword.alternate_urls.push_back("a_url1");
   keyword.alternate_urls.push_back("a_url2");
   keyword.search_terms_replacement_key = "espv";
+  keyword.image_url = "http://image-search-url/";
+  keyword.search_url_post_params = "ie=utf-8,oe=utf-8";
+  keyword.image_url_post_params = "name=1,value=2";
   EXPECT_TRUE(table_->AddKeyword(keyword));
 
   keyword.SetKeyword(ASCIIToUTF16("url"));
@@ -197,13 +203,16 @@
   keyword.sync_guid = "FEDC-BA09-8765-4321";
   keyword.alternate_urls.clear();
   keyword.search_terms_replacement_key.clear();
+  keyword.image_url.clear();
+  keyword.search_url_post_params.clear();
+  keyword.image_url_post_params.clear();
   EXPECT_TRUE(table_->AddKeyword(keyword));
 
   const char kTestContents[] = "1short_nameurlhttp://favicon.url/http://url/1"
       "http://originating.url/00Shift_JIS1url250http://instant2/0"
-      "FEDC-BA09-8765-4321[]"
-      "2short_namekeywordhttp://favicon.url/http://url/1001"
-      "url20001234-5678-90AB-CDEF[\"a_url1\",\"a_url2\"]espv";
+      "FEDC-BA09-8765-4321[]2short_namekeywordhttp://favicon.url/"
+      "http://url/1001url20001234-5678-90AB-CDEF[\"a_url1\",\"a_url2\"]espv"
+      "http://image-search-url/ie=utf-8,oe=utf-8name=1,value=2";
 
   std::string contents;
   EXPECT_TRUE(table_->GetTableContents("keywords",
diff --git a/chrome/browser/webdata/token_service_table.cc b/chrome/browser/webdata/token_service_table.cc
index deb6895..0915f3b 100644
--- a/chrome/browser/webdata/token_service_table.cc
+++ b/chrome/browser/webdata/token_service_table.cc
@@ -61,6 +61,14 @@
   return s.Run();
 }
 
+bool TokenServiceTable::RemoveTokenForService(const std::string& service) {
+  sql::Statement s(db_->GetUniqueStatement(
+      "DELETE FROM token_service WHERE service = ?"));
+  s.BindString(0, service);
+
+  return s.Run();
+}
+
 bool TokenServiceTable::SetTokenForService(const std::string& service,
                                            const std::string& token) {
   std::string encrypted_token;
diff --git a/chrome/browser/webdata/token_service_table.h b/chrome/browser/webdata/token_service_table.h
index 3a07f19..d26f08a 100644
--- a/chrome/browser/webdata/token_service_table.h
+++ b/chrome/browser/webdata/token_service_table.h
@@ -30,6 +30,9 @@
   // Remove all tokens previously set with SetTokenForService.
   bool RemoveAllTokens();
 
+  // Removes a token related to the service from the token_service table.
+  bool RemoveTokenForService(const std::string& service);
+
   // Retrieves all tokens previously set with SetTokenForService.
   // Returns true if there were tokens and we decrypted them,
   // false if there was a failure somehow
diff --git a/chrome/browser/webdata/token_service_table_unittest.cc b/chrome/browser/webdata/token_service_table_unittest.cc
index d291a5a..0f1398d 100644
--- a/chrome/browser/webdata/token_service_table_unittest.cc
+++ b/chrome/browser/webdata/token_service_table_unittest.cc
@@ -40,9 +40,11 @@
 #if defined(OS_MACOSX)
 #define MAYBE_TokenServiceGetAllRemoveAll DISABLED_TokenServiceGetAllRemoveAll
 #define MAYBE_TokenServiceGetSet DISABLED_TokenServiceGetSet
+#define MAYBE_TokenServiceRemove DISABLED_TokenServiceRemove
 #else
 #define MAYBE_TokenServiceGetAllRemoveAll TokenServiceGetAllRemoveAll
 #define MAYBE_TokenServiceGetSet TokenServiceGetSet
+#define MAYBE_TokenServiceRemove TokenServiceRemove
 #endif
 
 TEST_F(TokenServiceTableTest, MAYBE_TokenServiceGetAllRemoveAll) {
@@ -59,8 +61,8 @@
   EXPECT_TRUE(table_->SetTokenForService(service, "pepperoni"));
   EXPECT_TRUE(table_->SetTokenForService(service2, "steak"));
   EXPECT_TRUE(table_->GetAllTokens(&out_map));
-  EXPECT_EQ(out_map.find(service)->second, "pepperoni");
-  EXPECT_EQ(out_map.find(service2)->second, "steak");
+  EXPECT_EQ("pepperoni", out_map.find(service)->second);
+  EXPECT_EQ("steak", out_map.find(service2)->second);
   out_map.clear();
 
   // Purge
@@ -71,7 +73,7 @@
   // Check that you can still add it back in
   EXPECT_TRUE(table_->SetTokenForService(service, "cheese"));
   EXPECT_TRUE(table_->GetAllTokens(&out_map));
-  EXPECT_EQ(out_map.find(service)->second, "cheese");
+  EXPECT_EQ("cheese", out_map.find(service)->second);
 }
 
 TEST_F(TokenServiceTableTest, MAYBE_TokenServiceGetSet) {
@@ -84,17 +86,32 @@
 
   EXPECT_TRUE(table_->SetTokenForService(service, "pepperoni"));
   EXPECT_TRUE(table_->GetAllTokens(&out_map));
-  EXPECT_EQ(out_map.find(service)->second, "pepperoni");
+  EXPECT_EQ("pepperoni", out_map.find(service)->second);
   out_map.clear();
 
   // try blanking it - won't remove it from the db though!
   EXPECT_TRUE(table_->SetTokenForService(service, std::string()));
   EXPECT_TRUE(table_->GetAllTokens(&out_map));
-  EXPECT_EQ(out_map.find(service)->second, "");
+  EXPECT_EQ("", out_map.find(service)->second);
   out_map.clear();
 
   // try mutating it
   EXPECT_TRUE(table_->SetTokenForService(service, "ham"));
   EXPECT_TRUE(table_->GetAllTokens(&out_map));
-  EXPECT_EQ(out_map.find(service)->second, "ham");
+  EXPECT_EQ("ham", out_map.find(service)->second);
+}
+
+TEST_F(TokenServiceTableTest, MAYBE_TokenServiceRemove) {
+  std::map<std::string, std::string> out_map;
+  std::string service;
+  std::string service2;
+  service = "testservice";
+  service2 = "othertestservice";
+
+  EXPECT_TRUE(table_->SetTokenForService(service, "pepperoni"));
+  EXPECT_TRUE(table_->SetTokenForService(service2, "steak"));
+  EXPECT_TRUE(table_->RemoveTokenForService(service));
+  EXPECT_TRUE(table_->GetAllTokens(&out_map));
+  EXPECT_EQ(0u, out_map.count(service));
+  EXPECT_EQ("steak", out_map.find(service2)->second);
 }
diff --git a/chrome/browser/webdata/token_web_data.cc b/chrome/browser/webdata/token_web_data.cc
index e659b67..9c28d62 100644
--- a/chrome/browser/webdata/token_web_data.cc
+++ b/chrome/browser/webdata/token_web_data.cc
@@ -29,6 +29,15 @@
     return WebDatabase::COMMIT_NOT_NEEDED;
   }
 
+  WebDatabase::State RemoveTokenForService(
+      const std::string& service, WebDatabase* db) {
+    if (TokenServiceTable::FromWebDatabase(db)
+            ->RemoveTokenForService(service)) {
+      return WebDatabase::COMMIT_NEEDED;
+    }
+    return WebDatabase::COMMIT_NOT_NEEDED;
+  }
+
   WebDatabase::State SetTokenForService(
       const std::string& service, const std::string& token, WebDatabase* db) {
     if (TokenServiceTable::FromWebDatabase(db)->SetTokenForService(service,
@@ -78,6 +87,12 @@
       Bind(&TokenWebDataBackend::RemoveAllTokens, token_backend_));
 }
 
+void TokenWebData::RemoveTokenForService(const std::string& service) {
+  wdbs_->ScheduleDBTask(FROM_HERE,
+      Bind(&TokenWebDataBackend::RemoveTokenForService, token_backend_,
+           service));
+}
+
 // Null on failure. Success is WDResult<std::string>
 WebDataServiceBase::Handle TokenWebData::GetAllTokens(
     WebDataServiceConsumer* consumer) {
diff --git a/chrome/browser/webdata/token_web_data.h b/chrome/browser/webdata/token_web_data.h
index f74f230..0314782 100644
--- a/chrome/browser/webdata/token_web_data.h
+++ b/chrome/browser/webdata/token_web_data.h
@@ -48,6 +48,9 @@
   // Remove all tokens stored in the web database.
   void RemoveAllTokens();
 
+  // Removes a token related to |service| from the web database.
+  void RemoveTokenForService(const std::string& service);
+
   // Null on failure. Success is WDResult<std::vector<std::string> >
   virtual Handle GetAllTokens(WebDataServiceConsumer* consumer);
 
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 42922e9..9b03294 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -183,6 +183,8 @@
             'browser/devtools/adb/android_usb_socket.h',
             'browser/devtools/adb_client_socket.cc',
             'browser/devtools/adb_client_socket.h',
+            'browser/devtools/adb_web_socket.cc',
+            'browser/devtools/adb_web_socket.h',
             'browser/devtools/browser_list_tabcontents_provider.cc',
             'browser/devtools/browser_list_tabcontents_provider.h',
             'browser/devtools/devtools_adb_bridge.cc',
@@ -285,8 +287,8 @@
             'utility/importer/nss_decryptor_win.h',
             'utility/importer/safari_importer.h',
             'utility/importer/safari_importer.mm',
-            'utility/itunes_pref_parser_win.cc',
-            'utility/itunes_pref_parser_win.h',
+            'utility/media_galleries/itunes_pref_parser_win.cc',
+            'utility/media_galleries/itunes_pref_parser_win.h',
             'utility/profile_import_handler.cc',
             'utility/profile_import_handler.h',
             'utility/utility_message_handler.h',
@@ -305,8 +307,8 @@
             }],
             ['OS=="win" or OS=="mac"', {
               'sources': [
-                'utility/itunes_library_parser.cc',
-                'utility/itunes_library_parser.h',
+                'utility/media_galleries/itunes_library_parser.cc',
+                'utility/media_galleries/itunes_library_parser.h',
                 'utility/media_galleries/picasa_album_table_reader.cc',
                 'utility/media_galleries/picasa_album_table_reader.h',
                 'utility/media_galleries/picasa_albums_indexer.cc',
diff --git a/chrome/chrome.user32.delay.imports b/chrome/chrome.user32.delay.imports
index baa1231..ee35cf0 100644
--- a/chrome/chrome.user32.delay.imports
+++ b/chrome/chrome.user32.delay.imports
@@ -23,7 +23,6 @@
     'GetTouchInputInfo@16',
     'IsTouchWindow@8',
     'RegisterTouchWindow@8',
-    'SetGestureConfig@20',
     'UnregisterTouchWindow@4',
   ],
 }
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 0d2d58c..88e2515 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -101,6 +101,8 @@
         'browser/android/favicon_helper.h',
         'browser/android/field_trial_helper.cc',
         'browser/android/field_trial_helper.h',
+        'browser/android/foreign_session_helper.cc',
+        'browser/android/foreign_session_helper.h',
         'browser/android/google_location_settings_helper.h',
         'browser/android/intent_helper.cc',
         'browser/android/intent_helper.h',
@@ -108,6 +110,8 @@
         'browser/android/intercept_download_resource_throttle.h',
         'browser/android/most_visited_sites.cc',
         'browser/android/most_visited_sites.h',
+        'browser/android/omnibox/omnibox_prerender.cc',
+        'browser/android/omnibox/omnibox_prerender.h',
         'browser/android/provider/blocking_ui_thread_async_request.cc',
         'browser/android/provider/blocking_ui_thread_async_request.h',
         'browser/android/provider/bookmark_model_observer_task.cc',
@@ -926,6 +930,10 @@
         'browser/mac/relauncher.h',
         'browser/mac/security_wrappers.cc',
         'browser/mac/security_wrappers.h',
+        'browser/managed_mode/custodian_profile_downloader_service.cc',
+        'browser/managed_mode/custodian_profile_downloader_service.h',
+        'browser/managed_mode/custodian_profile_downloader_service_factory.cc',
+        'browser/managed_mode/custodian_profile_downloader_service_factory.h',
         'browser/managed_mode/managed_mode_interstitial.cc',
         'browser/managed_mode/managed_mode_interstitial.h',
         'browser/managed_mode/managed_mode_navigation_observer.cc',
@@ -936,20 +944,30 @@
         'browser/managed_mode/managed_mode_site_list.h',
         'browser/managed_mode/managed_mode_url_filter.cc',
         'browser/managed_mode/managed_mode_url_filter.h',
-        'browser/managed_mode/managed_user_registration_service.cc',
-        'browser/managed_mode/managed_user_registration_service.h',
-        'browser/managed_mode/managed_user_registration_service_factory.cc',
-        'browser/managed_mode/managed_user_registration_service_factory.h',
+        'browser/managed_mode/managed_user_refresh_token_fetcher.cc',
+        'browser/managed_mode/managed_user_refresh_token_fetcher.h',
+        'browser/managed_mode/managed_user_registration_utility.cc',
+        'browser/managed_mode/managed_user_registration_utility.h',
         'browser/managed_mode/managed_user_service.cc',
         'browser/managed_mode/managed_user_service.h',
         'browser/managed_mode/managed_user_service_factory.cc',
         'browser/managed_mode/managed_user_service_factory.h',
-        'browser/managed_mode/managed_user_refresh_token_fetcher.cc',
-        'browser/managed_mode/managed_user_refresh_token_fetcher.h',
+        'browser/managed_mode/managed_user_sync_service.cc',
+        'browser/managed_mode/managed_user_sync_service.h',
+        'browser/managed_mode/managed_user_sync_service_factory.cc',
+        'browser/managed_mode/managed_user_sync_service_factory.h',
+        'browser/managed_mode/managed_user_sync_service_observer.h',
         'browser/managed_mode/managed_user_theme.cc',
         'browser/managed_mode/managed_user_theme.h',
         'browser/media/audio_stream_indicator.cc',
         'browser/media/audio_stream_indicator.h',
+        'browser/media/chrome_midi_permission_context.cc',
+        'browser/media/chrome_midi_permission_context.h',
+        'browser/media/chrome_midi_permission_context_factory.cc',
+        'browser/media/chrome_midi_permission_context_factory.h',
+        'browser/media/desktop_media_picker.h',
+        'browser/media/desktop_media_picker_model.cc',
+        'browser/media/desktop_media_picker_model.h',
         'browser/media/media_capture_devices_dispatcher.cc',
         'browser/media/media_capture_devices_dispatcher.h',
         'browser/media/media_stream_capture_indicator.cc',
@@ -958,6 +976,8 @@
         'browser/media/media_stream_devices_controller.h',
         'browser/media/media_stream_infobar_delegate.cc',
         'browser/media/media_stream_infobar_delegate.h',
+        'browser/media/midi_permission_infobar_delegate.cc',
+        'browser/media/midi_permission_infobar_delegate.h',
         'browser/media/webrtc_log_upload_list.cc',
         'browser/media/webrtc_log_upload_list.h',
         'browser/media/webrtc_log_uploader.cc',
@@ -1328,6 +1348,8 @@
         'browser/policy/cloud/cloud_policy_constants.h',
         'browser/policy/cloud/cloud_policy_core.cc',
         'browser/policy/cloud/cloud_policy_core.h',
+        'browser/policy/cloud/cloud_policy_invalidator.cc',
+        'browser/policy/cloud/cloud_policy_invalidator.h',
         'browser/policy/cloud/cloud_policy_manager.cc',
         'browser/policy/cloud/cloud_policy_manager.h',
         'browser/policy/cloud/cloud_policy_refresh_scheduler.cc',
@@ -1356,6 +1378,10 @@
         'browser/policy/cloud/rate_limiter.h',
         'browser/policy/cloud/resource_cache.cc',
         'browser/policy/cloud/resource_cache.h',
+        'browser/policy/cloud/user_cloud_policy_invalidator_factory.cc',
+        'browser/policy/cloud/user_cloud_policy_invalidator_factory.h',
+        'browser/policy/cloud/user_cloud_policy_invalidator.cc',
+        'browser/policy/cloud/user_cloud_policy_invalidator.h',
         'browser/policy/cloud/user_cloud_policy_manager.cc',
         'browser/policy/cloud/user_cloud_policy_manager.h',
         'browser/policy/cloud/user_cloud_policy_manager_factory.cc',
@@ -1423,6 +1449,8 @@
         'browser/policy/profile_policy_connector.h',
         'browser/policy/profile_policy_connector_factory.cc',
         'browser/policy/profile_policy_connector_factory.h',
+        'browser/policy/registry_dict_win.cc',
+        'browser/policy/registry_dict_win.h',
         'browser/policy/url_blacklist_manager.cc',
         'browser/policy/url_blacklist_manager.h',
         'browser/predictors/autocomplete_action_predictor.cc',
@@ -1858,6 +1886,8 @@
         'browser/signin/signin_names_io_thread.h',
         'browser/signin/signin_tracker.cc',
         'browser/signin/signin_tracker.h',
+        'browser/signin/signin_promo.cc',
+        'browser/signin/signin_promo.h',
         'browser/signin/signin_ui_util.cc',
         'browser/signin/signin_ui_util.h',
         'browser/signin/token_service.cc',
@@ -1969,9 +1999,6 @@
         'browser/storage_monitor/storage_monitor_win.h',
         'browser/storage_monitor/storage_monitor.cc',
         'browser/storage_monitor/storage_monitor.h',
-        # TODO(thestig) Refactor StorageMonitor so these are test-only.
-        'browser/storage_monitor/test_media_transfer_protocol_manager_linux.cc',
-        'browser/storage_monitor/test_media_transfer_protocol_manager_linux.h',
         'browser/storage_monitor/transient_device_ids.cc',
         'browser/storage_monitor/transient_device_ids.h',
         'browser/storage_monitor/udev_util_linux.cc',
@@ -2002,8 +2029,6 @@
         'browser/sync/glue/change_processor.h',
         'browser/sync/glue/chrome_encryptor.cc',
         'browser/sync/glue/chrome_encryptor.h',
-        'browser/sync/glue/chrome_extensions_activity_monitor.cc',
-        'browser/sync/glue/chrome_extensions_activity_monitor.h',
         'browser/sync/glue/chrome_report_unrecoverable_error.cc',
         'browser/sync/glue/chrome_report_unrecoverable_error.h',
         'browser/sync/glue/data_type_controller.cc',
@@ -2022,6 +2047,8 @@
         'browser/sync/glue/extension_data_type_controller.h',
         'browser/sync/glue/extension_setting_data_type_controller.cc',
         'browser/sync/glue/extension_setting_data_type_controller.h',
+        'browser/sync/glue/extensions_activity_monitor.cc',
+        'browser/sync/glue/extensions_activity_monitor.h',
         'browser/sync/glue/failed_data_types_handler.cc',
         'browser/sync/glue/failed_data_types_handler.h',
         'browser/sync/glue/favicon_cache.cc',
@@ -2280,6 +2307,8 @@
         'browser/translate/translate_url_util.h',
         'browser/undo/undo_manager.cc',
         'browser/undo/undo_manager.h',
+        'browser/undo/undo_manager_utils.cc',
+        'browser/undo/undo_manager_utils.h',
         'browser/undo/undo_operation.h',
         'browser/upgrade_detector.cc',
         'browser/upgrade_detector.h',
@@ -2291,6 +2320,8 @@
         'browser/usb/usb_device_handle.h',
         'browser/usb/usb_interface.cc',
         'browser/usb/usb_interface.h',
+        'browser/usb/usb_context.cc',
+        'browser/usb/usb_context.h',
         'browser/usb/usb_service.cc',
         'browser/usb/usb_service.h',
         'browser/user_data_dir_extractor.cc',
@@ -2392,8 +2423,10 @@
             '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
             '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber',
             '../third_party/libusb/libusb.gyp:libusb',
+            '../third_party/libyuv/libyuv.gyp:libyuv',
             '../third_party/npapi/npapi.gyp:npapi',
             '../third_party/re2/re2.gyp:re2',
+            '../third_party/webrtc/modules/modules.gyp:desktop_capture',
             '../ui/gl/gl.gyp:gl',
             '../ui/surface/surface.gyp:surface',
             '../ui/web_dialogs/web_dialogs.gyp:web_dialogs',
@@ -2497,6 +2530,14 @@
             ['exclude', '^browser/task_manager/'],
           ],
         }],
+        ['enable_spellcheck==0', {
+          'sources/': [
+             ['exclude', '^browser/spellchecker/'],
+          ],
+          'dependencies!': [
+            '../third_party/hunspell/hunspell.gyp:hunspell',
+          ],
+        }],
         ['disable_nacl==0', {
           'sources': [
             'browser/nacl_host/nacl_broker_host_win.cc',
@@ -3316,9 +3357,11 @@
             'android/java/src/org/chromium/chrome/browser/database/SQLiteCursor.java',
             'android/java/src/org/chromium/chrome/browser/favicon/FaviconHelper.java',
             'android/java/src/org/chromium/chrome/browser/FieldTrialHelper.java',
+            'android/java/src/org/chromium/chrome/browser/ForeignSessionHelper.java',
             'android/java/src/org/chromium/chrome/browser/IntentHelper.java',
             'android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java',
             'android/java/src/org/chromium/chrome/browser/NavigationPopup.java',
+            'android/java/src/org/chromium/chrome/browser/omnibox/OmniboxPrerender.java',
             'android/java/src/org/chromium/chrome/browser/profiles/MostVisitedSites.java',
             'android/java/src/org/chromium/chrome/browser/profiles/Profile.java',
             'android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java',
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index 1c9378b..6763f83 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -143,15 +143,6 @@
         'browser/chromeos/attestation/attestation_policy_observer.h',
         'browser/chromeos/audio/audio_devices_pref_handler_impl.cc',
         'browser/chromeos/audio/audio_devices_pref_handler_impl.h',
-        'browser/chromeos/audio/audio_handler.cc',
-        'browser/chromeos/audio/audio_handler.h',
-        'browser/chromeos/audio/audio_pref_handler_impl.cc',
-        'browser/chromeos/audio/audio_pref_handler_impl.h',
-        'browser/chromeos/audio/audio_mixer.h',
-        'browser/chromeos/audio/audio_mixer_alsa.cc',
-        'browser/chromeos/audio/audio_mixer_alsa.h',
-        'browser/chromeos/audio/audio_mixer_cras.cc',
-        'browser/chromeos/audio/audio_mixer_cras.h',
         'browser/chromeos/background/ash_user_wallpaper_delegate.cc',
         'browser/chromeos/background/ash_user_wallpaper_delegate.h',
         'browser/chromeos/bluetooth/bluetooth_pairing_dialog.cc',
@@ -324,14 +315,14 @@
         'browser/chromeos/extensions/external_cache.h',
         'browser/chromeos/extensions/external_pref_cache_loader.cc',
         'browser/chromeos/extensions/external_pref_cache_loader.h',
-        'browser/chromeos/extensions/file_manager/file_handler_util.cc',
-        'browser/chromeos/extensions/file_manager/file_handler_util.h',
         'browser/chromeos/extensions/file_manager/file_manager_event_router.cc',
         'browser/chromeos/extensions/file_manager/file_manager_event_router.h',
         'browser/chromeos/extensions/file_manager/file_manager_notifications.cc',
         'browser/chromeos/extensions/file_manager/file_manager_notifications.h',
         'browser/chromeos/extensions/file_manager/file_manager_util.cc',
         'browser/chromeos/extensions/file_manager/file_manager_util.h',
+        'browser/chromeos/extensions/file_manager/file_tasks.cc',
+        'browser/chromeos/extensions/file_manager/file_tasks.h',
         'browser/chromeos/extensions/file_manager/file_watcher_extensions.cc',
         'browser/chromeos/extensions/file_manager/file_watcher_extensions.h',
         'browser/chromeos/extensions/file_manager/mounted_disk_monitor.cc',
@@ -509,6 +500,7 @@
         'browser/chromeos/login/screen_locker_delegate.h',
         'browser/chromeos/login/screens/base_screen.cc',
         'browser/chromeos/login/screens/base_screen.h',
+        'browser/chromeos/login/screens/core_oobe_actor.h',
         'browser/chromeos/login/screens/error_screen.cc',
         'browser/chromeos/login/screens/error_screen.h',
         'browser/chromeos/login/screens/error_screen_actor.cc',
@@ -725,6 +717,8 @@
         'browser/chromeos/prerender_condition_network.h',
         'browser/chromeos/profiles/profile_helper.cc',
         'browser/chromeos/profiles/profile_helper.h',
+        'browser/chromeos/profiles/profile_util.cc',
+        'browser/chromeos/profiles/profile_util.h',
         'browser/chromeos/proxy_config_service_impl.cc',
         'browser/chromeos/proxy_config_service_impl.h',
         'browser/chromeos/proxy_cros_settings_parser.cc',
@@ -767,8 +761,6 @@
         'browser/chromeos/status/data_promo_notification.h',
         'browser/chromeos/status/network_menu.cc',
         'browser/chromeos/status/network_menu.h',
-        'browser/chromeos/status/network_menu_icon.cc',
-        'browser/chromeos/status/network_menu_icon.h',
         'browser/chromeos/swap_metrics.cc',
         'browser/chromeos/swap_metrics.h',
         'browser/chromeos/system/ash_system_tray_delegate.cc',
@@ -829,13 +821,6 @@
         'browser/chromeos/upgrade_detector_chromeos.h',
         'browser/chromeos/version_loader.cc',
         'browser/chromeos/version_loader.h',
-        'browser/chromeos/view_ids.h',
-        'browser/chromeos/web_socket_proxy.cc',
-        'browser/chromeos/web_socket_proxy.h',
-        'browser/chromeos/web_socket_proxy_controller.cc',
-        'browser/chromeos/web_socket_proxy_controller.h',
-        'browser/chromeos/web_socket_proxy_helper.cc',
-        'browser/chromeos/web_socket_proxy_helper.h',
         'browser/chromeos/xinput_hierarchy_changed_event_listener.cc',
         'browser/chromeos/xinput_hierarchy_changed_event_listener.h',
         'browser/chromeos/xinput_hierarchy_changed_event_listener_aura.cc',
@@ -854,6 +839,22 @@
             'browser/chromeos/extensions/file_manager/file_browser_private_api_factory.h',
             'browser/chromeos/extensions/file_manager/private_api_base.cc',
             'browser/chromeos/extensions/file_manager/private_api_base.h',
+            'browser/chromeos/extensions/file_manager/private_api_dialog.cc',
+            'browser/chromeos/extensions/file_manager/private_api_dialog.h',
+            'browser/chromeos/extensions/file_manager/private_api_drive.cc',
+            'browser/chromeos/extensions/file_manager/private_api_drive.h',
+            'browser/chromeos/extensions/file_manager/private_api_file_system.cc',
+            'browser/chromeos/extensions/file_manager/private_api_file_system.h',
+            'browser/chromeos/extensions/file_manager/private_api_misc.cc',
+            'browser/chromeos/extensions/file_manager/private_api_misc.h',
+            'browser/chromeos/extensions/file_manager/private_api_mount.cc',
+            'browser/chromeos/extensions/file_manager/private_api_mount.h',
+            'browser/chromeos/extensions/file_manager/private_api_strings.cc',
+            'browser/chromeos/extensions/file_manager/private_api_strings.h',
+            'browser/chromeos/extensions/file_manager/private_api_tasks.cc',
+            'browser/chromeos/extensions/file_manager/private_api_tasks.h',
+            'browser/chromeos/extensions/file_manager/private_api_util.cc',
+            'browser/chromeos/extensions/file_manager/private_api_util.h',
             'browser/chromeos/extensions/file_manager/zip_file_creator.cc',
             'browser/chromeos/extensions/file_manager/zip_file_creator.h',
             'browser/chromeos/extensions/input_method_api.cc',
@@ -902,8 +903,10 @@
             ['exclude', 'browser/chromeos/extensions/file_manager/file_browser_private_api.h'],
             ['exclude', 'browser/chromeos/extensions/file_manager/file_browser_private_api_factory.cc'],
             ['exclude', 'browser/chromeos/extensions/file_manager/file_browser_private_api_factory.h'],
-            ['exclude', 'browser/chromeos/extensions/file_manager/file_handler_util.cc'],
+            ['exclude', 'browser/chromeos/extensions/file_manager/file_manager_util.cc'],
             ['exclude', 'browser/chromeos/extensions/file_manager/file_manager_util.h'],
+            ['exclude', 'browser/chromeos/extensions/file_manager/file_tasks.cc'],
+            ['exclude', 'browser/chromeos/extensions/file_manager/file_tasks.h'],
             ['exclude', 'browser/chromeos/extensions/file_manager/zip_file_creator.cc'],
             ['exclude', 'browser/chromeos/extensions/file_manager/zip_file_creator.h'],
             ['exclude', 'browser/chromeos/extensions/media_player_api.cc'],
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index 724e640..ae67a7c 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -378,6 +378,16 @@
         'browser/extensions/api/push_messaging/push_messaging_invalidation_mapper.h',
         'browser/extensions/api/record/record_api.cc',
         'browser/extensions/api/record/record_api.h',
+        'browser/extensions/api/recovery_private/recovery_operation.cc',
+        'browser/extensions/api/recovery_private/recovery_operation.h',
+        'browser/extensions/api/recovery_private/recovery_operation_manager.cc',
+        'browser/extensions/api/recovery_private/recovery_operation_manager.h',
+        'browser/extensions/api/recovery_private/recovery_private_api.cc',
+        'browser/extensions/api/recovery_private/recovery_private_api.h',
+        'browser/extensions/api/recovery_private/write_from_file_operation.cc',
+        'browser/extensions/api/recovery_private/write_from_file_operation.h',
+        'browser/extensions/api/recovery_private/write_from_url_operation.cc',
+        'browser/extensions/api/recovery_private/write_from_url_operation.h',
         'browser/extensions/api/runtime/runtime_api.cc',
         'browser/extensions/api/runtime/runtime_api.h',
         'browser/extensions/api/serial/serial_api.cc',
@@ -392,6 +402,10 @@
         'browser/extensions/api/serial/serial_port_enumerator_win.cc',
         'browser/extensions/api/session_restore/session_restore_api.cc',
         'browser/extensions/api/session_restore/session_restore_api.h',
+        'browser/extensions/api/signedin_devices/id_mapping_helper.cc',
+        'browser/extensions/api/signedin_devices/id_mapping_helper.h',
+        'browser/extensions/api/signedin_devices/signedin_devices_api.cc',
+        'browser/extensions/api/signedin_devices/signedin_devices_api.h',
         'browser/extensions/api/socket/socket.cc',
         'browser/extensions/api/socket/socket.h',
         'browser/extensions/api/socket/socket_api.cc',
@@ -514,8 +528,6 @@
         'browser/extensions/api/web_request/web_request_permissions.h',
         'browser/extensions/api/web_request/web_request_time_tracker.cc',
         'browser/extensions/api/web_request/web_request_time_tracker.h',
-        'browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_api.cc',
-        'browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_api.h',
         'browser/extensions/api/webstore_private/webstore_private_api.cc',
         'browser/extensions/api/webstore_private/webstore_private_api.h',
         'browser/extensions/api/webview/webview_api.cc',
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 879ff60..9f203dc 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -1439,6 +1439,9 @@
         'browser/ui/sync/browser_synced_window_delegate.cc',
         'browser/ui/sync/inline_login_dialog.cc',
         'browser/ui/sync/inline_login_dialog.h',
+        'browser/ui/sync/one_click_signin_bubble_delegate.h',
+        'browser/ui/sync/one_click_signin_bubble_links_delegate.cc',
+        'browser/ui/sync/one_click_signin_bubble_links_delegate.h',
         'browser/ui/sync/one_click_signin_helper.cc',
         'browser/ui/sync/one_click_signin_helper.h',
         'browser/ui/sync/one_click_signin_histogram.h',
@@ -1540,6 +1543,8 @@
         'browser/ui/views/autofill/autofill_dialog_views.h',
         'browser/ui/views/autofill/autofill_popup_view_views.cc',
         'browser/ui/views/autofill/autofill_popup_view_views.h',
+        'browser/ui/views/autofill/decorated_textfield.cc',
+        'browser/ui/views/autofill/decorated_textfield.h',
         'browser/ui/views/avatar_menu_bubble_view.cc',
         'browser/ui/views/avatar_menu_bubble_view.h',
         'browser/ui/views/avatar_menu_button.cc',
@@ -1603,6 +1608,7 @@
         'browser/ui/views/crypto_module_password_dialog_view.h',
         'browser/ui/views/critical_notification_bubble_view.cc',
         'browser/ui/views/critical_notification_bubble_view.h',
+        'browser/ui/views/desktop_media_picker_views.cc',
         'browser/ui/views/detachable_toolbar_view.cc',
         'browser/ui/views/detachable_toolbar_view.h',
         'browser/ui/views/download/download_in_progress_dialog_view.cc',
@@ -1713,6 +1719,9 @@
         'browser/ui/views/frame/opaque_browser_frame_view.h',
         'browser/ui/views/frame/popup_non_client_frame_view.cc',
         'browser/ui/views/frame/popup_non_client_frame_view.h',
+        'browser/ui/views/frame/scroll_end_effect_controller.h',
+        'browser/ui/views/frame/scroll_end_effect_controller_ash.cc',
+        'browser/ui/views/frame/scroll_end_effect_controller_ash.h',
         'browser/ui/views/frame/system_menu_insertion_delegate_win.cc',
         'browser/ui/views/frame/system_menu_insertion_delegate_win.h',
         'browser/ui/views/frame/system_menu_model_builder.cc',
@@ -2078,8 +2087,6 @@
         'browser/ui/webui/extensions/pack_extension_handler.h',
         'browser/ui/webui/favicon_source.cc',
         'browser/ui/webui/favicon_source.h',
-        'browser/ui/webui/feedback_ui.cc',
-        'browser/ui/webui/feedback_ui.h',
         'browser/ui/webui/fileicon_source.cc',
         'browser/ui/webui/fileicon_source.h',
         'browser/ui/webui/flags_ui.cc',
@@ -2310,8 +2317,6 @@
         'browser/ui/webui/quota_internals/quota_internals_types.h',
         'browser/ui/webui/quota_internals/quota_internals_ui.cc',
         'browser/ui/webui/quota_internals/quota_internals_ui.h',
-        'browser/ui/webui/screenshot_source.cc',
-        'browser/ui/webui/screenshot_source.h',
         'browser/ui/webui/set_as_default_browser_ui.cc',
         'browser/ui/webui/set_as_default_browser_ui.h',
         'browser/ui/webui/signin/login_ui_service.cc',
@@ -2346,8 +2351,6 @@
         'browser/ui/webui/sync_promo/sync_promo_trial.h',
         'browser/ui/webui/sync_setup_handler.cc',
         'browser/ui/webui/sync_setup_handler.h',
-        'browser/ui/webui/tab_modal_confirm_dialog_webui.cc',
-        'browser/ui/webui/tab_modal_confirm_dialog_webui.h',
         'browser/ui/webui/task_manager/task_manager_dialog.cc',
         'browser/ui/webui/task_manager/task_manager_dialog.h',
         'browser/ui/webui/task_manager/task_manager_handler.cc',
@@ -2458,6 +2461,9 @@
             'browser/ui/cocoa/one_click_signin_bubble_controller.mm',
             'browser/ui/gtk/one_click_signin_bubble_gtk.cc',
             'browser/ui/gtk/one_click_signin_bubble_gtk.h',
+            'browser/ui/sync/one_click_signin_bubble_delegate.h',
+            'browser/ui/sync/one_click_signin_bubble_links_delegate.cc',
+            'browser/ui/sync/one_click_signin_bubble_links_delegate.h',
             'browser/ui/sync/one_click_signin_helper.cc',
             'browser/ui/sync/one_click_signin_helper.h',
             'browser/ui/sync/one_click_signin_histogram.h',
@@ -2966,8 +2972,6 @@
             'browser/ui/views/simple_message_box_views.cc',
             'browser/ui/webui/help/version_updater_basic.cc',
             'browser/ui/webui/help/version_updater_basic.h',
-            'browser/ui/webui/tab_modal_confirm_dialog_webui.cc',
-            'browser/ui/webui/tab_modal_confirm_dialog_webui.h',
           ],
           'conditions': [
             ['win_use_allocator_shim==1', {
@@ -3015,8 +3019,6 @@
               'sources/': [
                 # Exclude all of views.
                 ['exclude', '^browser/ui/views/'],
-                ['exclude', '^browser/ui/webui/tab_modal_confirm_dialog_webui.cc'],
-                ['exclude', '^browser/ui/webui/tab_modal_confirm_dialog_webui.h']
               ]
             }],
             # GTK build only
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index fde3124..4647180 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -386,6 +386,8 @@
         'common/mac/objc_zombie.h',
         'common/mac/objc_zombie.mm',
         'common/media/webrtc_logging_messages.h',
+        'common/metrics/caching_permuted_entropy_provider.cc',
+        'common/metrics/caching_permuted_entropy_provider.h',
         'common/metrics/entropy_provider.cc',
         'common/metrics/entropy_provider.h',
         'common/metrics/metrics_log_base.cc',
@@ -513,10 +515,10 @@
         }],
         ['OS=="win" or OS=="mac"', {
           'sources': [
-            'common/itunes_library.cc',
-            'common/itunes_library.h',
-            'common/itunes_xml_utils.cc',
-            'common/itunes_xml_utils.h',
+            'common/media_galleries/itunes_library.cc',
+            'common/media_galleries/itunes_library.h',
+            'common/media_galleries/itunes_xml_utils.cc',
+            'common/media_galleries/itunes_xml_utils.h',
             'common/media_galleries/picasa_types.cc',
             'common/media_galleries/picasa_types.h',
             'common/media_galleries/pmp_constants.h',
diff --git a/chrome/chrome_dll.gypi b/chrome/chrome_dll.gypi
index 363db35..2c137d0 100644
--- a/chrome/chrome_dll.gypi
+++ b/chrome/chrome_dll.gypi
@@ -148,7 +148,6 @@
                 '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.rc',
                 '<(SHARED_INTERMEDIATE_DIR)/ui/ui_resources/ui_unscaled_resources.rc',
                 '<(SHARED_INTERMEDIATE_DIR)/webkit/blink_resources.rc',
-                '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.rc',
               ],
               'include_dirs': [
                 '<(DEPTH)/third_party/wtl/include',
@@ -291,6 +290,7 @@
                     'app/breakpad_mac.h',
                     'app/chrome_breakpad_client.cc',
                     'app/chrome_breakpad_client.h',
+                    'app/chrome_breakpad_client_mac.mm',
                   ],
                 }, {  # else: mac_breakpad_compiled_in!=1
                   # No Breakpad, put in the stubs.
@@ -356,6 +356,8 @@
             'CHROME_MULTIPLE_DLL_CHILD',
           ],
           'sources': [
+            '<(SHARED_INTERMEDIATE_DIR)/chrome/common_resources.rc',
+            '<(SHARED_INTERMEDIATE_DIR)/chrome/extensions_api_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/chrome_version/chrome_dll_version.rc',
             'app/chrome_main.cc',
             'app/chrome_main_delegate.cc',
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi
index 75955e7..1e56650 100644
--- a/chrome/chrome_renderer.gypi
+++ b/chrome/chrome_renderer.gypi
@@ -373,6 +373,14 @@
             'renderer/media/webrtc_logging_message_filter.h',
           ],
         }],
+        ['enable_spellcheck==0', {
+          'sources/': [
+            ['exclude', '^renderer/spellchecker/']
+          ],
+          'dependencies!': [
+            '../third_party/hunspell/hunspell.gyp:hunspell',
+          ],
+        }],
         ['OS=="mac"', {
           'dependencies': [
             '../third_party/mach_override/mach_override.gyp:mach_override',
diff --git a/chrome/chrome_repack_chrome.gypi b/chrome/chrome_repack_chrome.gypi
index 80683c5..a895435 100644
--- a/chrome/chrome_repack_chrome.gypi
+++ b/chrome/chrome_repack_chrome.gypi
@@ -16,7 +16,6 @@
           '<(SHARED_INTERMEDIATE_DIR)/content/content_resources.pak',
           '<(SHARED_INTERMEDIATE_DIR)/content/browser/tracing/tracing_resources.pak',
           '<(SHARED_INTERMEDIATE_DIR)/webkit/blink_resources.pak',
-          '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.pak',
         ],
       }],
       ['enable_extensions==1', {
diff --git a/chrome/chrome_syzygy.gyp b/chrome/chrome_syzygy.gyp
index 09d4f57..d4994ac 100644
--- a/chrome/chrome_syzygy.gyp
+++ b/chrome/chrome_syzygy.gyp
@@ -3,80 +3,33 @@
 # found in the LICENSE file.
 {
   'conditions': [
-    ['OS=="win" and fastbuild==0 and chrome_multiple_dll==0', {
-      # Reorder or instrument the initial chrome DLL executable, placing the
-      # optimized output and corresponding PDB file into the "syzygy"
-      # subdirectory.
-      # This target won't build in fastbuild, since there are no PDBs.
+    ['OS=="win" and fastbuild==0', {
+      'variables': {
+        'dll_name': 'chrome',
+      },
       'targets': [
         {
           'target_name': 'chrome_dll_syzygy',
           'type': 'none',
           'sources' : [],
-          'dependencies': [
-            '<(DEPTH)/chrome/chrome.gyp:chrome_dll',
+          'includes': [
+            'chrome_syzygy.gypi',
           ],
-          'variables': {
-            'dest_dir': '<(PRODUCT_DIR)/syzygy',
-          },
-          'conditions': [
-            ['asan!=1', {
-              # Reorder chrome DLL executable.
-              # If there's a matching chrome.dll-ordering.json file present in
-              # the output directory, chrome.dll will be ordered according to
-              # that, otherwise it will be randomized.
-              'actions': [
-                {
-                  'action_name': 'Reorder Chrome with Syzygy',
-                  'msvs_cygwin_shell': 0,
-                  'inputs': [
-                    '<(PRODUCT_DIR)/chrome.dll',
-                    '<(PRODUCT_DIR)/chrome.dll.pdb',
-                  ],
-                  'outputs': [
-                    '<(dest_dir)/chrome.dll',
-                    '<(dest_dir)/chrome.dll.pdb',
-                  ],
-                  'action': [
-                    'python',
-                    '<(DEPTH)/chrome/tools/build/win/syzygy_reorder.py',
-                    '--input_executable', '<(PRODUCT_DIR)/chrome.dll',
-                    '--input_symbol', '<(PRODUCT_DIR)/chrome.dll.pdb',
-                    '--destination_dir', '<(dest_dir)',
-                  ],
-                },
-              ],
-            }, {
-              # Instrument chrome DLL executable with SyzyAsan.
-              'actions': [
-                {
-                  'action_name': 'Instrument Chrome with SyzyAsan',
-                  'msvs_cygwin_shell': 0,
-                  'inputs': [
-                    '<(PRODUCT_DIR)/chrome.dll',
-                    '<(PRODUCT_DIR)/chrome.dll.pdb',
-                    '<(DEPTH)/chrome/tools/build/win/win-syzyasan-filter.txt',
-                  ],
-                  'outputs': [
-                    '<(dest_dir)/chrome.dll',
-                    '<(dest_dir)/chrome.dll.pdb',
-                    '<(dest_dir)/asan_rtl.dll',
-                    '<(dest_dir)/asan_rtl.dll.pdb',
-                    '<(dest_dir)/win-syzyasan-filter.txt.json',
-                  ],
-                  'action': [
-                    'python',
-                    '<(DEPTH)/chrome/tools/build/win/syzygy_instrument.py',
-                    '--mode', 'asan',
-                    '--input_executable', '<(PRODUCT_DIR)/chrome.dll',
-                    '--input_symbol', '<(PRODUCT_DIR)/chrome.dll.pdb',
-                    '--filter',
-                    '<(DEPTH)/chrome/tools/build/win/win-syzyasan-filter.txt',
-                    '--destination_dir', '<(dest_dir)',
-                  ],
-                },
-              ],
-            }],
+        },
+      ],
+    }],
+    # Note, not else.
+    ['OS=="win" and fastbuild==0 and chrome_multiple_dll==1', {
+      'variables': {
+        'dll_name': 'chrome_child',
+      },
+      'targets': [
+        {
+          'target_name': 'chrome_child_dll_syzygy',
+          'type': 'none',
+          'sources' : [],
+          'includes': [
+            'chrome_syzygy.gypi',
           ],
         },
       ],
diff --git a/chrome/chrome_syzygy.gypi b/chrome/chrome_syzygy.gypi
new file mode 100644
index 0000000..0fbf6bc
--- /dev/null
+++ b/chrome/chrome_syzygy.gypi
@@ -0,0 +1,78 @@
+# Copyright 2013 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.
+
+# Intended to be included by chrome_syzygy.gyp. A variable 'dll_name' should
+# be set to the base name of the DLL. This is used to generate the build steps
+# for both chrome.dll and chrome_child.dll when in multiple dll mode.
+{
+  # Reorder or instrument the initial chrome DLL executable, placing the
+  # optimized output and corresponding PDB file into the "syzygy"
+  # subdirectory.
+  # This target won't build in fastbuild, since there are no PDBs.
+  'dependencies': [
+    '<(DEPTH)/chrome/chrome.gyp:<(dll_name)_dll',
+  ],
+  'variables': {
+    'dest_dir': '<(PRODUCT_DIR)/syzygy',
+  },
+  'conditions': [
+    ['asan!=1', {
+      # Reorder chrome DLL executable.
+      # If there's a matching chrome.dll-ordering.json file present in
+      # the output directory, chrome.dll will be ordered according to
+      # that, otherwise it will be randomized.
+      'actions': [
+        {
+          'action_name': 'Reorder Chrome with Syzygy',
+          'msvs_cygwin_shell': 0,
+          'inputs': [
+            '<(PRODUCT_DIR)/<(dll_name).dll',
+            '<(PRODUCT_DIR)/<(dll_name).dll.pdb',
+          ],
+          'outputs': [
+            '<(dest_dir)/<(dll_name).dll',
+            '<(dest_dir)/<(dll_name).dll.pdb',
+          ],
+          'action': [
+            'python',
+            '<(DEPTH)/chrome/tools/build/win/syzygy_reorder.py',
+            '--input_executable', '<(PRODUCT_DIR)/<(dll_name).dll',
+            '--input_symbol', '<(PRODUCT_DIR)/<(dll_name).dll.pdb',
+            '--destination_dir', '<(dest_dir)',
+          ],
+        },
+      ],
+    }, {
+      # Instrument chrome DLL executable with SyzyAsan.
+      'actions': [
+        {
+          'action_name': 'Instrument Chrome with SyzyAsan',
+          'msvs_cygwin_shell': 0,
+          'inputs': [
+            '<(PRODUCT_DIR)/<(dll_name).dll',
+            '<(PRODUCT_DIR)/<(dll_name).dll.pdb',
+            '<(DEPTH)/chrome/tools/build/win/win-syzyasan-filter.txt',
+          ],
+          'outputs': [
+            '<(dest_dir)/<(dll_name).dll',
+            '<(dest_dir)/<(dll_name).dll.pdb',
+            '<(dest_dir)/asan_rtl.dll',
+            '<(dest_dir)/asan_rtl.dll.pdb',
+            '<(dest_dir)/win-syzyasan-filter.txt.json',
+          ],
+          'action': [
+            'python',
+            '<(DEPTH)/chrome/tools/build/win/syzygy_instrument.py',
+            '--mode', 'asan',
+            '--input_executable', '<(PRODUCT_DIR)/<(dll_name).dll',
+            '--input_symbol', '<(PRODUCT_DIR)/<(dll_name).dll.pdb',
+            '--filter',
+            '<(DEPTH)/chrome/tools/build/win/win-syzyasan-filter.txt',
+            '--destination_dir', '<(dest_dir)',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index fe5ade0..3902617 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -205,6 +205,7 @@
         'INTERACTIVE_TESTS',
       ],
       'sources': [
+        '../apps/app_shim/app_shim_quit_interactive_uitest_mac.mm',
         'browser/autofill/autofill_interactive_uitest.cc',
         'browser/browser_keyevents_browsertest.cc',
         'browser/extensions/api/omnibox/omnibox_api_interactive_test.cc',
@@ -328,6 +329,7 @@
         ['use_ash==1', {
           'sources': [
             '../ash/drag_drop/drag_drop_interactive_uitest.cc',
+            'browser/ui/window_sizer/window_sizer_ash_uitest.cc',
           ],
         }],
         ['OS=="linux" and toolkit_views==1', {
@@ -389,6 +391,7 @@
           'sources': [
             'browser/chromeos/cros/cros_in_process_browser_test.cc',
             'browser/chromeos/cros/cros_in_process_browser_test.h',
+            'browser/chromeos/input_method/keyboard_browsertest.cc',
             'browser/chromeos/input_method/textinput_browsertest.cc',
             'browser/chromeos/input_method/textinput_surroundingtext_browsertest.cc',
             'browser/chromeos/input_method/textinput_test_helper.cc',
@@ -453,7 +456,6 @@
             '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/ui/ui_resources/ui_unscaled_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/webkit/blink_resources.rc',
-            '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.rc',
 
             'browser/ui/views/accessibility/browser_views_accessibility_browsertest.cc',
           ],
@@ -999,8 +1001,6 @@
       'sources': [
         'test/chromedriver/server/http_handler.cc',
         'test/chromedriver/server/http_handler.h',
-        'test/chromedriver/server/http_response.cc',
-        'test/chromedriver/server/http_response.h',
       ],
       # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
       'msvs_disabled_warnings': [ 4267, ],
@@ -1015,13 +1015,10 @@
         '..',
       ],
       'sources': [
-        '../third_party/mongoose/mongoose.c',
-        '../third_party/mongoose/mongoose.h',
         'test/chromedriver/server/chromedriver_server.cc',
       ],
       # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-      # c4306 is because Mongoose casts -1 directly to a handle type.
-      'msvs_disabled_warnings': [ 4306, 4267, ],
+      'msvs_disabled_warnings': [ 4267, ],
     },
     {
       'target_name': 'chromedriver2_unittests',
@@ -1061,7 +1058,6 @@
         'test/chromedriver/commands_unittest.cc',
         'test/chromedriver/logging_unittest.cc',
         'test/chromedriver/server/http_handler_unittest.cc',
-        'test/chromedriver/server/http_response_unittest.cc',
         'test/chromedriver/session_commands_unittest.cc',
         'test/chromedriver/session_unittest.cc',
         'test/chromedriver/util_unittest.cc',
@@ -1352,7 +1348,6 @@
         'browser/extensions/api/usb/usb_manual_apitest.cc',
         'browser/extensions/api/web_navigation/web_navigation_apitest.cc',
         'browser/extensions/api/web_request/web_request_apitest.cc',
-        'browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_apitest.cc',
         'browser/extensions/api/webstore_private/webstore_private_apitest.cc',
         'browser/extensions/app_background_page_apitest.cc',
         'browser/extensions/app_process_apitest.cc',
@@ -1484,6 +1479,7 @@
         'browser/policy/policy_prefs_browsertest.cc',
         'browser/policy/test_utils.cc',
         'browser/policy/test_utils.h',
+        'browser/prefs/pref_functional_browsertest.cc',
         'browser/prefs/pref_service_browsertest.cc',
         'browser/prerender/prefetch_browsertest.cc',
         'browser/prerender/prerender_browsertest.cc',
@@ -1592,6 +1588,7 @@
         'browser/ui/panels/panel_extension_browsertest.cc',
         'browser/ui/prefs/prefs_tab_helper_browsertest.cc',
         'browser/ui/startup/startup_browser_creator_browsertest.cc',
+        'browser/ui/sync/one_click_signin_bubble_links_delegate_browsertest.cc',
         'browser/ui/sync/profile_signin_confirmation_helper_browsertest.cc',
         'browser/ui/tab_modal_confirm_dialog_browsertest.cc',
         'browser/ui/tab_modal_confirm_dialog_browsertest.h',
@@ -1605,7 +1602,6 @@
         'browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc',
         'browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc',
         'browser/ui/views/select_file_dialog_extension_browsertest.cc',
-        'browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc',
         'browser/ui/views/toolbar_view_browsertest.cc',
         'browser/ui/views/web_dialog_view_browsertest.cc',
         'browser/ui/webui/bidi_checker_web_ui_test.cc',
@@ -1640,6 +1636,7 @@
         'browser/ui/webui/options/font_settings_browsertest.js',
         'browser/ui/webui/options/language_options_browsertest.js',
         'browser/ui/webui/options/language_options_dictionary_download_browsertest.js',
+        'browser/ui/webui/options/manage_profile_browsertest.js',
         'browser/ui/webui/options/options_browsertest.cc',
         'browser/ui/webui/options/options_browsertest.js',
         'browser/ui/webui/options/options_ui_browsertest.cc',
@@ -1760,7 +1757,7 @@
         ['enable_one_click_signin==0', {
           'sources!': [
             'browser/ui/gtk/one_click_signin_bubble_gtk_browsertest.cc',
-            'browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc',
+            'browser/ui/sync/one_click_signin_bubble_links_delegate_browsertest.cc',
           ]
         }],
         ['disable_nacl==0', {
@@ -1928,7 +1925,6 @@
             '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/ui/ui_resources/ui_unscaled_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/webkit/blink_resources.rc',
-            '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.rc',
           ],
           'include_dirs': [
             '<(DEPTH)/third_party/wtl/include',
@@ -2167,7 +2163,6 @@
             '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/ui/ui_resources/ui_unscaled_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/webkit/blink_resources.rc',
-            '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.rc',
           ],
           'include_dirs': [
             '<(DEPTH)/third_party/wtl/include',
@@ -2551,7 +2546,6 @@
             '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/ui/ui_resources/ui_unscaled_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/webkit/blink_resources.rc',
-            '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.rc',
           ],
           'include_dirs': [
             '<(DEPTH)/third_party/wtl/include',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 12ae40f..1b3caca 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -114,6 +114,8 @@
         'browser/extensions/api/messaging/native_messaging_test_util.h',
         'browser/extensions/api/system_storage/test_storage_info_provider.cc',
         'browser/extensions/api/system_storage/test_storage_info_provider.h',
+        'browser/extensions/extension_notification_observer.cc',
+        'browser/extensions/extension_notification_observer.h',
         'browser/extensions/fake_safe_browsing_database_manager.cc',
         'browser/extensions/fake_safe_browsing_database_manager.h',
         'browser/extensions/mock_extension_special_storage_policy.cc',
@@ -170,6 +172,8 @@
         'browser/signin/fake_signin_manager.h',
         'browser/ssl/ssl_client_auth_requestor_mock.cc',
         'browser/ssl/ssl_client_auth_requestor_mock.h',
+        'browser/storage_monitor/test_media_transfer_protocol_manager_linux.cc',
+        'browser/storage_monitor/test_media_transfer_protocol_manager_linux.h',
         'browser/storage_monitor/test_storage_monitor.cc',
         'browser/storage_monitor/test_storage_monitor.h',
         'browser/ui/browser.h',
@@ -374,8 +378,8 @@
         }],
         ['OS=="win" or OS=="mac"', {
           'sources': [
-            'utility/media_galleries/pmp_test_helper.cc',
-            'utility/media_galleries/pmp_test_helper.h',
+            'common/media_galleries/pmp_test_helper.cc',
+            'common/media_galleries/pmp_test_helper.h',
           ],
         }],
         ['OS=="mac"', {
@@ -661,6 +665,7 @@
         'browser/chromeos/login/parallel_authenticator_unittest.cc',
         'browser/chromeos/login/screens/screen_context_unittest.cc',
         'browser/chromeos/login/user_manager_unittest.cc',
+        'browser/chromeos/login/wallpaper_manager_unittest.cc',
         'browser/chromeos/memory/oom_priority_manager_unittest.cc',
         'browser/chromeos/mobile/mobile_activator_unittest.cc',
         'browser/chromeos/mobile_config_unittest.cc',
@@ -690,12 +695,9 @@
         'browser/chromeos/settings/owner_key_util_unittest.cc',
         'browser/chromeos/settings/session_manager_operation_unittest.cc',
         'browser/chromeos/settings/stub_cros_settings_provider_unittest.cc',
-        'browser/chromeos/status/network_menu_icon.cc',
-        'browser/chromeos/status/network_menu_icon_unittest.cc',
         'browser/chromeos/system/automatic_reboot_manager_unittest.cc',
         'browser/chromeos/system_logs/lsb_release_log_source_unittest.cc',
         'browser/chromeos/version_loader_unittest.cc',
-        'browser/chromeos/web_socket_proxy_helper_unittest.cc',
         'browser/command_updater_unittest.cc',
         'browser/component_updater/test/component_installers_unittest.cc',
         'browser/component_updater/test/component_patcher_mock.h',
@@ -788,6 +790,8 @@
         'browser/extensions/api/push_messaging/obfuscated_gaia_id_fetcher_unittest.cc',
         'browser/extensions/api/push_messaging/push_messaging_invalidation_handler_unittest.cc',
         'browser/extensions/api/serial/serial_port_enumerator_unittest.cc',
+        'browser/extensions/api/signedin_devices/id_mapping_helper_unittest.cc',
+        'browser/extensions/api/signedin_devices/signedin_devices_api_unittest.cc',
         'browser/extensions/api/socket/socket_api_unittest.cc',
         'browser/extensions/api/socket/tcp_socket_unittest.cc',
         'browser/extensions/api/socket/udp_socket_unittest.cc',
@@ -927,9 +931,11 @@
         'browser/local_discovery/privet_url_fetcher_unittest.cc',
         'browser/mac/keystone_glue_unittest.mm',
         'browser/managed_mode/managed_mode_url_filter_unittest.cc',
-        'browser/managed_mode/managed_user_registration_service_unittest.cc',
         'browser/managed_mode/managed_user_service_unittest.cc',
+        'browser/managed_mode/managed_user_sync_service_unittest.cc',
         'browser/managed_mode/managed_user_refresh_token_fetcher_unittest.cc',
+        'browser/managed_mode/managed_user_registration_utility_unittest.cc',
+        'browser/media/desktop_media_picker_model_unittest.cc',
         'browser/media/webrtc_log_uploader_unittest.cc',
         'browser/media_galleries/fileapi/native_media_file_util_unittest.cc',
         'browser/media_galleries/linux/mtp_device_object_enumerator_unittest.cc',
@@ -1007,6 +1013,7 @@
         'browser/policy/cloud/cloud_external_data_store_unittest.cc',
         'browser/policy/cloud/cloud_policy_client_unittest.cc',
         'browser/policy/cloud/cloud_policy_core_unittest.cc',
+        'browser/policy/cloud/cloud_policy_invalidator_unittest.cc',
         'browser/policy/cloud/cloud_policy_manager_unittest.cc',
         'browser/policy/cloud/cloud_policy_refresh_scheduler_unittest.cc',
         'browser/policy/cloud/cloud_policy_service_unittest.cc',
@@ -1045,6 +1052,7 @@
         'browser/policy/preferences_mock_mac.cc',
         'browser/policy/preferences_mock_mac.h',
         'browser/policy/preg_parser_win_unittest.cc',
+        'browser/policy/registry_dict_win_unittest.cc',
         'browser/policy/url_blacklist_manager_unittest.cc',
         'browser/predictors/autocomplete_action_predictor_table_unittest.cc',
         'browser/predictors/autocomplete_action_predictor_unittest.cc',
@@ -1195,7 +1203,6 @@
         'browser/sync/glue/change_processor_mock.cc',
         'browser/sync/glue/change_processor_mock.h',
         'browser/sync/glue/chrome_encryptor_unittest.cc',
-        'browser/sync/glue/chrome_extensions_activity_monitor_unittest.cc',
         'browser/sync/glue/data_type_controller_mock.cc',
         'browser/sync/glue/data_type_controller_mock.h',
         'browser/sync/glue/data_type_error_handler_mock.cc',
@@ -1203,6 +1210,7 @@
         'browser/sync/glue/data_type_manager_impl_unittest.cc',
         'browser/sync/glue/data_type_manager_mock.cc',
         'browser/sync/glue/data_type_manager_mock.h',
+        'browser/sync/glue/extensions_activity_monitor_unittest.cc',
         'browser/sync/glue/fake_data_type_controller.cc',
         'browser/sync/glue/fake_data_type_controller.h',
         'browser/sync/glue/fake_generic_change_processor.cc',
@@ -1536,7 +1544,9 @@
         'browser/ui/search/search_model_unittest.cc',
         'browser/ui/startup/session_crashed_infobar_delegate_unittest.cc',
         'browser/ui/sync/one_click_signin_helper_unittest.cc',
+        'browser/ui/sync/one_click_signin_sync_starter_unittest.cc',
         'browser/ui/sync/profile_signin_confirmation_helper_unittest.cc',
+        'browser/ui/sync/sync_promo_ui_unittest.cc',
         'browser/ui/tab_contents/tab_contents_iterator_unittest.cc',
         'browser/ui/tabs/dock_info_unittest.cc',
         'browser/ui/tabs/pinned_tab_codec_unittest.cc',
@@ -1561,6 +1571,7 @@
         'browser/ui/toolbar/wrench_menu_model_unittest.cc',
         'browser/ui/views/accelerator_table_unittest.cc',
         'browser/ui/views/accessibility/accessibility_event_router_views_unittest.cc',
+        'browser/ui/views/autofill/decorated_textfield_unittest.cc',
         'browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc',
         'browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc',
         'browser/ui/views/bookmarks/bookmark_context_menu_test.cc',
@@ -1578,6 +1589,7 @@
         'browser/ui/views/reload_button_unittest.cc',
         'browser/ui/views/select_file_dialog_extension_unittest.cc',
         'browser/ui/views/status_icons/status_tray_win_unittest.cc',
+        'browser/ui/views/sync/one_click_signin_bubble_view_unittest.cc',
         'browser/ui/views/tabs/fake_base_tab_strip_controller.cc',
         'browser/ui/views/tabs/fake_base_tab_strip_controller.h',
         'browser/ui/views/tabs/stacked_tab_strip_layout_unittest.cc',
@@ -1606,7 +1618,7 @@
         'browser/upload_list_unittest.cc',
         'browser/chrome_content_browser_client_unittest.cc',
         'browser/undo/undo_manager_test.cc',
-        'browser/usb/usb_service_unittest.cc',
+        'browser/usb/usb_context_unittest.cc',
         'browser/user_style_sheet_watcher_unittest.cc',
         'browser/value_store/leveldb_value_store_unittest.cc',
         'browser/value_store/testing_value_store_unittest.cc',
@@ -1711,6 +1723,7 @@
         'common/mac/nscoder_util_unittest.mm',
         'common/mac/objc_method_swizzle_unittest.mm',
         'common/mac/objc_zombie_unittest.mm',
+        'common/metrics/caching_permuted_entropy_provider_unittest.cc',
         'common/metrics/entropy_provider_unittest.cc',
         'common/metrics/metrics_log_base_unittest.cc',
         'common/metrics/metrics_log_manager_unittest.cc',
@@ -1730,8 +1743,6 @@
         'common/translate/translate_common_metrics_unittest.cc',
         'common/translate/translate_util_unittest.cc',
         'common/worker_thread_ticker_unittest.cc',
-        'nacl/nacl_ipc_adapter_unittest.cc',
-        'nacl/nacl_validation_query_unittest.cc',
         'renderer/chrome_content_renderer_client_unittest.cc',
         'renderer/content_settings_observer_unittest.cc',
         'renderer/extensions/chrome_v8_context_set_unittest.cc',
@@ -1843,6 +1854,12 @@
         '../components/autofill/core/browser/webdata/autofill_table_unittest.cc',
         '../components/autofill/core/browser/webdata/web_data_service_unittest.cc',
         '../components/autofill/core/common/password_form_fill_data_unittest.cc',
+
+        # TODO(yael): Move to //components/components_tests.gypi once
+        # nacl_defines is moved out of chrome.gyp into a common place.
+        '../components/nacl/loader/nacl_ipc_adapter_unittest.cc',
+        '../components/nacl/loader/nacl_validation_query_unittest.cc',
+
         '../components/webdata/common/web_database_migration_unittest.cc',
         '../components/webdata/common/web_data_service_test_util.cc',
         '../components/webdata/common/web_data_service_test_util.h',
@@ -1966,10 +1983,21 @@
             ['exclude', '^browser/background/'],
           ],
         }],
+        ['enable_spellcheck==0', {
+          'sources/': [
+             ['exclude', '^browser/spellchecker/'],
+             ['exclude', '^renderer/spellchecker/'],
+          ],
+          'dependencies!': [
+             '../third_party/hunspell/hunspell.gyp:hunspell',
+          ],
+        }],
         ['enable_one_click_signin==0', {
           'sources!': [
             'browser/ui/cocoa/one_click_signin_bubble_controller_unittest.mm',
             'browser/ui/sync/one_click_signin_helper_unittest.cc',
+            'browser/ui/sync/one_click_signin_sync_starter_unittest.cc',
+            'browser/ui/views/sync/one_click_signin_bubble_view_unittest.cc',
           ]
         }],
         ['disable_nacl==1', {
@@ -1978,8 +2006,10 @@
             'browser/nacl_host/nacl_validation_cache_unittest.cc',
             'browser/nacl_host/pnacl_host_unittest.cc',
             'browser/nacl_host/pnacl_translation_cache_unittest.cc',
-            'nacl/nacl_ipc_adapter_unittest.cc',
-            'nacl/nacl_validation_query_unittest.cc',
+            # TODO(yael): Move to //components/components_tests.gypi once
+            # nacl_defines is moved out of chrome.gyp into a common place.
+            '../components/nacl/loader/nacl_ipc_adapter_unittest.cc',
+            '../components/nacl/loader/nacl_validation_query_unittest.cc',
           ],
         }],
         ['enable_extensions==0', {
@@ -1987,7 +2017,7 @@
             ['exclude', '^../extensions/'],
             ['exclude', '^browser/extensions/activity_log/'],
             ['exclude', '^browser/extensions/api/'],
-            ['exclude', '^browser/sync/glue/chrome_extensions_activity_monitor_unittest.cc'],
+            ['exclude', '^browser/sync/glue/extensions_activity_monitor_unittest.cc'],
             ['exclude', '^common/extensions/api/'],
             ['exclude', '^common/extensions/manifest_handlers/'],
             ['exclude', '^common/extensions/manifest_tests/'],
@@ -2143,9 +2173,7 @@
             'browser/signin/signin_manager_unittest.cc',
             'browser/storage_monitor/storage_monitor_linux_unittest.cc',
             'browser/ui/sync/one_click_signin_helper_unittest.cc',
-          ],
-          'sources': [
-            'browser/ui/webui/feedback_ui_unittest.cc',
+            'browser/ui/sync/one_click_signin_sync_starter_unittest.cc',
           ],
         }, { # else: chromeos == 0
           'sources/': [
@@ -2282,7 +2310,7 @@
         ['OS=="win" or OS=="mac"', {
           'sources': [
             'browser/media_galleries/fileapi/picasa/picasa_file_util_unittest.cc',
-            'utility/itunes_library_parser_unittest.cc',
+            'utility/media_galleries/itunes_library_parser_unittest.cc',
             'utility/media_galleries/picasa_album_table_reader_unittest.cc',
             'utility/media_galleries/picasa_albums_indexer_unittest.cc',
             'utility/media_galleries/pmp_column_reader_unittest.cc',
@@ -2339,7 +2367,6 @@
             '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/ui/ui_resources/ui_unscaled_resources.rc',
             '<(SHARED_INTERMEDIATE_DIR)/webkit/blink_resources.rc',
-            '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.rc',
           ],
           'link_settings': {
             'libraries': [
@@ -2410,7 +2437,7 @@
             'browser/ui/browser_iterator_unittest.cc',
             'browser/ui/fullscreen/fullscreen_controller_state_unittest.cc',
             'browser/ui/fullscreen/fullscreen_controller_unittest.cc',
-            'browser/ui/search/instant_controller_unittest.cc',
+            'browser/ui/search/instant_ntp_prerenderer_unittest.cc',
             'browser/ui/search/instant_page_unittest.cc',
             'browser/ui/search/search_delegate_unittest.cc',
             'browser/ui/search/search_model_unittest.cc',
@@ -2452,7 +2479,7 @@
             'common/importer/firefox_importer_utils_unittest.cc',
 
             # USB service is not supported on Android.
-            'browser/usb/usb_service_unittest.cc',
+            'browser/usb/usb_context_unittest.cc',
           ],
           'sources/': [
             ['exclude', '^browser/captive_portal/'],
@@ -2472,7 +2499,6 @@
             ['exclude', '^browser/ui/tabs/'],
             ['exclude', '^browser/ui/toolbar/'],
             ['exclude', '^browser/ui/webui/downloads_'],
-            ['exclude', '^browser/ui/webui/feedback_'],
             ['exclude', '^browser/ui/webui/flags_'],
             ['exclude', '^browser/ui/webui/help/'],
             ['exclude', '^browser/ui/webui/options/'],
diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc
index 25e5d05..874a039 100644
--- a/chrome/common/chrome_constants.cc
+++ b/chrome/common/chrome_constants.cc
@@ -220,7 +220,7 @@
 
 const char* const kUnknownLanguageCode = "und";
 
-const int kJavascriptMessageExpectedDelay = 1000;
+const int kJavaScriptMessageExpectedDelay = 1000;
 
 #if defined(OS_ANDROID)
 const bool kEnableTouchIcon = true;
diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h
index 195b4c8..6775c80 100644
--- a/chrome/common/chrome_constants.h
+++ b/chrome/common/chrome_constants.h
@@ -96,11 +96,10 @@
 // (Matches what the CLD -Compact Language Detection- library reports.)
 extern const char* const kUnknownLanguageCode;
 
-// If another javascript message box is displayed within
-// kJavascriptMessageExpectedDelay of a previous javascript message box being
-// dismissed, display an option to suppress future message boxes from this
-// contents.
-extern const int kJavascriptMessageExpectedDelay;
+// If a WebContents is impolite and displays a second JavaScript alert within
+// kJavaScriptMessageExpectedDelay of a previous JavaScript alert being
+// dismissed, display an option to suppress future alerts from this WebContents.
+extern const int kJavaScriptMessageExpectedDelay;
 
 // Are touch icons enabled? False by default.
 extern const bool kEnableTouchIcon;
diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h
index a3b4dab..9d1b050 100644
--- a/chrome/common/chrome_paths.h
+++ b/chrome/common/chrome_paths.h
@@ -101,11 +101,9 @@
   DIR_CHROMEOS_CUSTOM_WALLPAPERS,     // Directory where custom wallpapers
                                       // reside.
 #endif
-#if defined(ENABLE_MANAGED_USERS)
   DIR_MANAGED_USERS_DEFAULT_APPS,  // Directory where installer places .crx
                                    // files to be installed when managed user
                                    // session starts.
-#endif
 
   // Valid only in development environment; TODO(darin): move these
   DIR_GEN_TEST_DATA,            // Directory where generated test data resides.
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 5694f1b..14665c0 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -164,6 +164,12 @@
 // expiration of credentials during testing.
 const char kClearTokenService[]             = "clear-token-service";
 
+// The maximum amount of delay in ms between receiving a cloud policy
+// invalidation and fetching the policy. A random delay up to this value is used
+// to prevent Chrome clients from overwhelming the cloud policy server when a
+// policy which affects many users is changed.
+const char kCloudPolicyInvalidationDelay[]  = "cloud-policy-invalidation-delay";
+
 // Used with kCloudPrintFile. Tells Chrome to delete the file when finished
 // displaying the print dialog.
 const char kCloudPrintDeleteFile[]          = "cloud-print-delete-file";
@@ -518,8 +524,8 @@
 // Enables the benchmarking extensions.
 const char kEnableBenchmarking[]            = "enable-benchmarking";
 
-// Enables a sync promo that is displayed in the bookmark bubble.
-const char kEnableBookmarkSyncPromo[]       = "enable-bookmark-sync-promo";
+// Enables pushing cloud policy to Chrome using an invalidation service.
+const char kEnableCloudPolicyPush[]         = "enable-cloud-policy-push";
 
 // This applies only when the process type is "service". Enables the Cloud
 // Print Proxy component within the service process.
@@ -549,10 +555,6 @@
 const char kEasyOffStoreExtensionInstall[]  =
     "easy-off-store-extension-install";
 
-// Enables extension APIs that are in development.
-const char kEnableExperimentalExtensionApis[] =
-    "enable-experimental-extension-apis";
-
 // Enables logging for extension activity.
 const char kEnableExtensionActivityLogging[] =
     "enable-extension-activity-logging";
@@ -684,6 +686,9 @@
 // Enables support of sticky keys.
 const char kEnableStickyKeys[]              = "enable-sticky-keys";
 
+// Disables support of sticky keys.
+const char kDisableStickyKeys[]              = "disable-sticky-keys";
+
 // Disable SPDY/3.1. This is a temporary testing flag.
 const char kDisableSpdy31[]                 = "disable-spdy31";
 
@@ -1006,6 +1011,13 @@
 // launching Chrome for the purpose of hosting background apps).
 const char kNoStartupWindow[]               = "no-startup-window";
 
+// Disables checking whether we received an acknowledgment when registering
+// a supervised user. Also disables the timeout during registration that waits
+// for the ack. Useful when debugging against a server that does not
+// support notifications.
+const char kNoManagedUserAcknowledgmentCheck[]  =
+    "no-managed-user-acknowledgment-check";
+
 // Specifies the maximum number of threads to use for running the Proxy
 // Autoconfig (PAC) script.
 const char kNumPacThreads[]                 = "num-pac-threads";
@@ -1476,6 +1488,10 @@
 
 // Uses the tablet specific UI components when available.
 const char kTabletUI[]                      = "tablet-ui";
+
+// Enables support for playing videos on Google Cast devices.
+const char kEnableCast[]                    = "enable-cast";
+
 #endif
 
 #if defined(USE_ASH)
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 94431c4..613a30f 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -61,6 +61,7 @@
 extern const char kChromeVersion[];
 extern const char kCipherSuiteBlacklist[];
 extern const char kClearTokenService[];
+extern const char kCloudPolicyInvalidationDelay[];
 extern const char kCloudPrintDeleteFile[];
 extern const char kCloudPrintFile[];
 extern const char kCloudPrintJobTitle[];
@@ -156,14 +157,13 @@
 extern const char kEnableAuthNegotiatePort[];
 extern const char kEnableAutologin[];
 extern const char kEnableBenchmarking[];
-extern const char kEnableBookmarkSyncPromo[];
+extern const char kEnableCloudPolicyPush[];
 extern const char kEnableCloudPrintProxy[];
 extern const char kEnableComponentCloudPolicy[];
 extern const char kEnableContacts[];
 extern const char kEnableDeviceDiscovery[];
 extern const char kEnableDevToolsExperiments[];
 extern const char kEnableDnsProbes[];
-extern const char kEnableExperimentalExtensionApis[];
 extern const char kEnableExtensionActivityLogging[];
 extern const char kEnableExtensionActivityLogTesting[];
 extern const char kEnableFastUnload[];
@@ -196,6 +196,8 @@
 extern const char kEnableSavePasswordBubble[];
 extern const char kEnableSdch[];
 extern const char kEnableStickyKeys[];
+extern const char kDisableStickyKeys[];
+extern const char kDisableSpdy31[];
 extern const char kEnableSpdy4a2[];
 extern const char kEnableSpdyCredentialFrames[];
 extern const char kEnableSpellingAutoCorrect[];
@@ -273,6 +275,7 @@
 extern const char kNoPings[];
 extern const char kNoServiceAutorun[];
 extern const char kNoStartupWindow[];
+extern const char kNoManagedUserAcknowledgmentCheck[];
 extern const char kNtpAppInstallHint[];
 extern const char kNumPacThreads[];
 extern const char kOnlyBlockSettingThirdPartyCookies[];
@@ -403,6 +406,7 @@
 extern const char kEnableTranslate[];
 extern const char kFakeCloudPolicyType[];
 extern const char kTabletUI[];
+extern const char kEnableCast[];
 #endif
 
 #if defined(USE_ASH)
diff --git a/chrome/common/chrome_utility_messages.h b/chrome/common/chrome_utility_messages.h
index f48b136..3534f60 100644
--- a/chrome/common/chrome_utility_messages.h
+++ b/chrome/common/chrome_utility_messages.h
@@ -14,7 +14,7 @@
 #include "base/tuple.h"
 #include "base/values.h"
 #include "chrome/common/extensions/update_manifest.h"
-#include "chrome/common/itunes_library.h"
+#include "chrome/common/media_galleries/itunes_library.h"
 #include "chrome/common/media_galleries/picasa_types.h"
 #include "chrome/common/safe_browsing/zip_analyzer.h"
 #include "ipc/ipc_message_macros.h"
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 458fa63..b96eb44 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -44,6 +44,26 @@
     "channel": "stable",
     "contexts": ["blessed_extension", "unblessed_extension", "content_script"]
   },
+  "app.getDetails": {
+    "contexts": ["blessed_extension", "unblessed_extension", "content_script"],
+    "matches": []
+  },
+  "app.getDetailsForFrame": {
+    "contexts": ["blessed_extension", "unblessed_extension", "content_script"],
+    "matches": []
+  },
+  "app.getIsInstalled": {
+    "contexts": ["blessed_extension", "unblessed_extension", "content_script"],
+    "matches": []
+  },
+  "app.installState": {
+    "contexts": ["blessed_extension", "unblessed_extension", "content_script"],
+    "matches": []
+  },
+  "app.runningState": {
+    "contexts": ["blessed_extension", "unblessed_extension", "content_script"],
+    "matches": []
+  },
   "audio": {
     "dependencies": ["permission:audio"],
     "contexts": ["blessed_extension"]
@@ -185,10 +205,6 @@
     "dependencies": ["permission:experimental"],
     "contexts": ["blessed_extension"]
   },
-  "experimental.infobars": {
-    "dependencies": ["permission:experimental"],
-    "contexts": ["blessed_extension"]
-  },
   "experimental.input.virtualKeyboard": {
     "dependencies": ["permission:experimental"],
     "contexts": ["blessed_extension"]
@@ -282,6 +298,10 @@
     "dependencies": ["permission:idle"],
     "contexts": ["blessed_extension"]
   },
+  "infobars": {
+    "dependencies": ["permission:infobars"],
+    "contexts": ["blessed_extension"]
+  },
   "input.ime": {
     "platform": "chromeos",
     "dependencies": ["permission:input"],
@@ -300,6 +320,16 @@
     "dependencies": ["permission:management"],
     "contexts": ["blessed_extension"]
   },
+  "management.getPermissionWarningsByManifest": {
+    "dependencies": [],
+    "channel": "stable",
+    "extension_types": ["extension", "packaged_app", "platform_app"]
+  },
+  "management.uninstallSelf": {
+    "dependencies": [],
+    "channel": "stable",
+    "extension_types": ["extension", "packaged_app", "platform_app"]
+  },
   "mediaGalleries": {
     "dependencies": ["permission:mediaGalleries"],
     "contexts": ["blessed_extension"]
@@ -346,10 +376,6 @@
     "dependencies": ["permission:pageCapture"],
     "contexts": ["blessed_extension"]
   },
-  "pageLauncher": {
-    "dependencies": ["manifest:page_launcher"],
-    "contexts": ["blessed_extension"]
-  },
   "permissions": {
     "channel": "stable",
     "extension_types": ["extension", "packaged_app", "platform_app"],
@@ -376,6 +402,11 @@
     "dependencies": ["permission:pushMessaging"],
     "contexts": ["blessed_extension"]
   },
+  "recoveryPrivate": {
+    "dependencies": ["permission:recoveryPrivate"],
+    "extension_types": ["platform_app"],
+    "contexts": ["blessed_extension"]
+  },
   "rtcPrivate": {
     "dependencies": ["permission:rtcPrivate"],
     "contexts": ["blessed_extension"]
@@ -552,10 +583,6 @@
     "channel": "stable",
     "contexts": ["blessed_extension", "unblessed_extension", "content_script"]
   },
-  "webSocketProxyPrivate": {
-    "dependencies": ["permission:webSocketProxyPrivate"],
-    "contexts": ["blessed_extension"]
-  },
   "webstore": {
     // Hosted apps can use the webstore API from within a blessed context.
     "channel": "stable",
diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json
index d512179..e925dcd 100644
--- a/chrome/common/extensions/api/_manifest_features.json
+++ b/chrome/common/extensions/api/_manifest_features.json
@@ -147,7 +147,7 @@
     }
   ],
   "export": {
-    "channel": "dev",
+    "channel": "stable",
     "extension_types": ["shared_module"],
     "whitelist": [
       "gpcckkmippodnppallflahfabmeilgjg", // browser and unit tests
@@ -177,7 +177,7 @@
     "extension_types": "all"
   },
   "import": {
-    "channel": "dev",
+    "channel": "stable",
     "extension_types": "all"
   },
   "incognito": {
@@ -282,10 +282,6 @@
     "extension_types": ["extension"],
     "max_manifest_version": 1
   },
-  "page_launcher": {
-    "channel": "dev",
-    "extension_types": ["platform_app"]
-  },
   "permissions": {
     "channel": "stable",
     "extension_types": [
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index b6152bb..464645c 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -189,14 +189,16 @@
   ],
   "downloads": {
     "channel": "beta",
-    "extension_types": [
-      "extension"
-    ]
+    "extension_types": ["extension"]
   },
   "downloads.open": {
     "channel": "beta",
     "extension_types": ["extension"]
   },
+  "downloads.shelf": {
+    "channel": "beta",
+    "extension_types": ["extension"]
+  },
   "dial": {
     "channel": "stable",
     "extension_types": ["extension"],
@@ -293,6 +295,10 @@
     "channel": "stable",
     "extension_types": ["extension", "packaged_app", "platform_app"]
   },
+  "infobars": {
+    "channel": "dev",
+    "extension_types": ["extension", "packaged_app", "platform_app"]
+  },
   "input": {
     "channel": "stable",
     "extension_types": ["extension", "packaged_app"],
@@ -317,7 +323,7 @@
     {
       "channel": "dev",
       "extension_types": [
-        "extension", "packaged_app", "hosted_app", "platform_app"
+        "extension", "packaged_app", "platform_app"
       ]
     },
     {
@@ -455,7 +461,7 @@
   "power": {
     "channel": "stable",
     "extension_types": [
-      "extension", "hosted_app", "packaged_app", "platform_app"
+      "extension", "packaged_app", "platform_app"
     ]
   },
   "preferencesPrivate":  {
@@ -477,6 +483,13 @@
     "channel": "stable",
     "extension_types": ["extension", "platform_app"]
   },
+  "recoveryPrivate": {
+    "channel": "dev",
+    "extension_types": ["platform_app"],
+    "whitelist": [
+      "nmedaodmkamdcnmfceajookiaicfnkhd"
+    ]
+  },
   "rtcPrivate": {
     "channel": "stable",
     "extension_types": ["extension", "packaged_app"],
@@ -544,23 +557,15 @@
   },
   "system.cpu": {
     "channel": "dev",
-    "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
-    ],
-    "whitelist": [""]
+    "extension_types": ["extension", "packaged_app", "platform_app"]
   },
   "system.memory": {
     "channel": "dev",
-    "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
-    ],
-    "whitelist": [""]
+    "extension_types": ["extension", "packaged_app", "platform_app"]
   },
   "system.storage": {
     "channel": "stable",
-    "extension_types": [
-      "extension", "packaged_app", "platform_app"
-    ]
+    "extension_types": ["extension", "packaged_app", "platform_app"]
   },
   "system.display": {
     "channel": "stable",
@@ -636,17 +641,6 @@
     "channel": "stable",
     "extension_types": ["extension", "packaged_app"]
   },
-  "webSocketProxyPrivate": {
-    "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
-    "whitelist": [
-      "haiffjcadagjlijoggckpgfnoeiflnem",  // Citrix Receiver
-      "gnedhmakppccajfpfiihfcdlnpgomkcf",  // Citrix Receiver Beta
-      "fjcibdnjlbfnbfdjneajpipnlcppleek",  // Citrix Receiver Dev
-      "pnhechapfaindjhompbnflcldabbghjo",  // HTerm
-      "okddffdblfhhnmhodogpojmfkjmhinfp"   // HTerm dev
-    ]
-  },
   "webstorePrivate": {
     "channel": "stable",
     "extension_types": ["extension", "packaged_app"],
diff --git a/chrome/common/extensions/api/api.gyp b/chrome/common/extensions/api/api.gyp
index f18ff71..bc32cdd 100644
--- a/chrome/common/extensions/api/api.gyp
+++ b/chrome/common/extensions/api/api.gyp
@@ -30,6 +30,7 @@
           'bluetooth.idl',
           'bookmark_manager_private.json',
           'bookmarks.json',
+          'browsing_data.json',
           'chromeos_info_private.json',
           'cloud_print_private.json',
           'command_line_private.json',
@@ -51,7 +52,6 @@
           'experimental_history.json',
           'experimental_identity.idl',
           'experimental_idltest.idl',
-          'experimental_infobars.json',
           'location.idl',
           'experimental_media_galleries.idl',
           'experimental_record.json',
@@ -66,6 +66,7 @@
           'identity.idl',
           'identity_private.idl',
           'idle.json',
+          'infobars.json',
           'input_ime.json',
           'management.json',
           'manifest_types.json',
@@ -82,6 +83,7 @@
           'preferences_private.json',
           'power.idl',
           'push_messaging.idl',
+          'recovery_private.idl',
           'rtc_private.idl',
           'runtime.json',
           'serial.idl',
@@ -103,7 +105,7 @@
           'wallpaper_private.json',
           'web_navigation.json',
           'web_request.json',
-          'web_socket_proxy_private.json',
+          'webstore_private.json',
           'webview.json',
           'windows.json',
         ],
diff --git a/chrome/common/extensions/api/app_window.idl b/chrome/common/extensions/api/app_window.idl
index d4e1b40..227c38f 100644
--- a/chrome/common/extensions/api/app_window.idl
+++ b/chrome/common/extensions/api/app_window.idl
@@ -168,7 +168,7 @@
     [nodoc] static void setIcon(DOMString icon_url);
 
     // The JavaScript 'window' object for the created child.
-    [instanceOf=global] object contentWindow;
+    [instanceOf=Window] object contentWindow;
   };
 
   interface Functions {
diff --git a/chrome/common/extensions/api/downloads.idl b/chrome/common/extensions/api/downloads.idl
index 9de8e53..e13d3aa 100644
--- a/chrome/common/extensions/api/downloads.idl
+++ b/chrome/common/extensions/api/downloads.idl
@@ -111,7 +111,9 @@
   //     <dt>accepted</dt>
   //     <dd>The user has accepted the dangerous download.</dd>
   // </dl>
-  enum DangerType {file, url, content, uncommon, host, unwanted, safe, accepted};
+  enum DangerType {
+    file, url, content, uncommon, host, unwanted, safe, accepted
+  };
 
   // <dl><dt>in_progress</dt>
   //     <dd>The download is currently receiving data from the server.</dd>
@@ -206,6 +208,15 @@
     // $ref:search() may be called as often as necessary, but will not check for
     // file existence any more frequently than once every 10 seconds.
     boolean exists;
+
+    // The identifier for the extension that initiated this download if this
+    // download was initiated by an extension. Does not change once it is set.
+    DOMString? byExtensionId;
+
+    // The localized name of the extension that initiated this download if this
+    // download was initiated by an extension. May change if the extension
+    // changes its name or if the user changes their locale.
+    DOMString? byExtensionName;
   };
 
   [inline_doc] dictionary DownloadQuery {
@@ -480,6 +491,14 @@
     // Initiate dragging the downloaded file to another application. Call in a
     // javascript <code>ondragstart</code> handler.
     static void drag(long downloadId);
+
+    // Enable or disable the gray shelf at the bottom of every window associated
+    // with the current browser profile. The shelf will be disabled as long as
+    // at least one extension has disabled it. Enabling the shelf while at least
+    // one other extension has disabled it will return an error through
+    // $ref:runtime.lastError. Requires the <code>"downloads.shelf"</code>
+    // permission in addition to the <code>"downloads"</code> permission.
+    static void setShelfEnabled(boolean enabled);
   };
 
   interface Events {
diff --git a/chrome/common/extensions/api/extension.json b/chrome/common/extensions/api/extension.json
index a16b778..f2ba094 100644
--- a/chrome/common/extensions/api/extension.json
+++ b/chrome/common/extensions/api/extension.json
@@ -97,7 +97,7 @@
           "items": {
             "name": "viewGlobals",
             "type": "object",
-            "isInstanceOf": "global",
+            "isInstanceOf": "Window",
             "additionalProperties": { "type": "any" }
           }
         }
@@ -112,7 +112,7 @@
             "type": "object",
             "optional": true,
             "name": "backgroundPageGlobal",
-            "isInstanceOf": "global",
+            "isInstanceOf": "Window",
             "additionalProperties": { "type": "any" }
          }
       },
@@ -132,7 +132,7 @@
           "items": {
             "type": "object",
             "name": "tabGlobals",
-            "isInstanceOf": "global",
+            "isInstanceOf": "Window",
             "additionalProperties": { "type": "any" }
           }
         }
diff --git a/chrome/common/extensions/api/extension_api.cc b/chrome/common/extensions/api/extension_api.cc
index 4d0d45f..360db62 100644
--- a/chrome/common/extensions/api/extension_api.cc
+++ b/chrome/common/extensions/api/extension_api.cc
@@ -309,10 +309,6 @@
                                                 const Extension* extension,
                                                 Feature::Context context,
                                                 const GURL& url) {
-  std::string feature_type;
-  std::string feature_name;
-  SplitDependencyName(full_name, &feature_type, &feature_name);
-
   Feature* feature = GetFeatureDependency(full_name);
   CHECK(feature) << full_name;
 
diff --git a/chrome/common/extensions/api/extension_api_unittest.cc b/chrome/common/extensions/api/extension_api_unittest.cc
index a900ab2..89d6129 100644
--- a/chrome/common/extensions/api/extension_api_unittest.cc
+++ b/chrome/common/extensions/api/extension_api_unittest.cc
@@ -19,6 +19,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
+#include "chrome/common/extensions/extension_test_util.h"
 #include "chrome/common/extensions/features/api_feature.h"
 #include "chrome/common/extensions/features/base_feature_provider.h"
 #include "chrome/common/extensions/features/simple_feature.h"
@@ -27,6 +28,8 @@
 
 namespace extensions {
 
+using extension_test_util::BuildExtension;
+
 SimpleFeature* CreateAPIFeature() {
   return new APIFeature();
 }
@@ -310,10 +313,10 @@
 
   EXPECT_TRUE(apis->GetSchema("experimental.dns"));
   EXPECT_TRUE(apis->GetSchema("experimental.dns"));
-  EXPECT_TRUE(apis->GetSchema("experimental.infobars"));
-  EXPECT_TRUE(apis->GetSchema("experimental.infobars"));
   EXPECT_TRUE(apis->GetSchema("extension"));
   EXPECT_TRUE(apis->GetSchema("extension"));
+  EXPECT_TRUE(apis->GetSchema("infobars"));
+  EXPECT_TRUE(apis->GetSchema("infobars"));
   EXPECT_TRUE(apis->GetSchema("omnibox"));
   EXPECT_TRUE(apis->GetSchema("omnibox"));
   EXPECT_TRUE(apis->GetSchema("storage"));
@@ -480,7 +483,7 @@
   scoped_ptr<ExtensionAPI> extension_api(
       ExtensionAPI::CreateWithDefaultConfiguration());
 
-  // "runtime" should not be available in hosted apps.
+  // "runtime" and "tabs" should not be available in hosted apps.
   EXPECT_FALSE(extension_api->IsAvailable("runtime",
                                           extension.get(),
                                           Feature::BLESSED_EXTENSION_CONTEXT,
@@ -497,6 +500,10 @@
                                           extension.get(),
                                           Feature::BLESSED_EXTENSION_CONTEXT,
                                           GURL()).is_available());
+  EXPECT_FALSE(extension_api->IsAvailable("tabs.create",
+                                          extension.get(),
+                                          Feature::BLESSED_EXTENSION_CONTEXT,
+                                          GURL()).is_available());
 }
 
 TEST(ExtensionAPITest, AppAndFriendsAvailability) {
@@ -783,4 +790,83 @@
   EXPECT_EQ("fully.qualified.Type", type);
 }
 
+// Tests API availability with an empty manifest.
+TEST(ExtensionAPITest, NoPermissions) {
+  const struct {
+    const char* permission_name;
+    bool expect_success;
+  } kTests[] = {
+    // Test default module/package permission.
+    { "extension",      true },
+    { "i18n",           true },
+    { "permissions",    true },
+    { "runtime",        true },
+    { "test",           true },
+    // These require manifest keys.
+    { "browserAction",  false },
+    { "pageAction",     false },
+    { "pageActions",    false },
+    // Some negative tests.
+    { "bookmarks",      false },
+    { "cookies",        false },
+    { "history",        false },
+    // Make sure we find the module name after stripping '.'
+    { "runtime.abcd.onStartup",  true },
+    // Test Tabs functions.
+    { "tabs.create",      true },
+    { "tabs.duplicate",   true },
+    { "tabs.onRemoved",   true },
+    { "tabs.remove",      true },
+    { "tabs.update",      true },
+    { "tabs.getSelected", true },
+    { "tabs.onUpdated",   true },
+    // Test some whitelisted functions. These require no permissions.
+    { "app.getDetails",           true },
+    { "app.getDetailsForFrame",   true },
+    { "app.getIsInstalled",       true },
+    { "app.installState",         true },
+    { "app.runningState",         true },
+    { "management.getPermissionWarningsByManifest", true },
+    { "management.uninstallSelf", true },
+    // But other functions in those modules do.
+    { "management.getPermissionWarningsById", false },
+  };
+
+  scoped_ptr<ExtensionAPI> extension_api(
+      ExtensionAPI::CreateWithDefaultConfiguration());
+  scoped_refptr<Extension> extension =
+      BuildExtension(ExtensionBuilder().Pass()).Build();
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
+    EXPECT_EQ(kTests[i].expect_success,
+              extension_api->IsAvailable(kTests[i].permission_name,
+                                         extension.get(),
+                                         Feature::BLESSED_EXTENSION_CONTEXT,
+                                         GURL()).is_available())
+        << "Permission being tested: " << kTests[i].permission_name;
+  }
+}
+
+// Tests that permissions that require manifest keys are available when those
+// keys are present.
+TEST(ExtensionAPITest, ManifestKeys) {
+  scoped_ptr<ExtensionAPI> extension_api(
+      ExtensionAPI::CreateWithDefaultConfiguration());
+
+  scoped_refptr<Extension> extension =
+      BuildExtension(ExtensionBuilder().Pass())
+      .MergeManifest(DictionaryBuilder().Set("browser_action",
+                                             DictionaryBuilder().Pass()))
+      .Build();
+
+  EXPECT_TRUE(extension_api->IsAvailable("browserAction",
+                                         extension.get(),
+                                         Feature::BLESSED_EXTENSION_CONTEXT,
+                                         GURL()).is_available());
+  EXPECT_FALSE(extension_api->IsAvailable("pageAction",
+                                          extension.get(),
+                                          Feature::BLESSED_EXTENSION_CONTEXT,
+                                          GURL()).is_available());
+}
+
 }  // namespace extensions
diff --git a/chrome/common/extensions/api/feedback_private.idl b/chrome/common/extensions/api/feedback_private.idl
index 5d9116c..42ac6e7 100644
--- a/chrome/common/extensions/api/feedback_private.idl
+++ b/chrome/common/extensions/api/feedback_private.idl
@@ -68,6 +68,11 @@
     // Sends a feedback report.
     static void sendFeedback(FeedbackInfo feedback,
                              SendFeedbackCallback callback);
+
+    // Gets localized translated strings for feedback. It returns the
+    // strings as a dictionary mapping from string identifier to the
+    // translated string to use in the feedback app UI.
+    static void getStrings(GetStringsCallback callback);
   };
 
   interface Events {
diff --git a/chrome/common/extensions/api/file_browser_private.json b/chrome/common/extensions/api/file_browser_private.json
index b50edd7..54594d2 100644
--- a/chrome/common/extensions/api/file_browser_private.json
+++ b/chrome/common/extensions/api/file_browser_private.json
@@ -45,9 +45,9 @@
             "type": "boolean",
             "description": "Flag that specifies if volume is mounted in read-only mode."
           },
-          "totalSizeKB": {
-            "type": "integer",
-            "description": "Total disk volume size in KBs"
+          "totalSize": {
+            "type": "number",
+            "description": "Approximated total disk volume size in bytes."
           }
         }
       },
@@ -172,13 +172,13 @@
         "type": "object",
         "description": "Information about total and remaining size on the mount point.",
         "properties": {
-          "totalSizeKB": {
-            "type": "integer",
-            "description": "Total available size on the mount point."
+          "totalSize": {
+            "type": "number",
+            "description": "Approximate total available size on the mount point."
           },
-          "remainingSizeKB": {
-            "type": "integer",
-            "description": "Remaining available size on the mount point."
+          "remainingSize": {
+            "type": "number",
+            "description": "Approximate remaining available size on the mount point."
           }
         }
       },
diff --git a/chrome/common/extensions/api/file_system.idl b/chrome/common/extensions/api/file_system.idl
index 59d4870..07914a6 100644
--- a/chrome/common/extensions/api/file_system.idl
+++ b/chrome/common/extensions/api/file_system.idl
@@ -55,9 +55,18 @@
     // in the accepts argument. The default is true. If the accepts field is
     // unset or contains no valid entries, this will always be reset to true.
     boolean? acceptsAllTypes;
+
+    // Whether to accept multiple files. This is only supported for openFile and
+    // openWritableFile. The callback to chooseEntry will be called with a list
+    // of entries if this is set to true. Otherwise it will be called with a
+    // single Entry.
+    boolean? acceptsMultiple;
   };
   callback GetDisplayPathCallback = void (DOMString displayPath);
   callback FileEntryCallback = void ([instanceOf=FileEntry] object fileEntry);
+  callback FileEntriesCallback = void (
+      [instanceOf=FileEntry] optional object fileEntry,
+      [instanceOf=FileEntry] optional object[] fileEntries);
   callback IsWritableCallback = void (boolean isWritable);
   callback IsRestorableCallback = void (boolean isRestorable);
 
@@ -79,7 +88,7 @@
 
     // Ask the user to choose a file.
     static void chooseEntry(optional ChooseEntryOptions options,
-                            FileEntryCallback callback);
+                            FileEntriesCallback callback);
 
     // Returns the file entry with the given id if it can be restored. This call
     // will fail otherwise.
diff --git a/chrome/common/extensions/api/experimental_infobars.json b/chrome/common/extensions/api/infobars.json
similarity index 94%
rename from chrome/common/extensions/api/experimental_infobars.json
rename to chrome/common/extensions/api/infobars.json
index ec3cfdd..4842fe4 100644
--- a/chrome/common/extensions/api/experimental_infobars.json
+++ b/chrome/common/extensions/api/infobars.json
@@ -1,10 +1,10 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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.
 
 [
   {
-    "namespace": "experimental.infobars",
+    "namespace": "infobars",
     "description": "Use the <code>chrome.infobars</code> API to add a horizontal panel just above a tab's contents. See the screenshot below.",
     "compiler_options": {
       "implemented_in": "chrome/browser/infobars/infobar_extension_api.h"
diff --git a/chrome/common/extensions/api/location.idl b/chrome/common/extensions/api/location.idl
index 0900c8f..7797d12 100644
--- a/chrome/common/extensions/api/location.idl
+++ b/chrome/common/extensions/api/location.idl
@@ -3,9 +3,10 @@
 // found in the LICENSE file.
 // TODO(vadimt): Consider reusing WebKit/Blink types, if this is possible.
 
-// The <code>chrome.location</code> API augments the events from the
-// <a href="http://dev.w3.org/geo/api/spec-source.html">HTML Geolocation API</a>
-// to be event page compatible.
+// Use the <code>chrome.location</code> API to retrieve the geographic location
+// of the host machine. This API is a version of the <a
+// href="http://dev.w3.org/geo/api/spec-source.html">HTML Geolocation API</a>
+// that is compatible with event pages.
 namespace location {
   // Coordinates part of the Location object.
   dictionary Coordinates {
diff --git a/chrome/common/extensions/api/media_galleries_private.idl b/chrome/common/extensions/api/media_galleries_private.idl
index 22d0a8f..5f40bf7 100644
--- a/chrome/common/extensions/api/media_galleries_private.idl
+++ b/chrome/common/extensions/api/media_galleries_private.idl
@@ -88,8 +88,8 @@
     static void removeGalleryWatch(DOMString galleryId);
     static void getAllGalleryWatch(GetAllGalleryWatchCallback callback);
     static void removeAllGalleryWatch();
-    // The function has moved to experimental.systemInfo.storage namespace.
-    // See experimental_system_info_storage.idl.
+    // The function has moved to system.storage namespace.
+    // See system_storage.idl.
     static void ejectDevice(DOMString deviceId, EjectDeviceCallback callback);
     static void getHandlers(GetHandlersCallback callback);
   };
diff --git a/chrome/common/extensions/api/recovery_private.idl b/chrome/common/extensions/api/recovery_private.idl
new file mode 100644
index 0000000..54c8e03
--- /dev/null
+++ b/chrome/common/extensions/api/recovery_private.idl
@@ -0,0 +1,118 @@
+// Copyright 2013 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.
+
+// Use the <code>chrome.recovery</code> API to write images to
+// removable media.
+[nodoc] namespace recoveryPrivate {
+  // The different stages of a write call.
+  //
+  // <dl>
+  //    <dt>confirmation</dt>
+  //    <dd>The process starts by prompting the user for confirmation.</dd>
+  //    <dt>download</dt>
+  //    <dd>The image file is being download if a remote image was
+  //    requested.</dd>
+  //    <dt>verifyDownload</dt>
+  //    <dd>The download is being verified to match the image hash, if
+  //    provided</dd>
+  //    <dt>write</dt>
+  //    <dd>The image is being written to disk.</dd>
+  //    <dt>verifyWrite</dt>
+  //    <dt>The system is verifying that the written image matches the
+  //    downloaded image.</dd>
+  // <dl>
+  enum Stage {
+    confirmation,
+    download,
+    verifyDownload,
+    write,
+    verifyWrite
+  };
+
+  // Options for writing an image.
+  dictionary UrlWriteOptions {
+    // If present, verify that the downloaded image matches this hash.
+    DOMString? imageHash;
+    // If true, save the downloaded image as a file using the user's downloads
+    // preferences.
+    boolean? saveAsDownload;
+  };
+
+  dictionary ProgressInfo {
+    // The $ref:Stage that the write process is currently in.
+    Stage stage;
+    // Current progress within the stage.
+    long percentComplete;
+  };
+
+  callback WriteImageCallback = void ();
+  callback WriteCancelCallback = void ();
+  callback DestroyPartitionsCallback = void ();
+
+  interface Functions {
+    // Write an image to the disk downloaded from the provided URL.  The
+    // callback will be called when the entire operation completes, either
+    // successfully or on error.
+    //
+    // |storageUnitId|: The identifier for the storage unit, as provided by
+    // experimental.system_info.storage.
+    // |options|: If present the imageUrl of the options will be used to
+    // download the image.  Otherwise the user will be prompted for a local
+    // image to burn.
+    // |callback|: The callback which signifies that the write operation has
+    // been started by the system and provides a unique ID for this operation.
+    static void writeFromUrl(DOMString storageUnitId,
+                             DOMString imageUrl,
+                             optional UrlWriteOptions options,
+                             WriteImageCallback callback);
+
+    // Write an image to the disk, prompting the user to supply the image from
+    // a local file.  The callback will be called when the entire operation
+    // completes, either successfully or on error.
+    //
+    // |storageUnitId|: The identifier for the storage unit, as provided by
+    // experimental.system_info.storage.
+    // |options|: If present the imageUrl of the options will be used to
+    // download the image.  Otherwise the user will be prompted for a local
+    // image to burn.
+    // |callback|: The callback which signifies that the write operation has
+    // been started by the system and provides a unique ID for this operation.
+    static void writeFromFile(DOMString storageUnitId,
+                              WriteImageCallback callback);
+
+    // Cancel a current write operation.
+    //
+    // |callback|: The callback which is triggered with the write is
+    // successfully cancelled, passing the $ref:ProgressInfo of the operation at
+    // the time it was cancelled.
+    static boolean cancelWrite(WriteCancelCallback callback);
+
+    // Destroys the partition table of a disk, effectively erasing it.  This is
+    // a fairly quick operation and so it does not have complex stages or
+    // progress information.  However, it can fail and call the callback with
+    // an error.
+    //
+    // |storageUnitId|: The identifier of the storage unit to wipe, as provided
+    // by experimental.system_info.storage.
+    // |callback|: A callback which is called when the operation is complete.
+    static void destroyPartitions(DOMString storageUnitId,
+                                  DestroyPartitionsCallback callback);
+  };
+
+  interface Events {
+    // Fires periodically throughout the writing operation and at least once per
+    // stage.
+    static void onWriteProgress(ProgressInfo info);
+    // Fires when the write operation has completely finished, such as all
+    // devices being finalized and resources released.
+    static void onWriteComplete();
+    // Fires when an error occured during writing, passing the $ref:ProgressInfo
+    // of the operation at the time the error occured.
+    static void onWriteError(ProgressInfo info);
+    // Fires when an error occured while destroying partitions.
+    static void onDestroyPartitionsError();
+  };
+
+};
+
diff --git a/chrome/common/extensions/api/runtime.json b/chrome/common/extensions/api/runtime.json
index 58bfbff..26fc7ac 100644
--- a/chrome/common/extensions/api/runtime.json
+++ b/chrome/common/extensions/api/runtime.json
@@ -72,7 +72,7 @@
                 // for custom callbacks.
                 "optional": true,
                 "type": "object",
-                "isInstanceOf": "global",
+                "isInstanceOf": "Window",
                 "additionalProperties": { "type": "any" },
                 "description": "The JavaScript 'window' object for the background page."
               }
diff --git a/chrome/common/extensions/api/system_storage.idl b/chrome/common/extensions/api/system_storage.idl
index a682e9e..4ec51f6 100644
--- a/chrome/common/extensions/api/system_storage.idl
+++ b/chrome/common/extensions/api/system_storage.idl
@@ -17,7 +17,10 @@
   };
 
   dictionary StorageUnitInfo {
-    // The unique storage id. It will use the transient ID.
+    // The transient ID that uniquely identifies the storage device.
+    // This ID will be persistent within the same run of a single application.
+    // It will not be a persistent identifier between different runs of an
+    // application, or between different applications.
     DOMString id;
     // The name of the storage unit.
     DOMString name;
@@ -71,9 +74,6 @@
     static void getInfo(StorageInfoCallback callback);
 
     // Ejects a removable storage device.
-    // Note: We plan to move this function into a namespace that indicates it
-    // that modifies the state of the system rather than just gathering
-    // information.
     static void ejectDevice(DOMString id, EjectDeviceCallback callback);
 
     // Monitor a particular storage device available change capacity.
diff --git a/chrome/common/extensions/api/web_socket_proxy_private.json b/chrome/common/extensions/api/web_socket_proxy_private.json
deleted file mode 100644
index 3666079..0000000
--- a/chrome/common/extensions/api/web_socket_proxy_private.json
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2012 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.
-
-[
-  {
-    "namespace": "webSocketProxyPrivate",
-    "description": "none",
-    "functions": [
-      {
-        "name": "getPassportForTCP",
-        "description": "requests authorization token for websocket to TCP proxy.",
-        "parameters": [
-          {
-            "type": "string",
-            "name": "hostname",
-            "minLength": 1,
-            "description": "hostname to which TCP connection is requested."
-          },
-          {
-            "type": "integer",
-            "name": "port",
-            "minimum": 1,
-            "maximum": 65535,
-            "description": "TCP port number."
-          },
-          {
-            "type": "function",
-            "name": "callback",
-            "parameters": [
-              {
-                "type": "string",
-                "name": "passport",
-                "description": "Passport for passing to proxy."
-              }
-            ]
-          }
-        ]
-      },
-      {
-        "name": "getURLForTCP",
-        "description": "requests specific websocket URL that can be used as TCP proxy.",
-        "parameters": [
-          {
-            "type": "string",
-            "name": "hostname",
-            "minLength": 1,
-            "description": "hostname to which TCP connection is requested."
-          },
-          {
-            "type": "integer",
-            "name": "port",
-            "minimum": 1,
-            "maximum": 65535,
-            "description": "TCP port number."
-          },
-          {
-            "type": "object",
-            "name": "details",
-            "description": "Dictionary which contains requested parameters of connection",
-            "properties": {
-              "tls": {"type": "boolean", "optional": "true", "description": "whether TLS over TCP is requested"}
-            }
-          },
-          {
-            "type": "function",
-            "name": "callback",
-            "parameters": [
-              {
-                "type": "string",
-                "name": "url",
-                "description": "URL for opening as WebSocket."
-              }
-            ]
-          }
-        ]
-      }
-    ],
-    "events": []
-  }
-]
diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/background.js b/chrome/common/extensions/docs/examples/api/downloads/download_manager/background.js
index ffc2a4d..28e6075 100644
--- a/chrome/common/extensions/docs/examples/api/downloads/download_manager/background.js
+++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/background.js
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+if (chrome.downloads.setShelfEnabled)
+  chrome.downloads.setShelfEnabled(false);
+
 var colors = {
   progressColor: '#0d0',
   arrow: '#555',
diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json b/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json
index 85f2d0e..1141c0d 100644
--- a/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json
+++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json
@@ -11,4 +11,4 @@
    "default_popup": "popup.html"},
  "background": {"persistent": false, "scripts": ["background.js"]},
  "default_locale": "en",
- "permissions": ["management", "downloads", "downloads.open"]}
+ "permissions": ["management", "downloads", "downloads.open", "downloads.shelf"]}
diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.css b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.css
index 3624a86..46b0800 100644
--- a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.css
+++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.css
@@ -67,6 +67,7 @@
   float: right;
 }
 
+.by-ext img,
 svg {
   width: 2em;
   height: 2em;
diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.html b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.html
index 08bda79..b1cc635 100644
--- a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.html
+++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.html
@@ -51,6 +51,7 @@
       <ellipse cx="35" cy="50" rx="20" ry="20" />
       <ellipse cx="60" cy="50" rx="20" ry="20" />
       </g></svg></a>
+    <a href="#" class="by-ext"><img /></a>
     <a href="#" class="show-folder"><svg viewBox="0 0 100 100"><g>
       <rect x="5" y="5" rx="20" ry="20" width="90" height="90" class="border" />
       <polygon points="20,20 20,80 60,80 60,30 35,30 35,20" />
diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js
index 68212c9..e878fc7 100644
--- a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js
+++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js
@@ -177,6 +177,10 @@
     chrome.tabs.create({url: item.referrer});
     return false;
   };
+  item.getElement('by-ext').onclick = function() {
+    chrome.tabs.create({url: 'chrome://extensions#' + item.byExtensionId});
+    return false;
+  }
   item.getElement('open-filename').onclick = function() {
     item.open();
     return false;
@@ -317,6 +321,16 @@
   item.getElement('removed').innerText = item.basename;
   item.getElement('open-filename').innerText = item.basename;
 
+  if (item.byExtensionId && item.byExtensionName) {
+    item.getElement('by-ext').title = item.byExtensionName;
+    item.getElement('by-ext').href =
+      'chrome://extensions#' + item.byExtensionId;
+    item.getElement('by-ext img').src =
+      'chrome://extension-icon/' + item.byExtensionId + '/48/1';
+  } else {
+    item.getElement('by-ext').hidden = true;
+  }
+
   if (!item.getElement('error').hidden) {
     if (item.error) {
       // TODO(benjhayden) When https://codereview.chromium.org/16924017/ is
diff --git a/chrome/common/extensions/docs/examples/api/infobars/sandwichbar/background.js b/chrome/common/extensions/docs/examples/api/infobars/sandwichbar/background.js
index 9795d35..4b2bc5e 100644
--- a/chrome/common/extensions/docs/examples/api/infobars/sandwichbar/background.js
+++ b/chrome/common/extensions/docs/examples/api/infobars/sandwichbar/background.js
@@ -1,3 +1,7 @@
+// Copyright (c) 2013 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.
+
 /**
  * Handles requests sent by the content script.  Shows an infobar.
  */
@@ -7,7 +11,7 @@
   var url = "infobar.html#" + request.count;
 
   // Show the infobar on the tab where the request was sent.
-  chrome.experimental.infobars.show({
+  chrome.infobars.show({
     tabId: sender.tab.id,
     path: url
   });
diff --git a/chrome/common/extensions/docs/examples/api/infobars/sandwichbar/manifest.json b/chrome/common/extensions/docs/examples/api/infobars/sandwichbar/manifest.json
index 71af880..f14c8ad 100644
--- a/chrome/common/extensions/docs/examples/api/infobars/sandwichbar/manifest.json
+++ b/chrome/common/extensions/docs/examples/api/infobars/sandwichbar/manifest.json
@@ -5,7 +5,7 @@
   "background" : {
     "scripts": ["background.js"]
   },
-  "permissions" : [ "experimental" ],
+  "permissions" : [ "infobars" ],
   "icons" : {
     "16" : "sandwich-16.png",
     "48" : "sandwich-48.png",
diff --git a/chrome/common/extensions/docs/examples/api/nativeMessaging/README.txt b/chrome/common/extensions/docs/examples/api/nativeMessaging/README.txt
index 6719f44..5d5ba75 100644
--- a/chrome/common/extensions/docs/examples/api/nativeMessaging/README.txt
+++ b/chrome/common/extensions/docs/examples/api/nativeMessaging/README.txt
@@ -2,15 +2,18 @@
 messaging API that allows to communicate with a native application.
 
 In order for this example to work you must first install the native messaging
-host from the host directory. To install the host on Windows:
-  1. Replace HOST_PATH in host/com.google.chrome.example.echo.json with the full
-     path to host/native-messaging-example-host.bat
-  2. In the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts
-     add string value "com.google.chrome.example.echo" with full path to
-     host/com.google.chrome.example.echo.json .
+host from the host directory.
 
-Note that you need to have python installed.
+To install the host:
 
-On Mac and Linux you can use install_host.sh script in the host directory:
-  sudo host/install_host.sh
-You can later use host/uninstall_host.sh to uninstall the host.
+On Windows:
+  Add registry key
+  HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.google.chrome.example.echo
+  and set its default value to the full path to
+  host\com.google.chrome.example.echo-win.json . Note that you need to have
+  python installed.
+
+On Mac and Linux:
+  Run install_host.sh script in the host directory:
+    sudo host/install_host.sh
+  You can later use host/uninstall_host.sh to uninstall the host.
diff --git a/chrome/common/extensions/docs/examples/api/nativeMessaging/host/com.google.chrome.example.echo-win.json b/chrome/common/extensions/docs/examples/api/nativeMessaging/host/com.google.chrome.example.echo-win.json
new file mode 100644
index 0000000..84e5448
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/nativeMessaging/host/com.google.chrome.example.echo-win.json
@@ -0,0 +1,13 @@
+// Copyright 2013 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.
+
+{
+  "name": "com.google.chrome.example.echo",
+  "description": "Chrome Native Messaging API Example Host",
+  "path": "native-messaging-example-host.bat",
+  "type": "stdio",
+  "allowed_origins": [
+    "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"
+  ]
+}
diff --git a/chrome/common/extensions/docs/examples/extensions/buildbot/bg.js b/chrome/common/extensions/docs/examples/extensions/buildbot/bg.js
index cff9f8e..a351678 100644
--- a/chrome/common/extensions/docs/examples/extensions/buildbot/bg.js
+++ b/chrome/common/extensions/docs/examples/extensions/buildbot/bg.js
@@ -176,7 +176,9 @@
 
       issue.full_patchsets[patch.patchset] = patch;
 
-      // TODO(wittman): Revise to reduce load on the try servers.
+      // TODO(wittman): Revise to reduce load on the try servers. Repeatedly
+      // loading old try results increases the size of the working set of try
+      // jobs on the try servers, causing them to become disk-bound.
       // patch.try_job_results.forEach(function(results) {
       //   if (results.buildnumber) {
       //     tryJobResultsOutstanding++;
@@ -189,8 +191,11 @@
       //   }
       // });
 
-      if (++patchsetsRetrieved == issue.patchsets.length)
+      if (++patchsetsRetrieved == issue.patchsets.length) {
         updatedCallback(PATCHES_COMPLETE);
+        // TODO(wittman): Remove once we revise the try job fetching code.
+        updatedCallback(TRY_JOBS_COMPLETE);
+      }
     });
   });
 }
diff --git a/chrome/common/extensions/docs/examples/extensions/buildbot/manifest.json b/chrome/common/extensions/docs/examples/extensions/buildbot/manifest.json
index 3b0819a..1c1f5e0 100644
--- a/chrome/common/extensions/docs/examples/extensions/buildbot/manifest.json
+++ b/chrome/common/extensions/docs/examples/extensions/buildbot/manifest.json
@@ -1,6 +1,6 @@
 {
   "name": "Chromium Buildbot Monitor",
-  "version": "0.8.3",
+  "version": "0.8.4",
   "description": "Displays the status of the Chromium buildbot in the toolbar.  Click to see more detailed status in a popup.",
   "icons": { "128": "icon.png" },
   "background": {
diff --git a/chrome/common/extensions/docs/examples/extensions/gmail/background.js b/chrome/common/extensions/docs/examples/extensions/gmail/background.js
index fd66dcf..1a80009 100644
--- a/chrome/common/extensions/docs/examples/extensions/gmail/background.js
+++ b/chrome/common/extensions/docs/examples/extensions/gmail/background.js
@@ -7,7 +7,7 @@
 var canvas = document.getElementById('canvas');
 var loggedInImage = document.getElementById('logged_in');
 var canvasContext = canvas.getContext('2d');
-var pollIntervalMin = 5;  // 5 minutes
+var pollIntervalMin = 1;  // 1 minute
 var pollIntervalMax = 60;  // 1 hour
 var requestTimeout = 1000 * 2;  // 2 seconds
 var rotation = 0;
diff --git a/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/audio/forest.ogg b/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/audio/forest.ogg
deleted file mode 100644
index 903b0a4..0000000
--- a/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/audio/forest.ogg
+++ /dev/null
Binary files differ
diff --git a/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/audio/grandfather.ogg b/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/audio/grandfather.ogg
deleted file mode 100644
index 5eb7b64..0000000
--- a/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/audio/grandfather.ogg
+++ /dev/null
Binary files differ
diff --git a/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/audio/groove.ogg b/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/audio/groove.ogg
deleted file mode 100644
index c106544..0000000
--- a/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/audio/groove.ogg
+++ /dev/null
Binary files differ
diff --git a/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/credits.html b/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/credits.html
index a96f048..303b916 100644
--- a/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/credits.html
+++ b/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/credits.html
@@ -32,7 +32,6 @@
 <li>ClockStrikes12 from acclivity
 <li>Maxines from TexasMusicForge
 <li>fat_beat_1 from -zin-
-<li>morning_in_the_forest_2007_04_15 from reinsamba
 <li>20070812.rooster from dobroide
 </ul>
 
diff --git a/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/popup.html b/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/popup.html
index 1619949..cb82ed3 100644
--- a/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/popup.html
+++ b/chrome/common/extensions/docs/examples/extensions/talking_alarm_clock/popup.html
@@ -146,10 +146,7 @@
           <option value="none">None</option>
           <option selected value="cuckoo">Cuckoo Clock</option>
           <option value="digital">Digital Alarm Clock</option>
-          <option value="grandfather">Grandfather Clock</option>
-          <option value="groove">Groove</option>
           <option value="metal">Metal</option>
-          <option value="forest">Morning in the Forest</option>
           <option value="ringing">Ringing Alarm Clock</option>
           <option value="rooster">Rooster</option>
         </select>
diff --git a/chrome/common/extensions/docs/server2/api_list_data_source.py b/chrome/common/extensions/docs/server2/api_list_data_source.py
index 126d6cb..bddec89 100644
--- a/chrome/common/extensions/docs/server2/api_list_data_source.py
+++ b/chrome/common/extensions/docs/server2/api_list_data_source.py
@@ -18,37 +18,46 @@
   will contain non-API articles.
   """
   class Factory(object):
-    def __init__(self, compiled_fs_factory, api_path, public_path):
+    def __init__(self, compiled_fs_factory, file_system, api_path, public_path):
       self._compiled_fs = compiled_fs_factory.Create(self._ListAPIs,
                                                      APIListDataSource)
-      self._identity_fs = compiled_fs_factory.CreateIdentity(APIListDataSource)
+      self._file_system = file_system
       def Normalize(string):
         return string if string.endswith('/') else (string + '/')
       self._api_path = Normalize(api_path)
       self._public_path = Normalize(public_path)
 
     def _GetAPIsInSubdirectory(self, api_names, doc_type):
-      public_templates = self._identity_fs.GetFromFileListing(
-          '%s%s/' % (self._public_path, doc_type))
+      public_templates = []
+      for root, _, files in self._file_system.Walk(
+          self._public_path + doc_type):
+        public_templates.extend(
+            ('%s/%s' % (root, name)).lstrip('/') for name in files)
       template_names = set(os.path.splitext(name)[0]
                            for name in public_templates)
       experimental_apis = []
       chrome_apis = []
+      private_apis = []
       for template_name in sorted(template_names):
         if model.UnixName(template_name) not in api_names:
           continue
         entry = {'name': template_name.replace('_', '.')}
         if template_name.startswith('experimental'):
           experimental_apis.append(entry)
+        elif template_name.endswith('Private'):
+          private_apis.append(entry)
         else:
           chrome_apis.append(entry)
       if len(chrome_apis):
         chrome_apis[-1]['last'] = True
       if len(experimental_apis):
         experimental_apis[-1]['last'] = True
+      if len(private_apis):
+        private_apis[-1]['last'] = True
       return {
         'chrome': chrome_apis,
-        'experimental': experimental_apis
+        'experimental': experimental_apis,
+        'private': private_apis
       }
 
     def _ListAPIs(self, base_dir, apis):
@@ -68,7 +77,7 @@
   def GetAllNames(self):
     names = []
     for platform in ['apps', 'extensions']:
-      for category in ['chrome', 'experimental']:
+      for category in ['chrome', 'experimental', 'private']:
        names.extend(self.get(platform).get(category))
     return [api_name['name'] for api_name in names]
 
diff --git a/chrome/common/extensions/docs/server2/api_list_data_source_test.py b/chrome/common/extensions/docs/server2/api_list_data_source_test.py
index fbdf1eb..fdba6dc 100755
--- a/chrome/common/extensions/docs/server2/api_list_data_source_test.py
+++ b/chrome/common/extensions/docs/server2/api_list_data_source_test.py
@@ -24,8 +24,8 @@
     'browser_action.json',
     'experimental_bluetooth.idl',
     'experimental_history.idl',
-    'experimental_infobars.idl',
     'experimental_power.idl',
+    'infobars.idl',
     'something_internal.idl',
     'something_else_internal.json',
     'storage.json',
@@ -42,8 +42,8 @@
       'alarms.html',
       'browserAction.html',
       'experimental_history.html',
-      'experimental_infobars.html',
       'experimental_power.html',
+      'infobars.html',
       'storage.html',
     ],
   },
@@ -51,9 +51,11 @@
 
 class APIListDataSourceTest(unittest.TestCase):
   def setUp(self):
+    file_system = TestFileSystem(deepcopy(_TEST_DATA))
     self._factory = APIListDataSource.Factory(
-        CompiledFileSystem.Factory(TestFileSystem(deepcopy(_TEST_DATA)),
-                                   ObjectStoreCreator.ForTest()),
+        CompiledFileSystem.Factory(
+            file_system, ObjectStoreCreator.ForTest()),
+        file_system,
         'api',
         'public')
 
@@ -74,13 +76,13 @@
     api_list = self._factory.Create()
     self.assertEqual([{'name': 'alarms'},
                       {'name': 'browserAction'},
+                      {'name': 'infobars'},
                       {'name': 'storage', 'last': True}],
                      sorted(api_list.get('extensions').get('chrome')))
 
   def testExperimentalApps(self):
     api_list = self._factory.Create()
     self.assertEqual([{'name': 'experimental.history'},
-                      {'name': 'experimental.infobars'},
                       {'name': 'experimental.power', 'last': True}],
                      sorted(api_list.get('extensions').get('experimental')))
 
diff --git a/chrome/common/extensions/docs/server2/app.yaml b/chrome/common/extensions/docs/server2/app.yaml
index 3012f54..460c490c 100644
--- a/chrome/common/extensions/docs/server2/app.yaml
+++ b/chrome/common/extensions/docs/server2/app.yaml
@@ -1,5 +1,5 @@
 application: chrome-apps-doc
-version: 2-13-0
+version: 2-15-1
 runtime: python27
 api_version: 1
 threadsafe: false
diff --git a/chrome/common/extensions/docs/server2/branch_utility.py b/chrome/common/extensions/docs/server2/branch_utility.py
index 85e77e5..d449263 100644
--- a/chrome/common/extensions/docs/server2/branch_utility.py
+++ b/chrome/common/extensions/docs/server2/branch_utility.py
@@ -129,7 +129,7 @@
     if version == 'trunk':
       return 'trunk'
 
-    branch = self._branch_object_store.Get(version).Get()
+    branch = self._branch_object_store.Get(str(version)).Get()
     if branch is not None:
       return branch
 
diff --git a/chrome/common/extensions/docs/server2/chained_compiled_file_system_test.py b/chrome/common/extensions/docs/server2/chained_compiled_file_system_test.py
index face116..8c5b846 100755
--- a/chrome/common/extensions/docs/server2/chained_compiled_file_system_test.py
+++ b/chrome/common/extensions/docs/server2/chained_compiled_file_system_test.py
@@ -26,6 +26,8 @@
   },
 }
 
+identity = lambda _, x: x
+
 class ChainedCompiledFileSystemTest(unittest.TestCase):
   def setUp(self):
     object_store_creator = ObjectStoreCreator(start_empty=False)
@@ -38,9 +40,9 @@
     self._chained_factory = ChainedCompiledFileSystem.Factory(
         [(self._patched_factory, self._file_system),
          (self._base_factory, base_file_system)])
-    self._base_compiled_fs = self._base_factory.CreateIdentity(TestFileSystem)
-    self._chained_compiled_fs = self._chained_factory.CreateIdentity(
-        TestFileSystem)
+    self._base_compiled_fs = self._base_factory.Create(identity, TestFileSystem)
+    self._chained_compiled_fs = self._chained_factory.Create(
+        identity, TestFileSystem)
 
   def testGetFromFile(self):
     self.assertEqual(self._chained_compiled_fs.GetFromFile('a.txt'),
diff --git a/chrome/common/extensions/docs/server2/compiled_file_system.py b/chrome/common/extensions/docs/server2/compiled_file_system.py
index 29b1363..429d7f0 100644
--- a/chrome/common/extensions/docs/server2/compiled_file_system.py
+++ b/chrome/common/extensions/docs/server2/compiled_file_system.py
@@ -37,13 +37,6 @@
                                 create_object_store('file'),
                                 create_object_store('list'))
 
-    def CreateIdentity(self, cls):
-      '''Handy helper to get or create the identity compiled file system.
-      GetFromFile will return the file's contents.
-      GetFromFileListing will return the directory list.
-      '''
-      return self.Create(lambda _, x: x, cls)
-
   def __init__(self,
                file_system,
                populate_function,
diff --git a/chrome/common/extensions/docs/server2/compiled_file_system_test.py b/chrome/common/extensions/docs/server2/compiled_file_system_test.py
index 8923808..1d50feb 100755
--- a/chrome/common/extensions/docs/server2/compiled_file_system_test.py
+++ b/chrome/common/extensions/docs/server2/compiled_file_system_test.py
@@ -27,6 +27,8 @@
   }
 }
 
+identity = lambda _, x: x
+
 def _CreateFactory():
   return CompiledFileSystem.Factory(
       TestFileSystem(deepcopy(_TEST_DATA)),
@@ -35,37 +37,6 @@
                          disable_wrappers=True))
 
 class CompiledFileSystemTest(unittest.TestCase):
-  def testIdentityNamespace(self):
-    factory = _CreateFactory()
-    compiled_fs = factory.CreateIdentity(CompiledFileSystemTest)
-    self.assertEqual(
-        'class=CompiledFileSystem&'
-            'category=CompiledFileSystemTest/TestFileSystem/file&'
-            'app_version=%s' % GetAppVersion(),
-        compiled_fs._file_object_store.namespace)
-
-  def testIdentityFromFile(self):
-    compiled_fs = _CreateFactory().CreateIdentity(CompiledFileSystemTest)
-    self.assertEqual('404.html contents', compiled_fs.GetFromFile('404.html'))
-    self.assertEqual('a11y.html contents',
-                     compiled_fs.GetFromFile('apps/a11y.html'))
-    self.assertEqual('file.html contents',
-                     compiled_fs.GetFromFile('/apps/fakedir/file.html'))
-
-  def testIdentityFromFileListing(self):
-    compiled_fs = _CreateFactory().CreateIdentity(CompiledFileSystemTest)
-    self.assertEqual(set(('404.html',
-                          'apps/a11y.html',
-                          'apps/about_apps.html',
-                          'apps/fakedir/file.html',
-                          'extensions/activeTab.html',
-                          'extensions/alarms.html')),
-                     set(compiled_fs.GetFromFileListing('/')))
-    self.assertEqual(set(('a11y.html', 'about_apps.html', 'fakedir/file.html')),
-                     set(compiled_fs.GetFromFileListing('apps/')))
-    self.assertEqual(set(('file.html',)),
-                     set(compiled_fs.GetFromFileListing('apps/fakedir')))
-
   def testPopulateNamespace(self):
     def CheckNamespace(expected_file, expected_list, fs):
       self.assertEqual(expected_file, fs._file_object_store.namespace)
@@ -101,7 +72,7 @@
                      compiled_fs.GetFromFile('/apps/fakedir/file.html'))
 
   def testCaching(self):
-    compiled_fs = _CreateFactory().CreateIdentity(CompiledFileSystemTest)
+    compiled_fs = _CreateFactory().Create(identity, CompiledFileSystemTest)
     self.assertEqual('404.html contents', compiled_fs.GetFromFile('404.html'))
     self.assertEqual(set(('file.html',)),
                      set(compiled_fs.GetFromFileListing('apps/fakedir')))
@@ -118,7 +89,7 @@
                      set(compiled_fs.GetFromFileListing('apps/fakedir')))
 
   def testFailures(self):
-    compiled_fs = _CreateFactory().CreateIdentity(CompiledFileSystemTest)
+    compiled_fs = _CreateFactory().Create(identity, CompiledFileSystemTest)
     self.assertRaises(FileNotFoundError, compiled_fs.GetFromFile, '405.html')
     # TODO(kalman): would be nice to test this fails since apps/ is a dir.
     compiled_fs.GetFromFile('apps/')
diff --git a/chrome/common/extensions/docs/server2/cron.yaml b/chrome/common/extensions/docs/server2/cron.yaml
index 42377c2..1e33613 100644
--- a/chrome/common/extensions/docs/server2/cron.yaml
+++ b/chrome/common/extensions/docs/server2/cron.yaml
@@ -2,4 +2,4 @@
 - description: Repopulates all cached data.
   url: /_cron
   schedule: every 5 minutes
-  target: 2-13-0
+  target: 2-15-1
diff --git a/chrome/common/extensions/docs/server2/cron_servlet.py b/chrome/common/extensions/docs/server2/cron_servlet.py
index 14d4b39..15cd3d3 100644
--- a/chrome/common/extensions/docs/server2/cron_servlet.py
+++ b/chrome/common/extensions/docs/server2/cron_servlet.py
@@ -137,12 +137,12 @@
       # Extension examples have zip files too. Well, so do apps, but the app
       # file system doesn't get the Offline treatment so they don't need cron.
       if not IsDevServer():
-        manifest_json = '/manifest.json'
-        example_zips = [
-            '%s.zip' % filename[:-len(manifest_json)]
-            for filename in server_instance.content_cache.GetFromFileListing(
-                svn_constants.EXAMPLES_PATH)
-            if filename.endswith(manifest_json)]
+        manifest_json = 'manifest.json'
+        example_zips = []
+        for root, _, files in server_instance.host_file_system.Walk(
+            svn_constants.EXAMPLES_PATH):
+          example_zips.extend(
+              root + '.zip' for name in files if name == manifest_json)
         logging.info('cron: rendering %s example zips...' % len(example_zips))
         start_time = time.time()
         try:
diff --git a/chrome/common/extensions/docs/server2/example_zipper.py b/chrome/common/extensions/docs/server2/example_zipper.py
index f6160cd..b2a50fc 100644
--- a/chrome/common/extensions/docs/server2/example_zipper.py
+++ b/chrome/common/extensions/docs/server2/example_zipper.py
@@ -2,19 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import os
 from io import BytesIO
-import re
 from zipfile import ZipFile
 
-import compiled_file_system as compiled_fs
-
 class ExampleZipper(object):
   '''This class creates a zip file given a samples directory.
   '''
-  def __init__(self, compiled_fs_factory, base_path):
+  def __init__(self, compiled_fs_factory, file_system, base_path):
     self._base_path = base_path.rstrip('/')
-    self._file_cache = compiled_fs_factory.CreateIdentity(ExampleZipper)
+    self._file_system = file_system
     self._zip_cache = compiled_fs_factory.Create(self._MakeZipFile,
                                                  ExampleZipper)
 
@@ -26,7 +22,7 @@
     try:
       for file_name in files:
         file_path = '%s%s' % (base_dir, file_name)
-        file_contents = self._file_cache.GetFromFile(file_path, binary=True)
+        file_contents = self._file_system.ReadSingle(file_path, binary=True)
         if isinstance(file_contents, unicode):
           # Data is sometimes already cached as unicode.
           file_contents = file_contents.encode('utf8')
diff --git a/chrome/common/extensions/docs/server2/example_zipper_test.py b/chrome/common/extensions/docs/server2/example_zipper_test.py
index 43012c4..d38b604 100755
--- a/chrome/common/extensions/docs/server2/example_zipper_test.py
+++ b/chrome/common/extensions/docs/server2/example_zipper_test.py
@@ -21,6 +21,7 @@
         object_store_creator)
     self._example_zipper = ExampleZipper(
         CompiledFileSystem.Factory(self._file_system, object_store_creator),
+        self._file_system,
         'example_zipper')
 
   def testCreateZip(self):
diff --git a/chrome/common/extensions/docs/server2/file_system.py b/chrome/common/extensions/docs/server2/file_system.py
index 3226ebe..7f2d6c5 100644
--- a/chrome/common/extensions/docs/server2/file_system.py
+++ b/chrome/common/extensions/docs/server2/file_system.py
@@ -2,8 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import os
-
 class FileNotFoundError(Exception):
   def __init__(self, filename):
     Exception.__init__(self, filename)
@@ -79,19 +77,25 @@
     '''Recursively walk the directories in a file system, starting with root.
     Emulates os.walk from the standard os module.
     '''
-    if not root.endswith('/'):
-      root += '/'
+    basepath = root.rstrip('/') + '/'
 
-    dirs, files = [], []
+    def walk(root):
+      if not root.endswith('/'):
+        root += '/'
 
-    for f in self.ReadSingle(root):
-      if f.endswith('/'):
-        dirs.append(f)
-      else:
-        files.append(f)
+      dirs, files = [], []
 
-    yield (root.rstrip('/'), dirs, files)
+      for f in self.ReadSingle(root):
+        if f.endswith('/'):
+          dirs.append(f)
+        else:
+          files.append(f)
 
-    for d in dirs:
-      for walkinfo in self.Walk(root + d):
-        yield walkinfo
+      yield root[len(basepath):].rstrip('/'), dirs, files
+
+      for d in dirs:
+        for walkinfo in walk(root + d):
+          yield walkinfo
+
+    for walkinfo in walk(root):
+      yield walkinfo
diff --git a/chrome/common/extensions/docs/server2/file_system_test.py b/chrome/common/extensions/docs/server2/file_system_test.py
new file mode 100755
index 0000000..dc2d836
--- /dev/null
+++ b/chrome/common/extensions/docs/server2/file_system_test.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# Copyright 2013 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.
+
+import unittest
+
+from test_file_system import TestFileSystem
+
+file_system = TestFileSystem({
+  'templates': {
+    'public': {
+      'apps': {
+        '404.html': '',
+        'a11y.html': ''
+      },
+      'extensions': {
+        '404.html': '',
+        'cookies.html': ''
+      },
+      'redirects.json': 'redirect'
+    },
+    'json': {
+      'manifest.json': 'manifest'
+    }
+  }
+})
+
+class FileSystemTest(unittest.TestCase):
+  def testWalk(self):
+    expected_files = set([
+      'templates/public/apps/404.html',
+      'templates/public/apps/a11y.html',
+      'templates/public/extensions/404.html',
+      'templates/public/extensions/cookies.html',
+      'templates/public/redirects.json',
+      'templates/json/manifest.json'
+    ])
+
+    expected_dirs = set([
+      '/templates/',
+      'templates/public/',
+      'templates/public/apps/',
+      'templates/public/extensions/',
+      'templates/json/'
+    ])
+
+    all_files = set()
+    all_dirs = set()
+    for root, dirs, files in file_system.Walk(''):
+      all_files.update(root + '/' + name for name in files)
+      all_dirs.update(root + '/' + name for name in dirs)
+
+    self.assertEqual(expected_files, all_files)
+    self.assertEqual(expected_dirs, all_dirs)
+
+  def testSubWalk(self):
+    expected_files = set([
+      '/redirects.json',
+      'apps/404.html',
+      'apps/a11y.html',
+      'extensions/404.html',
+      'extensions/cookies.html'
+    ])
+
+    all_files = set()
+    for root, dirs, files in file_system.Walk('templates/public'):
+      all_files.update(root + '/' + name for name in files)
+
+    self.assertEqual(expected_files, all_files)
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/chrome/common/extensions/docs/server2/file_system_util.py b/chrome/common/extensions/docs/server2/file_system_util.py
index 4431e58..0882564 100644
--- a/chrome/common/extensions/docs/server2/file_system_util.py
+++ b/chrome/common/extensions/docs/server2/file_system_util.py
@@ -10,5 +10,5 @@
   '''
   for root, _, files in file_system.Walk(directory):
     for f in files:
-      url = posixpath.join(urlprefix, root[len(directory) + 1:], f)
-      yield url, '%s/%s' % (root, f)
+      url = posixpath.join(urlprefix, root, f)
+      yield url, posixpath.join(directory, root, f)
diff --git a/chrome/common/extensions/docs/server2/github_file_system.py b/chrome/common/extensions/docs/server2/github_file_system.py
index c0ffb1e..263397a 100644
--- a/chrome/common/extensions/docs/server2/github_file_system.py
+++ b/chrome/common/extensions/docs/server2/github_file_system.py
@@ -180,27 +180,29 @@
                                    username=USERNAME,
                                    password=PASSWORD)
     except urlfetch.DownloadError as e:
-      logging.error('GithubFileSystem Stat: %s' % e)
+      logging.warning('GithubFileSystem Stat: %s' % e)
       return self._DefaultStat(path)
+
     # Check if Github authentication failed.
     if result.status_code == 401:
-      logging.error('Github authentication failed for %s, falling back to '
-                    'unauthenticated.' % USERNAME)
+      logging.warning('Github authentication failed for %s, falling back to '
+                      'unauthenticated.' % USERNAME)
       try:
         result = self._fetcher.Fetch('commits/HEAD')
       except urlfetch.DownloadError as e:
-        logging.error('GithubFileSystem Stat: %s' % e)
+        logging.warning('GithubFileSystem Stat: %s' % e)
         return self._DefaultStat(path)
-    version = (json.loads(result.content).get('commit', {})
-                                         .get('tree', {})
-                                         .get('sha', None))
-    # Check if the JSON was valid, and set to 0 if not.
-    if version is not None:
+
+    # Parse response JSON - but sometimes github gives us invalid JSON.
+    try:
+      version = json.loads(result.content)['commit']['tree']['sha']
       self._stat_object_store.Set(path, version)
-    else:
-      logging.warning('Problem fetching commit hash from github.')
+      return StatInfo(version)
+    except StandardError as e:
+      logging.warning(
+          ('%s: got invalid or unexpected JSON from github. Response status ' +
+           'was %s, content %s') % (e, result.status_code, result.content))
       return self._DefaultStat(path)
-    return StatInfo(version)
 
   def GetIdentity(self):
     return '%s@%s' % (self.__class__.__name__, StringIdentity(self._url))
diff --git a/chrome/common/extensions/docs/server2/host_file_system_creator.py b/chrome/common/extensions/docs/server2/host_file_system_creator.py
index 381e664..956ce30 100644
--- a/chrome/common/extensions/docs/server2/host_file_system_creator.py
+++ b/chrome/common/extensions/docs/server2/host_file_system_creator.py
@@ -23,7 +23,7 @@
      # Provides custom create behavior, useful in tests.
      self._constructor_for_test = constructor_for_test
 
-  def Create(self, branch='trunk', revision=None):
+  def Create(self, branch='trunk', revision=None, offline=None):
     '''Creates either SVN file systems or specialized file systems from the
     constructor passed into this instance. Wraps the resulting file system in
     an Offline file system if the offline flag is set, and finally wraps it in a
@@ -34,7 +34,7 @@
     else:
       file_system = SubversionFileSystem.Create(branch=branch,
                                                 revision=revision)
-    if self._offline:
+    if offline or (offline is None and self._offline):
       file_system = OfflineFileSystem(file_system)
     return CachingFileSystem(file_system, self._object_store_creator)
 
diff --git a/chrome/common/extensions/docs/server2/link_error_detector.py b/chrome/common/extensions/docs/server2/link_error_detector.py
index a7e8942..7e4c137 100644
--- a/chrome/common/extensions/docs/server2/link_error_detector.py
+++ b/chrome/common/extensions/docs/server2/link_error_detector.py
@@ -118,7 +118,11 @@
     self._public_path = public_path
     self._pages = defaultdict(lambda: Page(404, (), (), ()))
     self._root_pages = frozenset(root_pages)
-    self._always_detached = frozenset(('apps/404.html', 'extensions/404.html'))
+    self._always_detached = frozenset((
+        'apps/404.html',
+        'extensions/404.html',
+        'apps/private_apis.html',
+        'extensions/private_apis.html'))
     self._redirection_whitelist = frozenset(('extensions/', 'apps/'))
 
     self._RenderAllPages()
@@ -183,7 +187,7 @@
       fragment = components.fragment
 
       if components.path == '':
-        if fragment == 'top':
+        if fragment == 'top' or fragment == '':
           continue
         if not fragment in page.anchors:
           broken_links.append((200, url, link, 'target anchor not found'))
@@ -202,13 +206,9 @@
                 url,
                 link,
                 'redirects to %s' % relink))
-
-          # target page with fragment is broken
-          elif fragment:
+          else:
             broken_links.append((
                 target_page.status, url, link, 'target page not found'))
-          else:
-            broken_links.append((target_page.status, url, link, ''))
 
         elif fragment:
           if not fragment in target_page.anchors:
@@ -242,7 +242,7 @@
     part of the connected component containing the |root_pages|. These pages
     are orphans and cannot be reached simply by clicking through the server.
     '''
-    pages_to_check = deque(self._root_pages)
+    pages_to_check = deque(self._root_pages.union(self._always_detached))
     found = set(self._root_pages) | self._always_detached
 
     while pages_to_check:
diff --git a/chrome/common/extensions/docs/server2/link_error_detector_test.py b/chrome/common/extensions/docs/server2/link_error_detector_test.py
index 4005015..dd8e17f 100755
--- a/chrome/common/extensions/docs/server2/link_error_detector_test.py
+++ b/chrome/common/extensions/docs/server2/link_error_detector_test.py
@@ -59,9 +59,10 @@
 
   def testGetBrokenLinks(self):
     expected_broken_links = set([
-      (404, 'apps/crx.html', 'apps/broken.html', ''),
-      (404, 'apps/index.html', 'apps/broken.json', ''),
-      (404, 'apps/unreachable.html', 'apps/invalid.html', ''),
+      (404, 'apps/crx.html', 'apps/broken.html', 'target page not found'),
+      (404, 'apps/index.html', 'apps/broken.json', 'target page not found'),
+      (404, 'apps/unreachable.html', 'apps/invalid.html',
+          'target page not found'),
       (404, 'apps/devtools_events.html', 'apps/fake.html#invalid',
           'target page not found'),
       (200, 'apps/devtools_events.html', 'apps/index.html#invalid',
diff --git a/chrome/common/extensions/docs/server2/patch_servlet.py b/chrome/common/extensions/docs/server2/patch_servlet.py
index 8207e1d..a394a17 100644
--- a/chrome/common/extensions/docs/server2/patch_servlet.py
+++ b/chrome/common/extensions/docs/server2/patch_servlet.py
@@ -33,10 +33,12 @@
     branch_utility = self._delegate.CreateBranchUtility(object_store_creator)
     host_file_system_creator = self._delegate.CreateHostFileSystemCreator(
         object_store_creator)
-    # TODO(fj): Use OfflineFileSystem here once all json/idl files in api/
-    # are pulled into data store by cron jobs.
-    base_file_system = CachingFileSystem(host_file_system_creator.Create(),
-                                         object_store_creator)
+    # offline=False because a patch can rely on files that are already in SVN
+    # repository but not yet pulled into data store by cron jobs (a typical
+    # example is to add documentation for an existing API).
+    base_file_system = CachingFileSystem(
+        host_file_system_creator.Create(offline=False),
+        object_store_creator)
     base_compiled_fs_factory = CompiledFileSystem.Factory(base_file_system,
                                                           object_store_creator)
 
diff --git a/chrome/common/extensions/docs/server2/redirector.py b/chrome/common/extensions/docs/server2/redirector.py
index 8c1d890..7f96090 100644
--- a/chrome/common/extensions/docs/server2/redirector.py
+++ b/chrome/common/extensions/docs/server2/redirector.py
@@ -72,4 +72,5 @@
     '''
     for root, dirs, files in self._file_system.Walk(self._root_path + '/'):
       if 'redirects.json' in files:
-        self._cache.GetFromFile('%s/redirects.json' % root)
+        self._cache.GetFromFile('%s/redirects.json' % posixpath.join(
+            self._root_path, root))
diff --git a/chrome/common/extensions/docs/server2/redirector_test.py b/chrome/common/extensions/docs/server2/redirector_test.py
index 713d6e4..0c2bdb4 100755
--- a/chrome/common/extensions/docs/server2/redirector_test.py
+++ b/chrome/common/extensions/docs/server2/redirector_test.py
@@ -73,8 +73,8 @@
         '/index.html', redirector.Redirect(HOST, 'apps/'))
 
   def testNotFound(self):
-    self.assertIsNone(redirector.Redirect(HOST, 'not/a/real/path'))
-    self.assertIsNone(redirector.Redirect(HOST, 'public/apps/okay.html'))
+    self.assertEqual(None, redirector.Redirect(HOST, 'not/a/real/path'))
+    self.assertEqual(None, redirector.Redirect(HOST, 'public/apps/okay.html'))
 
   def testOldHosts(self):
     self.assertEqual(
diff --git a/chrome/common/extensions/docs/server2/render_servlet.py b/chrome/common/extensions/docs/server2/render_servlet.py
index 77245d6..e9adcab 100644
--- a/chrome/common/extensions/docs/server2/render_servlet.py
+++ b/chrome/common/extensions/docs/server2/render_servlet.py
@@ -66,13 +66,13 @@
         content_type = 'application/zip'
       elif path.startswith('extensions/examples/'):
         mimetype = mimetypes.guess_type(path)[0] or 'text/plain'
-        content = server_instance.content_cache.GetFromFile(
+        content = server_instance.host_file_system.ReadSingle(
             '%s/%s' % (svn_constants.DOCS_PATH, path[len('extensions/'):]),
             binary=_IsBinaryMimetype(mimetype))
         content_type = mimetype
       elif path.startswith('static/'):
         mimetype = mimetypes.guess_type(path)[0] or 'text/plain'
-        content = server_instance.content_cache.GetFromFile(
+        content = server_instance.host_file_system.ReadSingle(
             ('%s/%s' % (svn_constants.DOCS_PATH, path)),
             binary=_IsBinaryMimetype(mimetype))
         content_type = mimetype
diff --git a/chrome/common/extensions/docs/server2/server_instance.py b/chrome/common/extensions/docs/server2/server_instance.py
index 6290f18..ded9c1c 100644
--- a/chrome/common/extensions/docs/server2/server_instance.py
+++ b/chrome/common/extensions/docs/server2/server_instance.py
@@ -50,6 +50,7 @@
 
     self.api_list_data_source_factory = APIListDataSource.Factory(
         self.compiled_host_fs_factory,
+        self.host_file_system,
         svn_constants.API_PATH,
         svn_constants.PUBLIC_TEMPLATE_PATH)
 
@@ -118,14 +119,11 @@
 
     self.example_zipper = ExampleZipper(
         self.compiled_host_fs_factory,
+        self.host_file_system,
         svn_constants.DOCS_PATH)
 
     self.path_canonicalizer = PathCanonicalizer(self.compiled_host_fs_factory)
 
-    # TODO(kalman): delete content cache.
-    self.content_cache = self.compiled_host_fs_factory.CreateIdentity(
-        ServerInstance)
-
     self.redirector = Redirector(
         self.compiled_host_fs_factory,
         self.host_file_system,
diff --git a/chrome/common/extensions/docs/server2/test_data/file_system/stat b/chrome/common/extensions/docs/server2/test_data/file_system/stat
index 2c64a3f..df42b24 100644
--- a/chrome/common/extensions/docs/server2/test_data/file_system/stat
+++ b/chrome/common/extensions/docs/server2/test_data/file_system/stat
@@ -1029,16 +1029,16 @@
 <tr class="vc_row_odd">
 <td colspan="2">
 
-<a name="experimental_infobars.json" href="/viewvc/chrome/trunk/src/chrome/common/extensions/api/experimental_infobars.json?view=log" title="View file revision log">
+<a name="infobars.json" href="/viewvc/chrome/trunk/src/chrome/common/extensions/api/infobars.json?view=log" title="View file revision log">
 
 <img src="/viewvc/*docroot*/images/text.png" alt="" width="16" height="16" />
-experimental_infobars.json</a>
+infobars.json</a>
 
 </td>
 
 
 
-<td>&nbsp;<a href="/viewvc/chrome/trunk/src/chrome/common/extensions/api/experimental_infobars.json?revision=136747&amp;view=markup" title="View file contents"><strong>136747</strong></a></td>
+<td>&nbsp;<a href="/viewvc/chrome/trunk/src/chrome/common/extensions/api/infobars.json?revision=136747&amp;view=markup" title="View file contents"><strong>136747</strong></a></td>
 
 <td>&nbsp;2 months</td>
 <td>&nbsp;cduvall@chromium.org</td>
@@ -2378,32 +2378,6 @@
 
 </tr>
 
-<tr class="vc_row_even">
-<td colspan="2">
-
-<a name="web_socket_proxy_private.json" href="/viewvc/chrome/trunk/src/chrome/common/extensions/api/web_socket_proxy_private.json?view=log" title="View file revision log">
-
-<img src="/viewvc/*docroot*/images/text.png" alt="" width="16" height="16" />
-web_socket_proxy_private.json</a>
-
-</td>
-
-
-
-<td>&nbsp;<a href="/viewvc/chrome/trunk/src/chrome/common/extensions/api/web_socket_proxy_private.json?revision=136747&amp;view=markup" title="View file contents"><strong>136747</strong></a></td>
-
-<td>&nbsp;2 months</td>
-<td>&nbsp;cduvall@chromium.org</td>
-
-
-<td>&nbsp;Files generated by the JSON schema compiler are named incorrectly
-
-Files are now...</td>
-
-
-
-</tr>
-
 <tr class="vc_row_odd">
 <td colspan="2">
 
@@ -2505,5 +2479,3 @@
 </table>
 </body>
 </html>
-
-
diff --git a/chrome/common/extensions/docs/server2/test_data/file_system/stat_result.json b/chrome/common/extensions/docs/server2/test_data/file_system/stat_result.json
index 75f0c11..5dc31a8 100644
--- a/chrome/common/extensions/docs/server2/test_data/file_system/stat_result.json
+++ b/chrome/common/extensions/docs/server2/test_data/file_system/stat_result.json
@@ -34,7 +34,6 @@
   "runtime.json": "148956",
   "alarms.idl": "143049",
   "declarative_web_request.json": "150421",
-  "web_socket_proxy_private.json": "136747",
   "devtools/": "149291",
   "experimental_idltest.idl": "138707",
   "script_badge.json": "146930",
@@ -83,6 +82,6 @@
   "socket.idl": "150190",
   "OWNERS": "150079",
   "cookies.json": "124878",
-  "experimental_infobars.json": "136747",
+  "infobars.json": "136747",
   "wallpaper_private.json": "148400"
 }
diff --git a/chrome/common/extensions/docs/templates/articles/analytics.html b/chrome/common/extensions/docs/templates/articles/analytics.html
new file mode 100644
index 0000000..42e1941
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/articles/analytics.html
@@ -0,0 +1,74 @@
+<h1>Analytics</h1>
+
+<table class="intro">
+  <tr>
+    <td><strong>Description:</strong></td>
+    <td>Use the Chrome Platform Analytics JavaScript library to measure user interactions with your app.</td>
+  </tr>
+  <tr>
+    <td><strong>Samples:</strong></td>
+    <td><a href="https://github.com/GoogleChrome/chrome-platform-analytics/tree/master/src/example">JavaScript and Closure examples</a></td>
+  </tr>
+  <tr>
+    <td><strong>Learn&nbsp;more:</strong></td>
+    <td>
+    <a href="https://github.com/GoogleChrome/chrome-platform-analytics/wiki">Chrome Platform Analytics</a></td>
+  </tr>
+</table>
+
+<h2 id="overview">Overview</h2>
+
+<p>
+The <a href="https://developers.google.com/analytics/devguides/platform/">Google Analytics Platform</a> lets you
+measure user interactions with your business across various devices and environments. The platform provides resources to
+collect, store, process, and report on user interactions.
+<a href="https://github.com/GoogleChrome/chrome-platform-analytics/wiki">Chrome Platform Analytics</a> (CPA) is
+a client library that lets you collect user interactions in Packaged Apps and Extensions,
+and send them to the Google Analytics Platform.
+</p>
+
+<p>
+The CPA library follows the basic usage pattern of <a href="http://www.google.com/analytics/index.html">Google Analytics</a>
+and will feel familiar to existing users of Google Analytics.
+However, unlike the web-centric official Google Analytics JavaScript libraries, the CPA library uses an "app-centric" model
+that is better suited for Packaged Apps. Among other features, the library provides methods for specific types of hits,
+such as "event" and "appView", and it can be used in both Closure-based and traditional JavaScript projects.
+</p>
+
+<h2 id="privacy">Privacy</h2>
+
+<p>
+<a href="https://developers.google.com/analytics/devguides/collection/protocol/policy">Google Analytics Policies</a>
+require you to give users notice about analytics tracking and the option to opt out of tracking.
+The CPA library makes it easy to build privacy-friendly apps by providing library-level support for user opt-out.
+</p>
+
+<h2 id="library">Library</h2>
+
+<p>
+The CPA library is hosted on GitHub: <a href="https://github.com/GoogleChrome/chrome-platform-analytics">chrome-platform-analytics</a>.
+</p>
+
+<p>
+The library is <a href="https://github.com/GoogleChrome/chrome-platform-analytics/blob/master/google-analytics-bundle.js">google-analytics-bundle.js</a>.
+</p>
+
+<h2 id="documentation">Documentation</h2>
+
+<p>
+Documentation for the CPA library is aslo on GitHub. See specifically:
+</p>
+
+<ul>
+  <li><a href="https://github.com/GoogleChrome/chrome-platform-analytics/wiki#prerequisites">Prerequisites</a></li>
+  <li><a href="https://github.com/GoogleChrome/chrome-platform-analytics/wiki#api-reference">API Reference</a></li>
+  <li><a href="https://github.com/GoogleChrome/chrome-platform-analytics/wiki#how-to">How-To</a></li>
+</ul>
+
+
+<h2 id="examples">Examples</h2>
+
+<p>
+For an illustration of how to use the CPA library to measure user interactions in an app, see the 
+<a href="https://github.com/GoogleChrome/chrome-platform-analytics/tree/master/src/example">examples</a>.
+</p>
diff --git a/chrome/common/extensions/docs/templates/articles/api_index.html b/chrome/common/extensions/docs/templates/articles/api_index.html
index 3b1f7b9..f9a1232 100644
--- a/chrome/common/extensions/docs/templates/articles/api_index.html
+++ b/chrome/common/extensions/docs/templates/articles/api_index.html
@@ -23,11 +23,11 @@
   {{?is_apps}}
   {{#api_list.apps.chrome}}
   <li><a href="{{name}}.html">{{name}}</a></li>
-  {{/}}
+  {{/api_list.apps.chrome}}
   {{:is_apps}}
   {{#api_list.extensions.chrome}}
   <li><a href="{{name}}.html">{{name}}</a></li>
-  {{/}}
+  {{/api_list.extensions.chrome}}
   {{/is_apps}}
 </ul>
 
diff --git a/chrome/common/extensions/docs/templates/articles/experimental.html b/chrome/common/extensions/docs/templates/articles/experimental.html
index c01521d..e02304e 100644
--- a/chrome/common/extensions/docs/templates/articles/experimental.html
+++ b/chrome/common/extensions/docs/templates/articles/experimental.html
@@ -28,7 +28,6 @@
 Pay special attention to the following APIs,
 which we expect to finalize soon:
 <b>devtools</b>,
-<b>infobars</b>,
 <b>permissions</b>,
 For examples of using the experimental APIs, see
 <a href="samples.html#experimental">Samples</a>.
diff --git a/chrome/common/extensions/docs/templates/articles/manifest.html b/chrome/common/extensions/docs/templates/articles/manifest.html
index 75344ac..290094a 100644
--- a/chrome/common/extensions/docs/templates/articles/manifest.html
+++ b/chrome/common/extensions/docs/templates/articles/manifest.html
@@ -189,7 +189,7 @@
 You can also specify a 16x16 icon to be used as the favicon
 for an extension's pages.
 The 16x16 icon is also displayed in the experimental extension
-<a href="experimental.infobars.html">infobar</a>
+<a href="infobars.html">infobar</a>
 feature.
 </p>
 
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/icons.html b/chrome/common/extensions/docs/templates/articles/manifest/icons.html
index 5432af4..37eb2af 100644
--- a/chrome/common/extensions/docs/templates/articles/manifest/icons.html
+++ b/chrome/common/extensions/docs/templates/articles/manifest/icons.html
@@ -10,7 +10,7 @@
 You can also specify a 16x16 icon to be used as the favicon
 for an extension's pages.
 The 16x16 icon is also displayed in the experimental extension
-<a href="http://developer.chrome.com/extensions/experimental.infobars.html">infobar</a>
+<a href="http://developer.chrome.com/extensions/infobars.html">infobar</a>
 feature.
 </p>
 
diff --git a/chrome/common/extensions/docs/templates/articles/messaging.html b/chrome/common/extensions/docs/templates/articles/messaging.html
index 5fea2b3..fe6b4c2 100644
--- a/chrome/common/extensions/docs/templates/articles/messaging.html
+++ b/chrome/common/extensions/docs/templates/articles/messaging.html
@@ -316,7 +316,9 @@
   </tr>
   <tr>
     <td><code>path</code></td>
-    <td>Path to the native messaging host binary.</td>
+    <td>Path to the native messaging host binary. On Linux and OSX the path must
+    be absolute. On Windows it can be relative to the directory in which the
+    manifest file is located.</td>
   </tr>
   <tr>
     <td><code>type</code></td>
diff --git a/chrome/common/extensions/docs/templates/articles/private_apis.html b/chrome/common/extensions/docs/templates/articles/private_apis.html
new file mode 100644
index 0000000..ef97db4
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/articles/private_apis.html
@@ -0,0 +1,15 @@
+<h1 class="page_title">Private APIs</h1>
+
+<p>These are only usable by extensions bundled with Chrome, they are not publicly accessible.</p>
+
+<ul>
+{{?is_apps}}
+{{#api_list.apps.private}}
+<li><a href="{{name}}.html">{{name}}</a></li>
+{{/api_list.apps.private}}
+{{:is_apps}}
+{{#api_list.extensions.private}}
+<li><a href="{{name}}.html">{{name}}</a></li>
+{{/api_list.extensions.private}}
+{{/is_apps}}
+</ul>
diff --git a/chrome/common/extensions/docs/templates/articles/publish_app.html b/chrome/common/extensions/docs/templates/articles/publish_app.html
index 34defda..f54f2bf 100644
--- a/chrome/common/extensions/docs/templates/articles/publish_app.html
+++ b/chrome/common/extensions/docs/templates/articles/publish_app.html
@@ -3,10 +3,9 @@
 <p>Packaged apps are published in the same way as other types of apps in the Chrome Web Store.
 For detailed instructions, see <a href="https://developers.google.com/chrome/web-store/docs/publish">Publishing Your App</a>.</p>
 
-<p><strong>Note regarding visibility of packaged apps:</strong><br />
-<ul>
-  <li>Only users with Chrome 28 or later can see packaged apps in the Chrome Web Store. Chrome 28 is currently in the
-  <a href="https://www.google.com/intl/en/chrome/browser/beta.html">beta channel</a>, but it will be pushed to the stable channel
-  and be available to all users soon.</li>
-  <li>Developers can share direct links to packaged apps with all users.</li>
-</ul>
+<p><strong>Tip:</strong>
+If your app uses <a href="https://developers.google.com/native-client/">Native Client</a>,
+you can structure your application directory hierarchy and zip file in a way that reduces the size of the user download package.
+For details, see
+<a href="https://developers.google.com/native-client/dev/devguide/distributing#multi-platform-zip">Reducing the size of the user download package</a>.
+</p>
diff --git a/chrome/common/extensions/docs/templates/intros/downloads.html b/chrome/common/extensions/docs/templates/intros/downloads.html
index 1e576ba..559c039 100644
--- a/chrome/common/extensions/docs/templates/intros/downloads.html
+++ b/chrome/common/extensions/docs/templates/intros/downloads.html
@@ -1,17 +1,25 @@
 <h2 id='manifest'>Manifest</h2>
 
 <p> You must declare the 'downloads' permission in the <a
-href='manifest.html'>extension manifest</a> to use this API.</p>
+href='manifest.html'>extension manifest</a> to use this API, along with <a
+href='declare_permissions.html'>host permissions</a> for any hosts that you may
+pass to $ref:downloads.download.</p>
 
 <pre>{
   'name': 'My extension',
   ...
 <b>  'permissions': [
     'downloads',
+    '*://*.google.com'
   ]</b>,
   ...
 }</pre>
 
+<p>If the URL's hostname is not specified in the permissions, then
+$ref:downloads.download will call its callback with a null
+<code>downloadId</code> and set the $ref:runtime.lastError object to indicate
+that the extension does not have permission to access that hostname.</p>
+
 <h2 id='examples'>Examples</h2>
 
 <p>You can find simple examples of using the downloads module in the <a
diff --git a/chrome/common/extensions/docs/templates/intros/experimental_infobars.html b/chrome/common/extensions/docs/templates/intros/infobars.html
similarity index 84%
rename from chrome/common/extensions/docs/templates/intros/experimental_infobars.html
rename to chrome/common/extensions/docs/templates/intros/infobars.html
index d3d0476..e3bba7d 100644
--- a/chrome/common/extensions/docs/templates/intros/experimental_infobars.html
+++ b/chrome/common/extensions/docs/templates/intros/infobars.html
@@ -28,9 +28,8 @@
 <h2 id="manifest">Manifest</h2>
 
 <p>
-The infobars API is currently
-experimental, so you must declare the "experimental"
-permission to use it. Also, you should specify
+The infobars API is avaiable under "infobars"
+permission and dev channel only. Also, you should specify
 a 16x16-pixel icon for display next to your infobar.
 For example:
 </p>
@@ -38,11 +37,11 @@
 <pre>{
   "name": "Andy's infobar extension",
   "version": "1.0",
-  <b>"permissions": ["experimental"],</b>
+  <b>"permissions": ["infobars"],</b>
   <b>"icons": {</b>
     <b>"16": "16.png"</b>
   <b>},</b>
   "background": {
     "scripts": ["background.js"]
   }
-}</pre>
\ No newline at end of file
+}</pre>
diff --git a/chrome/common/extensions/docs/templates/intros/location.html b/chrome/common/extensions/docs/templates/intros/location.html
new file mode 100644
index 0000000..95b024e
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/intros/location.html
@@ -0,0 +1,24 @@
+<h2 id="overview">Overview</h2>
+<p>
+  To start retrieving location information, you need to create a location watch
+  by calling $ref:location.watchLocation, passing in
+  the location watch details via the <code>requestInfo</code> parameter:
+</p>
+
+<pre>chrome.location.watchLocation(name, requestInfo);</pre>
+<p>
+  You also need to add a listener
+  for the $ref:location.onLocationUpdate event.
+  Chrome will fire this event after you create your location watch, and
+  it will keep firing the event every time the geographic
+  location of the host machine changes, until you call $ref:location.clearWatch.
+</p>
+<p>
+  Here's sample code
+  to listen for location updates:
+</p>
+<pre>
+  chrome.location.onLocationUpdate.addListener(function(position) {
+    console.log(JSON.stringify(position));
+  });
+</pre>
diff --git a/chrome/common/extensions/docs/templates/json/apps_sidenav.json b/chrome/common/extensions/docs/templates/json/apps_sidenav.json
index 0df9706..5b7c4a9 100644
--- a/chrome/common/extensions/docs/templates/json/apps_sidenav.json
+++ b/chrome/common/extensions/docs/templates/json/apps_sidenav.json
@@ -180,6 +180,10 @@
     "title": "Google Services",
     "items": [
       {
+        "title": "Analytics",
+        "href": "/apps/analytics.html"
+      },
+      {
         "title": "In-App Payments",
         "href": "/apps/in_app_payments.html"
       }
diff --git a/chrome/common/extensions/docs/templates/public/apps/analytics.html b/chrome/common/extensions/docs/templates/public/apps/analytics.html
new file mode 100644
index 0000000..b747699
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/apps/analytics.html
@@ -0,0 +1 @@
+{{+partials.standard_apps_article article:intros.analytics}}
diff --git a/chrome/common/extensions/docs/templates/public/apps/location.html b/chrome/common/extensions/docs/templates/public/apps/location.html
new file mode 100644
index 0000000..3666d4c
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/apps/location.html
@@ -0,0 +1 @@
+{{+partials.standard_apps_api api:apis.location intro:intros.location}}
diff --git a/chrome/common/extensions/docs/templates/public/apps/private_apis.html b/chrome/common/extensions/docs/templates/public/apps/private_apis.html
new file mode 100644
index 0000000..0e15873
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/apps/private_apis.html
@@ -0,0 +1 @@
+{{+partials.standard_apps_article article:intros.private_apis is_apps:true}}
diff --git a/chrome/common/extensions/docs/templates/public/extensions/experimental_infobars.html b/chrome/common/extensions/docs/templates/public/extensions/experimental_infobars.html
deleted file mode 100644
index 194ecf3..0000000
--- a/chrome/common/extensions/docs/templates/public/extensions/experimental_infobars.html
+++ /dev/null
@@ -1 +0,0 @@
-{{+partials.standard_extensions_api api:apis.experimental_infobars intro:intros.experimental_infobars}}
diff --git a/chrome/common/extensions/docs/templates/public/extensions/infobars.html b/chrome/common/extensions/docs/templates/public/extensions/infobars.html
new file mode 100644
index 0000000..009f9d9
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/extensions/infobars.html
@@ -0,0 +1 @@
+{{+partials.standard_extensions_api api:apis.infobars intro:intros.infobars}}
diff --git a/chrome/common/extensions/docs/templates/public/extensions/location.html b/chrome/common/extensions/docs/templates/public/extensions/location.html
new file mode 100644
index 0000000..1d71f9e
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/extensions/location.html
@@ -0,0 +1 @@
+{{+partials.standard_extensions_api api:apis.location intro:intros.location}}
diff --git a/chrome/common/extensions/docs/templates/public/extensions/private_apis.html b/chrome/common/extensions/docs/templates/public/extensions/private_apis.html
new file mode 100644
index 0000000..49adc99
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/extensions/private_apis.html
@@ -0,0 +1 @@
+{{+partials.standard_extensions_article article:intros.private_apis is_apps:false}}
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 9c94936..df10d27 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -335,8 +335,8 @@
   return PermissionsData::HasAPIPermission(this, permission);
 }
 
-bool Extension::HasAPIPermission(const std::string& function_name) const {
-  return PermissionsData::HasAPIPermission(this, function_name);
+bool Extension::HasAPIPermission(const std::string& permission_name) const {
+  return PermissionsData::HasAPIPermission(this, permission_name);
 }
 
 scoped_refptr<const PermissionSet> Extension::GetActivePermissions() const {
@@ -480,7 +480,7 @@
 }
 
 bool Extension::force_incognito_enabled() const {
-  return GetActivePermissions()->HasAnyAccessToAPI("proxy");
+  return PermissionsData::HasAPIPermission(this, APIPermission::kProxy);
 }
 
 void Extension::AddWebExtentPattern(const URLPattern& pattern) {
@@ -790,7 +790,6 @@
     const Extension* extension,
     extension_misc::UnloadedExtensionReason reason)
     : reason(reason),
-      already_disabled(false),
       extension(extension) {}
 
 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo(
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 394b927..87f1148 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -228,7 +228,7 @@
   // DEPRECATED: These methods have been moved to PermissionsData.
   // TODO(rdevlin.cronin): remove these once all calls have been updated.
   bool HasAPIPermission(APIPermission::ID permission) const;
-  bool HasAPIPermission(const std::string& function_name) const;
+  bool HasAPIPermission(const std::string& permission_name) const;
   scoped_refptr<const PermissionSet> GetActivePermissions() const;
 
   // Whether context menu should be shown for page and browser actions.
@@ -348,8 +348,6 @@
   // The following are helpers for InitFromValue to load various features of the
   // extension from the manifest.
 
-  bool LoadAppIsolation(string16* error);
-
   bool LoadRequiredFeatures(string16* error);
   bool LoadName(string16* error);
   bool LoadVersion(string16* error);
@@ -483,9 +481,6 @@
 struct UnloadedExtensionInfo {
   extension_misc::UnloadedExtensionReason reason;
 
-  // Was the extension already disabled?
-  bool already_disabled;
-
   // The extension being unloaded - this should always be non-NULL.
   const Extension* extension;
 
diff --git a/chrome/common/extensions/extension_builder.cc b/chrome/common/extensions/extension_builder.cc
index 22739aa..c7786f8 100644
--- a/chrome/common/extensions/extension_builder.cc
+++ b/chrome/common/extensions/extension_builder.cc
@@ -43,6 +43,11 @@
   return *this;
 }
 
+ExtensionBuilder& ExtensionBuilder::MergeManifest(DictionaryBuilder& builder) {
+  manifest_->MergeDictionary(builder.Build().get());
+  return *this;
+}
+
 ExtensionBuilder& ExtensionBuilder::AddFlags(int init_from_value_flags) {
   flags_ |= init_from_value_flags;
   return *this;
diff --git a/chrome/common/extensions/extension_builder.h b/chrome/common/extensions/extension_builder.h
index aca5b1b..c55eb73 100644
--- a/chrome/common/extensions/extension_builder.h
+++ b/chrome/common/extensions/extension_builder.h
@@ -26,6 +26,10 @@
   // CHECKs that the extension was created successfully.
   scoped_refptr<Extension> Build();
 
+  // Workaround to allow you to pass rvalue ExtensionBuilders by reference to
+  // other functions, e.g. UseBuilder(ExtensionBuilder().Pass())
+  ExtensionBuilder& Pass() { return *this; }
+
   // Defaults to FilePath().
   ExtensionBuilder& SetPath(const base::FilePath& path);
 
@@ -37,6 +41,10 @@
     return SetManifest(manifest_builder.Build());
   }
 
+  // Adds the keys from the DictionaryBuilder to the manifest, with new keys
+  // taking precedence.
+  ExtensionBuilder& MergeManifest(DictionaryBuilder& builder);
+
   ExtensionBuilder& AddFlags(int init_from_value_flags);
 
   // Defaults to the default extension ID created in Extension::Create.
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index a919234..b38de6a 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -127,6 +127,8 @@
     "/usr/share/chromeos-assets/accessibility/extensions/access_chromevox";
 const char kConnectivityDiagnosticsPath[] =
     "/usr/share/chromeos-assets/connectivity_diagnostics";
+const char kConnectivityDiagnosticsLauncherPath[] =
+    "/usr/share/chromeos-assets/connectivity_diagnostics_launcher";
 const char kSpeechSynthesisExtensionPath[] =
     "/usr/share/chromeos-assets/speech_synthesis/patts";
 const char kSpeechSynthesisExtensionId[] =
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index 8f688cd..52a8861 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -254,6 +254,7 @@
   extern const char kChromeVoxExtensionPath[];
   // Path to preinstalled Connectivity Diagnostics extension.
   extern const char kConnectivityDiagnosticsPath[];
+  extern const char kConnectivityDiagnosticsLauncherPath[];
   // Path to preinstalled speech synthesis extension.
   extern const char kSpeechSynthesisExtensionPath[];
   // The extension id of the speech synthesis extension.
diff --git a/chrome/common/extensions/extension_test_util.cc b/chrome/common/extensions/extension_test_util.cc
index b0e13e3..02e9b22 100644
--- a/chrome/common/extensions/extension_test_util.cc
+++ b/chrome/common/extensions/extension_test_util.cc
@@ -10,12 +10,38 @@
 #include "base/values.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_builder.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using extensions::DictionaryBuilder;
 using extensions::Extension;
+using extensions::ExtensionBuilder;
+using extensions::ListBuilder;
 using extensions::Manifest;
 
+namespace extensions {
+namespace extension_test_util {
+
+ExtensionBuilder& BuildExtension(ExtensionBuilder& builder) {
+  return builder
+         .SetManifest(DictionaryBuilder()
+                      .Set("name", "Test extension")
+                      .Set("version", "1.0")
+                      .Set("manifest_version", 2));
+}
+
+ExtensionBuilder& BuildExtensionWithPermissions(ExtensionBuilder& builder,
+                                                ListBuilder& permissions) {
+  return
+      BuildExtension(builder)
+      .MergeManifest(
+           DictionaryBuilder().Set("permissions", permissions));
+}
+
+}  // namespace extension_test_util
+}  // namespace extensions
+
 namespace extension_test_util {
 
 scoped_refptr<Extension> CreateExtensionWithID(std::string id) {
diff --git a/chrome/common/extensions/extension_test_util.h b/chrome/common/extensions/extension_test_util.h
index 12e5104..500bdb7 100644
--- a/chrome/common/extensions/extension_test_util.h
+++ b/chrome/common/extensions/extension_test_util.h
@@ -8,11 +8,22 @@
 #include <string>
 
 #include "base/memory/ref_counted.h"
+#include "chrome/common/extensions/extension_builder.h"
 #include "chrome/common/extensions/manifest.h"
 
 namespace extensions {
 class Extension;
-}
+
+// Newer functions go here.
+// TODO(mpcomplete): migrate older functions over.
+namespace extension_test_util {
+
+ExtensionBuilder& BuildExtension(ExtensionBuilder& builder);
+ExtensionBuilder& BuildExtensionWithPermissions(ExtensionBuilder& builder,
+                                                ListBuilder& permissions);
+
+}  // namespace extension_test_util
+}  // namespace extensions
 
 namespace extension_test_util {
 
diff --git a/chrome/common/extensions/features/permission_feature.cc b/chrome/common/extensions/features/permission_feature.cc
index 90e53a8..5beb26f 100644
--- a/chrome/common/extensions/features/permission_feature.cc
+++ b/chrome/common/extensions/features/permission_feature.cc
@@ -27,7 +27,7 @@
   if (!availability.is_available())
     return availability;
 
-  if (extension && !extension->HasAPIPermission(name()))
+  if (extension && !PermissionsData::HasAPIPermission(extension, name()))
     return CreateAvailability(NOT_PRESENT, extension->GetType());
 
   return CreateAvailability(IS_AVAILABLE);
@@ -49,4 +49,4 @@
   return std::string();
 }
 
-}  // namespace
+}  // namespace extensions
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_background_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_background_unittest.cc
index a6fcab1..10ddbe3 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_background_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_background_unittest.cc
@@ -7,7 +7,6 @@
 #include "base/command_line.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/values.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
@@ -90,8 +89,6 @@
 }
 
 TEST_F(ExtensionManifestBackgroundTest, BackgroundPageWebRequest) {
-  CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
   Feature::ScopedCurrentChannel current_channel(
       chrome::VersionInfo::CHANNEL_DEV);
 
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_experimental_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_experimental_unittest.cc
index 5db6d1e..be0f0d3 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_experimental_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_experimental_unittest.cc
@@ -5,10 +5,10 @@
 #include "chrome/common/extensions/manifest_tests/extension_manifest_test.h"
 
 #include "base/command_line.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
 #include "chrome/common/extensions/manifest.h"
+#include "extensions/common/switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace errors = extension_manifest_errors;
@@ -19,6 +19,6 @@
   LoadAndExpectSuccess("experimental.json", extensions::Manifest::INTERNAL,
                        extensions::Extension::FROM_WEBSTORE);
   CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
+      extensions::switches::kEnableExperimentalExtensionApis);
   LoadAndExpectSuccess("experimental.json");
 }
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_isolatedapp_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_isolatedapp_unittest.cc
index bdbba82..629d319 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_isolatedapp_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_isolatedapp_unittest.cc
@@ -5,10 +5,10 @@
 #include "chrome/common/extensions/manifest_tests/extension_manifest_test.h"
 
 #include "base/command_line.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
 #include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
+#include "extensions/common/switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace errors = extension_manifest_errors;
@@ -24,7 +24,7 @@
                      errors::kExperimentalFlagRequired);
 
   CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalExtensionApis);
+      extensions::switches::kEnableExperimentalExtensionApis);
   scoped_refptr<Extension> extension2(
       LoadAndExpectSuccess("isolated_app_valid.json"));
   EXPECT_TRUE(AppIsolationInfo::HasIsolatedStorage(extension2.get()));
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc
index ae4620b..3d361b0 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc
@@ -11,6 +11,7 @@
 #include "chrome/common/extensions/incognito_handler.h"
 #include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
 #include "chrome/common/extensions/manifest_tests/extension_manifest_test.h"
+#include "extensions/common/switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace errors = extension_manifest_errors;
@@ -81,7 +82,7 @@
   // key in the init_platform_app_csp.json manifest.)
   std::string test_id = "ahplfneplbnjcflhdgkkjeiglkkfeelb";
   CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      switches::kWhitelistedExtensionID, test_id);
+      ::switches::kWhitelistedExtensionID, test_id);
   scoped_refptr<Extension> extension =
       LoadAndExpectSuccess("init_platform_app_csp.json");
   EXPECT_EQ(0U, extension->install_warnings().size())
diff --git a/chrome/common/extensions/permissions/api_permission.h b/chrome/common/extensions/permissions/api_permission.h
index 2266a27..d99616d 100644
--- a/chrome/common/extensions/permissions/api_permission.h
+++ b/chrome/common/extensions/permissions/api_permission.h
@@ -69,6 +69,7 @@
     kDownloads,
     kDownloadsInternal,
     kDownloadsOpen,
+    kDownloadsShelf,
     kEchoPrivate,
     kEnterprisePlatformKeysPrivate,
     kExperimental,
@@ -86,6 +87,7 @@
     kIdentity,
     kIdentityPrivate,
     kIdle,
+    kInfobars,
     kInput,
     kInputMethodPrivate,
     kLocation,
@@ -106,6 +108,7 @@
     kPrivacy,
     kProxy,
     kPushMessaging,
+    kRecoveryPrivate,
     kRtcPrivate,
     kScreensaver,
     kSerial,
@@ -134,7 +137,6 @@
     kWebRequest,
     kWebRequestBlocking,
     kWebRequestInternal,
-    kWebSocketProxyPrivate,
     kWebstorePrivate,
     kWebView,
     kSystemCpu,
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index 45ad574..1742d79 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -54,6 +54,7 @@
       APIPermissionInfo::kFlagNone,
       IDS_EXTENSION_PROMPT_WARNING_DOWNLOADS_OPEN,
       PermissionMessage::kDownloadsOpen },
+    { APIPermission::kDownloadsShelf, "downloads.shelf" },
     { APIPermission::kIdentity, "identity" },
     { APIPermission::kExperimental, "experimental",
       APIPermissionInfo::kFlagCannotBeOptional },
@@ -96,6 +97,7 @@
       IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY,
       PermissionMessage::kBrowsingHistory },
     { APIPermission::kIdle, "idle" },
+    { APIPermission::kInfobars, "infobars" },
     { APIPermission::kInput, "input", APIPermissionInfo::kFlagNone,
       IDS_EXTENSION_PROMPT_WARNING_INPUT,
       PermissionMessage::kInput },
@@ -186,6 +188,8 @@
       APIPermissionInfo::kFlagCannotBeOptional },
     { APIPermission::kFeedbackPrivate, "feedbackPrivate",
       APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kRecoveryPrivate, "recoveryPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
     { APIPermission::kRtcPrivate, "rtcPrivate",
       APIPermissionInfo::kFlagCannotBeOptional },
     { APIPermission::kTerminalPrivate, "terminalPrivate",
@@ -193,8 +197,6 @@
     { APIPermission::kWallpaperPrivate, "wallpaperPrivate",
       APIPermissionInfo::kFlagCannotBeOptional },
     { APIPermission::kWebRequestInternal, "webRequestInternal" },
-    { APIPermission::kWebSocketProxyPrivate, "webSocketProxyPrivate",
-      APIPermissionInfo::kFlagCannotBeOptional },
     { APIPermission::kWebstorePrivate, "webstorePrivate",
       APIPermissionInfo::kFlagCannotBeOptional },
     { APIPermission::kMediaGalleriesPrivate, "mediaGalleriesPrivate",
diff --git a/chrome/common/extensions/permissions/permission_set.cc b/chrome/common/extensions/permissions/permission_set.cc
index d544806..1ad5646 100644
--- a/chrome/common/extensions/permissions/permission_set.cc
+++ b/chrome/common/extensions/permissions/permission_set.cc
@@ -37,45 +37,6 @@
   return false;
 }
 
-// Names of API modules that can be used without listing it in the
-// permissions section of the manifest.
-const char* kNonPermissionModuleNames[] = {
-  "browserAction",
-  "commands",
-  "devtools",
-  "events",
-  "extension",
-  "i18n",
-  "omnibox",
-  "pageAction",
-  "pageActions",
-  "pageLauncher",
-  "permissions",
-  "runtime",
-  "scriptBadge",
-  "tabs",
-  "test",
-  "types",
-  "windows"
-};
-const size_t kNumNonPermissionModuleNames =
-    arraysize(kNonPermissionModuleNames);
-
-// Names of functions (within modules requiring permissions) that can be used
-// without asking for the module permission. In other words, functions you can
-// use with no permissions specified.
-const char* kNonPermissionFunctionNames[] = {
-  "app.getDetails",
-  "app.getDetailsForFrame",
-  "app.getIsInstalled",
-  "app.installState",
-  "app.runningState",
-  "management.getPermissionWarningsByManifest",
-  "management.uninstallSelf",
-};
-const size_t kNumNonPermissionFunctionNames =
-    arraysize(kNonPermissionFunctionNames);
-
 void AddPatternsAndRemovePaths(const URLPatternSet& set, URLPatternSet* out) {
   DCHECK(out);
   for (URLPatternSet::const_iterator i = set.begin(); i != set.end(); ++i) {
@@ -85,19 +46,6 @@
   }
 }
 
-// Strips out the API name from a function or event name.
-// Functions will be of the form api_name.function
-// Events will be of the form api_name/id or api_name.optional.stuff
-std::string GetPermissionName(const std::string& function_name) {
-  size_t separator = function_name.find_first_of("./");
-  if (separator != std::string::npos)
-    return function_name.substr(0, separator);
-  else
-    return function_name;
-}
-
-
-
 }  // namespace
 
 namespace extensions {
@@ -230,21 +178,7 @@
   return apis_str;
 }
 
-bool PermissionSet::HasAnyAccessToAPI(
-    const std::string& api_name) const {
-  if (HasAccessToFunction(api_name, true))
-    return true;
-
-  for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) {
-    if (api_name == GetPermissionName(kNonPermissionFunctionNames[i]))
-      return true;
-  }
-
-  return false;
-}
-
-std::set<std::string>
-    PermissionSet::GetDistinctHostsForDisplay() const {
+std::set<std::string> PermissionSet::GetDistinctHostsForDisplay() const {
   URLPatternSet hosts_displayed_as_url;
   // Filters out every URL pattern that matches chrome:// scheme.
   for (URLPatternSet::const_iterator i = effective_hosts_.begin();
@@ -361,6 +295,13 @@
   return apis().find(id) != apis().end();
 }
 
+bool PermissionSet::HasAPIPermission(const std::string& permission_name) const {
+  const APIPermissionInfo* permission =
+      PermissionsInfo::GetInstance()->GetByName(permission_name);
+  CHECK(permission) << permission_name;
+  return (permission && apis_.count(permission->id()));
+}
+
 bool PermissionSet::CheckAPIPermission(APIPermission::ID permission) const {
   return CheckAPIPermissionWithParam(permission, NULL);
 }
@@ -374,45 +315,6 @@
   return iter->Check(param);
 }
 
-bool PermissionSet::HasAccessToFunction(
-    const std::string& function_name, bool allow_implicit) const {
-  // TODO(jstritar): Embed this information in each permission and add a method
-  // like GrantsAccess(function_name) to APIPermission. A "default"
-  // permission can then handle the modules and functions that everyone can
-  // access.
-  if (allow_implicit) {
-    for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) {
-      if (function_name == kNonPermissionFunctionNames[i])
-        return true;
-    }
-  }
-
-  // Search for increasingly smaller substrings of |function_name| to see if we
-  // find a matching permission or non-permission module name. E.g. for
-  // "a.b.c", we'll search on "a.b.c", then "a.b", and finally "a".
-  std::string name = function_name;
-  size_t lastdot;
-  do {
-    const APIPermissionInfo* permission =
-        PermissionsInfo::GetInstance()->GetByName(name);
-    if (permission && apis_.count(permission->id()))
-      return true;
-
-    if (allow_implicit) {
-      for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) {
-        if (name == kNonPermissionModuleNames[i]) {
-          return true;
-        }
-      }
-    }
-    lastdot = name.find_last_of("./");
-    if (lastdot != std::string::npos)
-      name = std::string(name, 0, lastdot);
-  } while (lastdot != std::string::npos);
-
-  return false;
-}
-
 bool PermissionSet::HasExplicitAccessToOrigin(
     const GURL& origin) const {
   return explicit_hosts().MatchesURL(origin);
diff --git a/chrome/common/extensions/permissions/permission_set.h b/chrome/common/extensions/permissions/permission_set.h
index 9d669da..10e4d86 100644
--- a/chrome/common/extensions/permissions/permission_set.h
+++ b/chrome/common/extensions/permissions/permission_set.h
@@ -70,12 +70,6 @@
   // Gets the API permissions in this set as a set of strings.
   std::set<std::string> GetAPIsAsStrings() const;
 
-  // Returns whether this namespace has any functions which the extension has
-  // permission to use.  For example, even though the extension may not have
-  // the "tabs" permission, "tabs.create" requires no permissions so
-  // HasAnyAccessToAPI("tabs") will return true.
-  bool HasAnyAccessToAPI(const std::string& api_name) const;
-
   // Gets the localized permission messages that represent this set.
   // The set of permission messages shown varies by extension type.
   PermissionMessages GetPermissionMessages(Manifest::Type extension_type) const;
@@ -96,6 +90,11 @@
   // Returns true if the set has the specified API permission.
   bool HasAPIPermission(APIPermission::ID permission) const;
 
+  // Returns true if the |extension| explicitly requests access to the given
+  // |permission_name|. Note this does not include APIs without no corresponding
+  // permission, like "runtime" or "browserAction".
+  bool HasAPIPermission(const std::string& permission_name) const;
+
   // Returns true if the set allows the given permission with the default
   // permission detal.
   bool CheckAPIPermission(APIPermission::ID permission) const;
@@ -104,13 +103,6 @@
   bool CheckAPIPermissionWithParam(APIPermission::ID permission,
       const APIPermission::CheckParam* param) const;
 
-  // Returns true if the permissions in this set grant access to the specified
-  // |function_name|. The |allow_implicit| flag controls whether we
-  // want to strictly check against just the explicit permissions, or also
-  // include implicit "no permission needed" namespaces/functions.
-  bool HasAccessToFunction(const std::string& function_name,
-                           bool allow_implicit) const;
-
   // Returns true if this includes permission to access |origin|.
   bool HasExplicitAccessToOrigin(const GURL& origin) const;
 
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index 9e784fc..9748e87 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -643,6 +643,7 @@
   skip.insert(APIPermission::kBrowsingData);
   skip.insert(APIPermission::kContextMenus);
   skip.insert(APIPermission::kDiagnostics);
+  skip.insert(APIPermission::kDownloadsShelf);
   skip.insert(APIPermission::kFontSettings);
   skip.insert(APIPermission::kFullscreen);
   skip.insert(APIPermission::kIdle);
@@ -650,8 +651,8 @@
   skip.insert(APIPermission::kPointerLock);
   skip.insert(APIPermission::kPower);
   skip.insert(APIPermission::kPushMessaging);
-  skip.insert(APIPermission::kSessionRestore);
   skip.insert(APIPermission::kScreensaver);
+  skip.insert(APIPermission::kSessionRestore);
   skip.insert(APIPermission::kStorage);
   skip.insert(APIPermission::kSystemCpu);
   skip.insert(APIPermission::kSystemDisplay);
@@ -709,19 +710,20 @@
   skip.insert(APIPermission::kFileBrowserHandlerInternal);
   skip.insert(APIPermission::kFileBrowserPrivate);
   skip.insert(APIPermission::kIdentityPrivate);
+  skip.insert(APIPermission::kInfobars);
   skip.insert(APIPermission::kInputMethodPrivate);
   skip.insert(APIPermission::kMediaGalleriesPrivate);
   skip.insert(APIPermission::kMediaPlayerPrivate);
   skip.insert(APIPermission::kMetricsPrivate);
   skip.insert(APIPermission::kNetworkingPrivate);
   skip.insert(APIPermission::kPreferencesPrivate);
+  skip.insert(APIPermission::kRecoveryPrivate);
   skip.insert(APIPermission::kRtcPrivate);
   skip.insert(APIPermission::kStreamsPrivate);
   skip.insert(APIPermission::kSystemPrivate);
   skip.insert(APIPermission::kTerminalPrivate);
   skip.insert(APIPermission::kWallpaperPrivate);
   skip.insert(APIPermission::kWebRequestInternal);
-  skip.insert(APIPermission::kWebSocketProxyPrivate);
   skip.insert(APIPermission::kWebstorePrivate);
 
   // Warned as part of host permissions.
@@ -757,77 +759,6 @@
   }
 }
 
-// Tests the default permissions (empty API permission set).
-TEST(PermissionsTest, DefaultFunctionAccess) {
-  const struct {
-    const char* permission_name;
-    bool expect_success;
-  } kTests[] = {
-    // Negative test.
-    { "non_existing_permission", false },
-    // Test default module/package permission.
-    { "browserAction",  true },
-    { "devtools",       true },
-    { "extension",      true },
-    { "i18n",           true },
-    { "pageAction",     true },
-    { "pageActions",    true },
-    { "test",           true },
-    // Some negative tests.
-    { "bookmarks",      false },
-    { "cookies",        false },
-    { "history",        false },
-    // Make sure we find the module name after stripping '.' and '/'.
-    { "browserAction/abcd/onClick",  true },
-    { "browserAction.abcd.onClick",  true },
-    // Test Tabs functions.
-    { "tabs.create",      true},
-    { "tabs.duplicate",   true},
-    { "tabs.update",      true},
-    { "tabs.getSelected", true},
-    { "tabs.onUpdated",   true },
-  };
-
-  scoped_refptr<PermissionSet> empty = new PermissionSet();
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
-    EXPECT_EQ(kTests[i].expect_success,
-              empty->HasAccessToFunction(kTests[i].permission_name, true))
-                  << "Permission being tested: " << kTests[i].permission_name;
-  }
-}
-
-// Tests the default permissions (empty API permission set).
-TEST(PermissionsTest, DefaultAnyAPIAccess) {
-  const struct {
-    const char* api_name;
-    bool expect_success;
-  } kTests[] = {
-    // Negative test.
-    { "non_existing_permission", false },
-    // Test default module/package permission.
-    { "browserAction",  true },
-    { "devtools",       true },
-    { "extension",      true },
-    { "i18n",           true },
-    { "pageAction",     true },
-    { "pageActions",    true },
-    { "test",           true },
-    // Some negative tests.
-    { "bookmarks",      false },
-    { "cookies",        false },
-    { "history",        false },
-    // Negative APIs that have positive individual functions.
-    { "management",     true},
-    { "tabs",           true},
-  };
-
-  scoped_refptr<PermissionSet> empty = new PermissionSet();
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
-    EXPECT_EQ(kTests[i].expect_success,
-              empty->HasAnyAccessToAPI(kTests[i].api_name));
-  }
-}
-
 TEST(PermissionsTest, GetWarningMessages_ManyHosts) {
   scoped_refptr<Extension> extension;
 
diff --git a/chrome/common/extensions/permissions/permissions_data.cc b/chrome/common/extensions/permissions/permissions_data.cc
index 37a5b2b..8b5796e 100644
--- a/chrome/common/extensions/permissions/permissions_data.cc
+++ b/chrome/common/extensions/permissions/permissions_data.cc
@@ -11,7 +11,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
 #include "chrome/common/extensions/features/base_feature_provider.h"
@@ -60,7 +59,7 @@
     return true;
 
   if (CommandLine::ForCurrentProcess()->HasSwitch(
-          ::switches::kEnableExperimentalExtensionApis)) {
+          switches::kEnableExperimentalExtensionApis)) {
     return true;
   }
 
@@ -357,11 +356,11 @@
 }
 
 // static
-bool PermissionsData::HasAPIPermission(const Extension* extension,
-                                       const std::string& function_name) {
+bool PermissionsData::HasAPIPermission(
+    const Extension* extension,
+    const std::string& permission_name) {
   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
-  return GetActivePermissions(extension)->HasAccessToFunction(
-      function_name, true);  // include implicit
+  return GetActivePermissions(extension)->HasAPIPermission(permission_name);
 }
 
 // static
diff --git a/chrome/common/extensions/permissions/permissions_data.h b/chrome/common/extensions/permissions/permissions_data.h
index 57f6555..18e3787 100644
--- a/chrome/common/extensions/permissions/permissions_data.h
+++ b/chrome/common/extensions/permissions/permissions_data.h
@@ -93,10 +93,13 @@
   // Returns true if the |extension| has the given |permission|. Prefer
   // IsExtensionWithPermissionOrSuggestInConsole when developers may be using an
   // api that requires a permission they didn't know about, e.g. open web apis.
+  // Note this does not include APIs with no corresponding permission, like
+  // "runtime" or "browserAction".
+  // TODO(mpcomplete): drop the "API" from these names, it's confusing.
   static bool HasAPIPermission(const Extension* extension,
                                APIPermission::ID permission);
   static bool HasAPIPermission(const Extension* extension,
-                               const std::string& function_name);
+                               const std::string& permission_name);
   static bool HasAPIPermissionForTab(const Extension* extension,
                                      int tab_id,
                                      APIPermission::ID permission);
diff --git a/chrome/common/extensions/permissions/permissions_data_unittest.cc b/chrome/common/extensions/permissions/permissions_data_unittest.cc
index b689006..a4d1cda 100644
--- a/chrome/common/extensions/permissions/permissions_data_unittest.cc
+++ b/chrome/common/extensions/permissions/permissions_data_unittest.cc
@@ -151,53 +151,6 @@
         "239.255.255.250", 1900));
 }
 
-// This tests the API permissions with an empty manifest (one that just
-// specifies a name and a version and nothing else).
-TEST(ExtensionPermissionsTest, ApiPermissions) {
-  const struct {
-    const char* permission_name;
-    bool expect_success;
-  } kTests[] = {
-    // Negative test.
-    { "non_existing_permission", false },
-    // Test default module/package permission.
-    { "browserAction",  true },
-    { "devtools",       true },
-    { "extension",      true },
-    { "i18n",           true },
-    { "pageAction",     true },
-    { "pageActions",    true },
-    { "test",           true },
-    // Some negative tests.
-    { "bookmarks",      false },
-    { "cookies",        false },
-    { "history",        false },
-    // Make sure we find the module name after stripping '.' and '/'.
-    { "browserAction/abcd/onClick",  true },
-    { "browserAction.abcd.onClick",  true },
-    // Test Tabs functions.
-    { "tabs.create",      true},
-    { "tabs.duplicate",   true},
-    { "tabs.onRemoved",   true},
-    { "tabs.remove",      true},
-    { "tabs.update",      true},
-    { "tabs.getSelected", true},
-    { "tabs.onUpdated",   true },
-    // Test getPermissionWarnings functions. Only one requires permissions.
-    { "management.getPermissionWarningsById", false },
-    { "management.getPermissionWarningsByManifest", true },
-  };
-
-  scoped_refptr<Extension> extension;
-  extension = LoadManifest("empty_manifest", "empty.json");
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
-    EXPECT_EQ(kTests[i].expect_success,
-              extension->HasAPIPermission(kTests[i].permission_name))
-                  << "Permission being tested: " << kTests[i].permission_name;
-  }
-}
-
 TEST(ExtensionPermissionsTest, GetPermissionMessages_ManyAPIPermissions) {
   scoped_refptr<Extension> extension;
   extension = LoadManifest("permissions", "many-apis.json");
diff --git a/chrome/common/extensions/value_builder.h b/chrome/common/extensions/value_builder.h
index 48e9133..db272c5 100644
--- a/chrome/common/extensions/value_builder.h
+++ b/chrome/common/extensions/value_builder.h
@@ -20,8 +20,8 @@
 //                         .Append("foo").Append("bar") /* No .Build() */);
 //
 // Because of limitations in C++03, and to avoid extra copies, you can't pass a
-// just-constructed Builder into another Builder's method without setting at
-// least 1 field.
+// just-constructed Builder into another Builder's method directly. Use the
+// Pass() method.
 //
 // The Build() method invalidates its builder, and returns ownership of the
 // built value.
@@ -49,6 +49,10 @@
   explicit DictionaryBuilder(const base::DictionaryValue& init);
   ~DictionaryBuilder();
 
+  // Workaround to allow you to pass rvalue ExtensionBuilders by reference to
+  // other functions.
+  DictionaryBuilder& Pass() { return *this; }
+
   // Can only be called once, after which it's invalid to use the builder.
   scoped_ptr<base::DictionaryValue> Build() { return dict_.Pass(); }
 
@@ -73,6 +77,10 @@
   explicit ListBuilder(const base::ListValue& init);
   ~ListBuilder();
 
+  // Workaround to allow you to pass rvalue ExtensionBuilders by reference to
+  // other functions.
+  ListBuilder& Pass() { return *this; }
+
   // Can only be called once, after which it's invalid to use the builder.
   scoped_ptr<base::ListValue> Build() { return list_.Pass(); }
 
diff --git a/chrome/common/media_galleries/OWNERS b/chrome/common/media_galleries/OWNERS
index f441493..77d28e1 100644
--- a/chrome/common/media_galleries/OWNERS
+++ b/chrome/common/media_galleries/OWNERS
@@ -1,4 +1,5 @@
 estade@chromium.org
 gbillock@chromium.org
 thestig@chromium.org
+tommycli@chromium.org
 vandebo@chromium.org
diff --git a/chrome/common/itunes_library.cc b/chrome/common/media_galleries/itunes_library.cc
similarity index 88%
rename from chrome/common/itunes_library.cc
rename to chrome/common/media_galleries/itunes_library.cc
index 9d7d4df..853eb7f 100644
--- a/chrome/common/itunes_library.cc
+++ b/chrome/common/media_galleries/itunes_library.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/common/itunes_library.h"
+#include "chrome/common/media_galleries/itunes_library.h"
 
 namespace itunes {
 namespace parser {
diff --git a/chrome/common/itunes_library.h b/chrome/common/media_galleries/itunes_library.h
similarity index 80%
rename from chrome/common/itunes_library.h
rename to chrome/common/media_galleries/itunes_library.h
index 6173d6b..be5c741 100644
--- a/chrome/common/itunes_library.h
+++ b/chrome/common/media_galleries/itunes_library.h
@@ -5,8 +5,8 @@
 // These data structures can be used to describe the contents of an iTunes
 // library.
 
-#ifndef CHROME_COMMON_ITUNES_LIBRARY_H_
-#define CHROME_COMMON_ITUNES_LIBRARY_H_
+#ifndef CHROME_COMMON_MEDIA_GALLERIES_ITUNES_LIBRARY_H_
+#define CHROME_COMMON_MEDIA_GALLERIES_ITUNES_LIBRARY_H_
 
 #include <map>
 #include <set>
@@ -32,5 +32,5 @@
 }  // namespace parser
 }  // namespace itunes
 
-#endif  // CHROME_COMMON_ITUNES_LIBRARY_H_
+#endif  // CHROME_COMMON_MEDIA_GALLERIES_ITUNES_LIBRARY_H_
 
diff --git a/chrome/common/itunes_xml_utils.cc b/chrome/common/media_galleries/itunes_xml_utils.cc
similarity index 95%
rename from chrome/common/itunes_xml_utils.cc
rename to chrome/common/media_galleries/itunes_xml_utils.cc
index 7531a4f..c87e6c8 100644
--- a/chrome/common/itunes_xml_utils.cc
+++ b/chrome/common/media_galleries/itunes_xml_utils.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/common/itunes_xml_utils.h"
+#include "chrome/common/media_galleries/itunes_xml_utils.h"
 
 #include <string>
 
diff --git a/chrome/common/itunes_xml_utils.h b/chrome/common/media_galleries/itunes_xml_utils.h
similarity index 80%
rename from chrome/common/itunes_xml_utils.h
rename to chrome/common/media_galleries/itunes_xml_utils.h
index 8bfef57..00bd49e 100644
--- a/chrome/common/itunes_xml_utils.h
+++ b/chrome/common/media_galleries/itunes_xml_utils.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_COMMON_ITUNES_XML_UTILS_H_
-#define CHROME_COMMON_ITUNES_XML_UTILS_H_
+#ifndef CHROME_COMMON_MEDIA_GALLERIES_ITUNES_XML_UTILS_H_
+#define CHROME_COMMON_MEDIA_GALLERIES_ITUNES_XML_UTILS_H_
 
 #include <string>
 
@@ -25,4 +25,4 @@
 
 }  // namespace itunes
 
-#endif  // CHROME_COMMON_ITUNES_XML_UTILS_H_
+#endif  // CHROME_COMMON_MEDIA_GALLERIES_ITUNES_XML_UTILS_H_
diff --git a/chrome/utility/media_galleries/pmp_test_helper.cc b/chrome/common/media_galleries/pmp_test_helper.cc
similarity index 65%
rename from chrome/utility/media_galleries/pmp_test_helper.cc
rename to chrome/common/media_galleries/pmp_test_helper.cc
index edda31f..1246493 100644
--- a/chrome/utility/media_galleries/pmp_test_helper.cc
+++ b/chrome/common/media_galleries/pmp_test_helper.cc
@@ -2,45 +2,35 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/utility/media_galleries/pmp_test_helper.h"
+#include "chrome/common/media_galleries/pmp_test_helper.h"
 
 #include <algorithm>
 #include <iterator>
 
 #include "base/file_util.h"
 #include "base/logging.h"
-#include "base/platform_file.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/common/media_galleries/picasa_types.h"
-#include "chrome/utility/media_galleries/pmp_column_reader.h"
 
 namespace picasa {
 
 namespace {
 
-bool WriteToFile(const base::FilePath& path, std::vector<uint8> data) {
-  // Cast for usage in WriteFile function
-  const char* data_char = reinterpret_cast<const char*>(&data[0]);
-  size_t bytes_written = file_util::WriteFile(path, data_char, data.size());
-  return (bytes_written == data.size());
-}
-
 // Flatten a vector of elements into an array of bytes.
 template<class T>
-std::vector<uint8> Flatten(const std::vector<T>& elems) {
+std::vector<char> Flatten(const std::vector<T>& elems) {
   if (elems.empty())
-    return std::vector<uint8>();
+    return std::vector<char>();
 
   const uint8* elems0 = reinterpret_cast<const uint8*>(&elems[0]);
-  std::vector<uint8> data_body(elems0, elems0 + sizeof(T) * elems.size());
-
+  std::vector<char> data_body(elems0, elems0 + sizeof(T) * elems.size());
   return data_body;
 }
 
 // Custom specialization for std::string.
 template<>
-std::vector<uint8> Flatten(const std::vector<std::string>& strings) {
-  std::vector<uint8> totalchars;
+std::vector<char> Flatten(const std::vector<std::string>& strings) {
+  std::vector<char> totalchars;
 
   for (std::vector<std::string>::const_iterator it = strings.begin();
       it != strings.end(); ++it) {
@@ -52,9 +42,9 @@
 }
 
 // Returns a new vector with the concatenated contents of |a| and |b|.
-std::vector<uint8> CombinedVectors(const std::vector<uint8>& a,
-                                   const std::vector<uint8>& b) {
-  std::vector<uint8> total;
+std::vector<char> CombinedVectors(const std::vector<char>& a,
+                                  const std::vector<char>& b) {
+  std::vector<char> total;
 
   std::copy(a.begin(), a.end(), std::back_inserter(total));
   std::copy(b.begin(), b.end(), std::back_inserter(total));
@@ -94,10 +84,11 @@
   base::FilePath path = temp_dir_.path().Append(
       base::FilePath::FromUTF8Unsafe(file_name));
 
-  std::vector<uint8> data = PmpTestHelper::MakeHeaderAndBody(
+  std::vector<char> data = PmpTestHelper::MakeHeaderAndBody(
       field_type, elements_vector.size(), elements_vector);
 
-  return WriteToFile(path, data);
+  size_t bytes_written = file_util::WriteFile(path, &data[0], data.size());
+  return (bytes_written == data.size());
 }
 
 // Explicit Instantiation for all the valid types.
@@ -112,37 +103,10 @@
 template bool PmpTestHelper::WriteColumnFileFromVector<uint64>(
     const std::string&, const PmpFieldType, const std::vector<uint64>&);
 
-bool PmpTestHelper::InitColumnReaderFromBytes(
-    PmpColumnReader* const reader,
-    const std::vector<uint8>& data,
-    const PmpFieldType expected_type) {
-  DCHECK(temp_dir_.IsValid());
-
-  base::FilePath temp_path;
-
-  if (!file_util::CreateTemporaryFileInDir(temp_dir_.path(), &temp_path)
-      || !WriteToFile(temp_path, data)) {
-    return false;
-  }
-
-  int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ;
-  base::PlatformFile platform_file =
-      base::CreatePlatformFile(temp_path, flags, NULL, NULL);
-  if (platform_file == base::kInvalidPlatformFileValue)
-    return false;
-
-  bool read_success = reader->ReadFile(platform_file, expected_type);
-
-  base::ClosePlatformFile(platform_file);
-  base::DeleteFile(temp_path, false /* recursive */);
-
-  return read_success;
-}
-
 // Return a vector so we don't have to worry about memory management.
-std::vector<uint8> PmpTestHelper::MakeHeader(const PmpFieldType field_type,
-                                             const uint32 row_count) {
-  std::vector<uint8> header(picasa::kPmpHeaderSize);
+std::vector<char> PmpTestHelper::MakeHeader(const PmpFieldType field_type,
+                                            const uint32 row_count) {
+  std::vector<char> header(picasa::kPmpHeaderSize);
 
   // Copy in magic bytes.
   memcpy(&header[picasa::kPmpMagic1Offset], &picasa::kPmpMagic1,
@@ -168,7 +132,7 @@
 }
 
 template<class T>
-std::vector<uint8> PmpTestHelper::MakeHeaderAndBody(
+std::vector<char> PmpTestHelper::MakeHeaderAndBody(
     const PmpFieldType field_type, const uint32 row_count,
     const std::vector<T>& elems) {
   return CombinedVectors(PmpTestHelper::MakeHeader(field_type, row_count),
@@ -176,15 +140,15 @@
 }
 
 // Explicit Instantiation for all the valid types.
-template std::vector<uint8> PmpTestHelper::MakeHeaderAndBody<std::string>(
+template std::vector<char> PmpTestHelper::MakeHeaderAndBody<std::string>(
     const PmpFieldType, const uint32, const std::vector<std::string>&);
-template std::vector<uint8> PmpTestHelper::MakeHeaderAndBody<uint32>(
+template std::vector<char> PmpTestHelper::MakeHeaderAndBody<uint32>(
     const PmpFieldType, const uint32, const std::vector<uint32>&);
-template std::vector<uint8> PmpTestHelper::MakeHeaderAndBody<double>(
+template std::vector<char> PmpTestHelper::MakeHeaderAndBody<double>(
     const PmpFieldType, const uint32, const std::vector<double>&);
-template std::vector<uint8> PmpTestHelper::MakeHeaderAndBody<uint8>(
+template std::vector<char> PmpTestHelper::MakeHeaderAndBody<uint8>(
     const PmpFieldType, const uint32, const std::vector<uint8>&);
-template std::vector<uint8> PmpTestHelper::MakeHeaderAndBody<uint64>(
+template std::vector<char> PmpTestHelper::MakeHeaderAndBody<uint64>(
     const PmpFieldType, const uint32, const std::vector<uint64>&);
 
 }  // namespace picasa
diff --git a/chrome/utility/media_galleries/pmp_test_helper.h b/chrome/common/media_galleries/pmp_test_helper.h
similarity index 60%
rename from chrome/utility/media_galleries/pmp_test_helper.h
rename to chrome/common/media_galleries/pmp_test_helper.h
index 2878efe..2de6452 100644
--- a/chrome/utility/media_galleries/pmp_test_helper.h
+++ b/chrome/common/media_galleries/pmp_test_helper.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_UTILITY_MEDIA_GALLERIES_PMP_TEST_HELPER_H_
-#define CHROME_UTILITY_MEDIA_GALLERIES_PMP_TEST_HELPER_H_
+#ifndef CHROME_COMMON_MEDIA_GALLERIES_PMP_TEST_HELPER_H_
+#define CHROME_COMMON_MEDIA_GALLERIES_PMP_TEST_HELPER_H_
 
 #include <string>
 #include <vector>
@@ -34,17 +34,13 @@
                                  const PmpFieldType field_type,
                                  const std::vector<T>& elements_vector);
 
-  bool InitColumnReaderFromBytes(PmpColumnReader* const reader,
-                                 const std::vector<uint8>& data,
-                                 const PmpFieldType expected_type);
-
-  static std::vector<uint8> MakeHeader(const PmpFieldType field_type,
+  static std::vector<char> MakeHeader(const PmpFieldType field_type,
                                        const uint32 row_count);
 
   template<class T>
-  static std::vector<uint8> MakeHeaderAndBody(const PmpFieldType field_type,
-                                              const uint32 row_count,
-                                              const std::vector<T>& elems);
+  static std::vector<char> MakeHeaderAndBody(const PmpFieldType field_type,
+                                             const uint32 row_count,
+                                             const std::vector<T>& elems);
 
  private:
   std::string table_name_;
@@ -53,4 +49,4 @@
 
 }  // namespace picasa
 
-#endif  // CHROME_UTILITY_MEDIA_GALLERIES_PMP_TEST_HELPER_H_
+#endif  // CHROME_COMMON_MEDIA_GALLERIES_PMP_TEST_HELPER_H_
diff --git a/chrome/common/metrics/caching_permuted_entropy_provider.cc b/chrome/common/metrics/caching_permuted_entropy_provider.cc
new file mode 100644
index 0000000..f58dee8
--- /dev/null
+++ b/chrome/common/metrics/caching_permuted_entropy_provider.cc
@@ -0,0 +1,110 @@
+// Copyright 2013 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.
+
+#include "chrome/common/metrics/caching_permuted_entropy_provider.h"
+
+#include <string>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/pref_service.h"
+#include "chrome/common/pref_names.h"
+
+namespace metrics {
+
+CachingPermutedEntropyProvider::CachingPermutedEntropyProvider(
+    PrefService* local_state,
+    uint16 low_entropy_source,
+    size_t low_entropy_source_max)
+    : PermutedEntropyProvider(low_entropy_source, low_entropy_source_max),
+      local_state_(local_state) {
+  ReadFromLocalState();
+}
+
+CachingPermutedEntropyProvider::~CachingPermutedEntropyProvider() {
+}
+
+// static
+void CachingPermutedEntropyProvider::RegisterPrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterStringPref(prefs::kMetricsPermutedEntropyCache,
+                               std::string());
+}
+
+// static
+void CachingPermutedEntropyProvider::ClearCache(PrefService* local_state) {
+  local_state->ClearPref(prefs::kMetricsPermutedEntropyCache);
+}
+
+uint16 CachingPermutedEntropyProvider::GetPermutedValue(
+    uint32 randomization_seed) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  uint16 value = 0;
+  if (!FindValue(randomization_seed, &value)) {
+    value = PermutedEntropyProvider::GetPermutedValue(randomization_seed);
+    AddToCache(randomization_seed, value);
+  }
+  return value;
+}
+
+void CachingPermutedEntropyProvider::ReadFromLocalState() const {
+  const std::string base64_cache_data =
+      local_state_->GetString(prefs::kMetricsPermutedEntropyCache);
+  std::string cache_data;
+  if (!base::Base64Decode(base64_cache_data, &cache_data) ||
+      !cache_.ParseFromString(cache_data)) {
+    local_state_->ClearPref(prefs::kMetricsPermutedEntropyCache);
+    NOTREACHED();
+  }
+}
+
+void CachingPermutedEntropyProvider::UpdateLocalState() const {
+  std::string serialized;
+  cache_.SerializeToString(&serialized);
+
+  std::string base64_encoded;
+  if (!base::Base64Encode(serialized, &base64_encoded)) {
+    NOTREACHED();
+    return;
+  }
+  local_state_->SetString(prefs::kMetricsPermutedEntropyCache, base64_encoded);
+}
+
+void CachingPermutedEntropyProvider::AddToCache(uint32 randomization_seed,
+                                                uint16 value) const {
+  PermutedEntropyCache::Entry* entry;
+  const int kMaxSize = 25;
+  if (cache_.entry_size() >= kMaxSize) {
+    // If the cache is full, evict the first entry, swapping later entries in
+    // to take its place. This effectively creates a FIFO cache, which is good
+    // enough here because the expectation is that there shouldn't be more than
+    // |kMaxSize| field trials at any given time, so eviction should happen very
+    // rarely, only as new trials are introduced, evicting old expired trials.
+    for (int i = 1; i < kMaxSize; ++i)
+      cache_.mutable_entry()->SwapElements(i - 1, i);
+    entry = cache_.mutable_entry(kMaxSize - 1);
+  } else {
+    entry = cache_.add_entry();
+  }
+
+  entry->set_randomization_seed(randomization_seed);
+  entry->set_value(value);
+
+  UpdateLocalState();
+}
+
+bool CachingPermutedEntropyProvider::FindValue(uint32 randomization_seed,
+                                               uint16* value) const {
+  for (int i = 0; i < cache_.entry_size(); ++i) {
+    if (cache_.entry(i).randomization_seed() == randomization_seed) {
+      *value = cache_.entry(i).value();
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace metrics
diff --git a/chrome/common/metrics/caching_permuted_entropy_provider.h b/chrome/common/metrics/caching_permuted_entropy_provider.h
new file mode 100644
index 0000000..6416d91
--- /dev/null
+++ b/chrome/common/metrics/caching_permuted_entropy_provider.h
@@ -0,0 +1,65 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_COMMON_METRICS_CACHING_PERMUTED_ENTROPY_PROVIDER_H_
+#define CHROME_COMMON_METRICS_CACHING_PERMUTED_ENTROPY_PROVIDER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/threading/thread_checker.h"
+#include "chrome/common/metrics/entropy_provider.h"
+#include "chrome/common/metrics/proto/permuted_entropy_cache.pb.h"
+
+class PrefService;
+class PrefRegistrySimple;
+
+namespace metrics {
+
+// CachingPermutedEntropyProvider is an entropy provider that uses the same
+// algorithm as the PermutedEntropyProvider, but caches the results in Local
+// State between runs.
+class CachingPermutedEntropyProvider : public PermutedEntropyProvider {
+ public:
+  // Creates a CachingPermutedEntropyProvider using the given |local_state|
+  // prefs service with the specified |low_entropy_source|, which should have a
+  // value in the range of [0, low_entropy_source_max).
+  CachingPermutedEntropyProvider(PrefService* local_state,
+                                 uint16 low_entropy_source,
+                                 size_t low_entropy_source_max);
+  virtual ~CachingPermutedEntropyProvider();
+
+  // Registers pref keys used by this class in the Local State pref registry.
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+  // Clears the cache in local state. Should be called when the low entropy
+  // source value gets reset.
+  static void ClearCache(PrefService* local_state);
+
+ private:
+  // PermutedEntropyProvider overrides:
+  virtual uint16 GetPermutedValue(uint32 randomization_seed) const OVERRIDE;
+
+  // Reads the cache from local state.
+  void ReadFromLocalState() const;
+
+  // Updates local state with the state of the cache.
+  void UpdateLocalState() const;
+
+  // Adds |randomization_seed| -> |value| to the cache.
+  void AddToCache(uint32 randomization_seed, uint16 value) const;
+
+  // Finds the value corresponding to |randomization_seed|, setting |value| and
+  // returning true if found.
+  bool FindValue(uint32 randomization_seed, uint16* value) const;
+
+  base::ThreadChecker thread_checker_;
+  PrefService* local_state_;
+  mutable PermutedEntropyCache cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(CachingPermutedEntropyProvider);
+};
+
+}  // namespace metrics
+
+#endif  // CHROME_COMMON_METRICS_CACHING_PERMUTED_ENTROPY_PROVIDER_H_
diff --git a/chrome/common/metrics/caching_permuted_entropy_provider_unittest.cc b/chrome/common/metrics/caching_permuted_entropy_provider_unittest.cc
new file mode 100644
index 0000000..000b07c
--- /dev/null
+++ b/chrome/common/metrics/caching_permuted_entropy_provider_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2013 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.
+
+#include "chrome/common/metrics/caching_permuted_entropy_provider.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+// Size of the low entropy source to use for the permuted entropy provider
+// in tests.
+const size_t kMaxLowEntropySize = 8000;
+
+// Field trial names used in unit tests.
+const char* const kTestTrialNames[] = { "TestTrial", "AnotherTestTrial",
+                                        "NewTabButton" };
+
+TEST(CachingPermutedEntropyProviderTest, HasConsistentResults) {
+  TestingPrefServiceSimple prefs;
+  CachingPermutedEntropyProvider::RegisterPrefs(prefs.registry());
+  const int kEntropyValue = 1234;
+
+  // Check that the caching provider returns the same results as the non caching
+  // one. Loop over the trial names twice, to test that caching returns the
+  // expected results.
+  PermutedEntropyProvider provider(kEntropyValue, kMaxLowEntropySize);
+  for (size_t i = 0; i < 2 * arraysize(kTestTrialNames); ++i) {
+    CachingPermutedEntropyProvider cached_provider(&prefs, kEntropyValue,
+                                                   kMaxLowEntropySize);
+    const std::string trial_name =
+        kTestTrialNames[i % arraysize(kTestTrialNames)];
+    EXPECT_EQ(provider.GetEntropyForTrial(trial_name, 0),
+              cached_provider.GetEntropyForTrial(trial_name, 0));
+  }
+
+  // Now, do the same test re-using the same caching provider.
+  CachingPermutedEntropyProvider cached_provider(&prefs, kEntropyValue,
+                                                 kMaxLowEntropySize);
+  for (size_t i = 0; i < 2 * arraysize(kTestTrialNames); ++i) {
+    const std::string trial_name =
+        kTestTrialNames[i % arraysize(kTestTrialNames)];
+    EXPECT_EQ(provider.GetEntropyForTrial(trial_name, 0),
+              cached_provider.GetEntropyForTrial(trial_name, 0));
+  }
+}
+
+}  // namespace metrics
diff --git a/chrome/common/metrics/entropy_provider.cc b/chrome/common/metrics/entropy_provider.cc
index 1c19cd2..385f418 100644
--- a/chrome/common/metrics/entropy_provider.cc
+++ b/chrome/common/metrics/entropy_provider.cc
@@ -8,15 +8,11 @@
 #include <limits>
 #include <vector>
 
-#include "base/base64.h"
 #include "base/logging.h"
-#include "base/prefs/pref_registry_simple.h"
-#include "base/prefs/pref_service.h"
 #include "base/rand_util.h"
 #include "base/sha1.h"
 #include "base/sys_byteorder.h"
 #include "chrome/common/metrics/metrics_util.h"
-#include "chrome/common/pref_names.h"
 
 namespace metrics {
 
@@ -109,115 +105,15 @@
   if (randomization_seed == 0)
     randomization_seed = HashName(trial_name);
 
-  std::vector<uint16> mapping(low_entropy_source_max_);
-  internal::PermuteMappingUsingRandomizationSeed(randomization_seed, &mapping);
-
-  return mapping[low_entropy_source_] /
+  return GetPermutedValue(randomization_seed) /
          static_cast<double>(low_entropy_source_max_);
 }
 
-CachingPermutedEntropyProvider::CachingPermutedEntropyProvider(
-    PrefService* local_state,
-    uint16 low_entropy_source,
-    size_t low_entropy_source_max)
-    : local_state_(local_state),
-      low_entropy_source_(low_entropy_source),
-      low_entropy_source_max_(low_entropy_source_max) {
-  DCHECK_LT(low_entropy_source, low_entropy_source_max);
-  DCHECK_LE(low_entropy_source_max, std::numeric_limits<uint16>::max());
-  ReadFromLocalState();
-}
-
-CachingPermutedEntropyProvider::~CachingPermutedEntropyProvider() {
-}
-
-// static
-void CachingPermutedEntropyProvider::RegisterPrefs(
-    PrefRegistrySimple* registry) {
-  registry->RegisterStringPref(prefs::kMetricsPermutedEntropyCache,
-                               std::string());
-}
-
-// static
-void CachingPermutedEntropyProvider::ClearCache(PrefService* local_state) {
-  local_state->ClearPref(prefs::kMetricsPermutedEntropyCache);
-}
-
-double CachingPermutedEntropyProvider::GetEntropyForTrial(
-    const std::string& trial_name,
+uint16 PermutedEntropyProvider::GetPermutedValue(
     uint32 randomization_seed) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (randomization_seed == 0)
-    randomization_seed = HashName(trial_name);
-
-  uint16 value = 0;
-  if (!FindValue(randomization_seed, &value)) {
-    std::vector<uint16> mapping(low_entropy_source_max_);
-    internal::PermuteMappingUsingRandomizationSeed(randomization_seed,
-                                                   &mapping);
-    value = mapping[low_entropy_source_];
-    AddToCache(randomization_seed, value);
-  }
-
-  return value / static_cast<double>(low_entropy_source_max_);
-}
-
-void CachingPermutedEntropyProvider::ReadFromLocalState() const {
-  const std::string base64_cache_data =
-      local_state_->GetString(prefs::kMetricsPermutedEntropyCache);
-  std::string cache_data;
-  if (!base::Base64Decode(base64_cache_data, &cache_data) ||
-      !cache_.ParseFromString(cache_data)) {
-    local_state_->ClearPref(prefs::kMetricsPermutedEntropyCache);
-    NOTREACHED();
-  }
-}
-
-void CachingPermutedEntropyProvider::UpdateLocalState() const {
-  std::string serialized;
-  cache_.SerializeToString(&serialized);
-
-  std::string base64_encoded;
-  if (!base::Base64Encode(serialized, &base64_encoded)) {
-    NOTREACHED();
-    return;
-  }
-  local_state_->SetString(prefs::kMetricsPermutedEntropyCache, base64_encoded);
-}
-
-void CachingPermutedEntropyProvider::AddToCache(uint32 randomization_seed,
-                                                uint16 value) const {
-  PermutedEntropyCache::Entry* entry;
-  const int kMaxSize = 25;
-  if (cache_.entry_size() >= kMaxSize) {
-    // If the cache is full, evict the first entry, swapping later entries in
-    // to take its place. This effectively creates a FIFO cache, which is good
-    // enough here because the expectation is that there shouldn't be more than
-    // |kMaxSize| field trials at any given time, so eviction should happen very
-    // rarely, only as new trials are introduced, evicting old expired trials.
-    for (int i = 1; i < kMaxSize; ++i)
-      cache_.mutable_entry()->SwapElements(i - 1, i);
-    entry = cache_.mutable_entry(kMaxSize - 1);
-  } else {
-    entry = cache_.add_entry();
-  }
-
-  entry->set_randomization_seed(randomization_seed);
-  entry->set_value(value);
-
-  UpdateLocalState();
-}
-
-bool CachingPermutedEntropyProvider::FindValue(uint32 randomization_seed,
-                                               uint16* value) const {
-  for (int i = 0; i < cache_.entry_size(); ++i) {
-    if (cache_.entry(i).randomization_seed() == randomization_seed) {
-      *value = cache_.entry(i).value();
-      return true;
-    }
-  }
-  return false;
+  std::vector<uint16> mapping(low_entropy_source_max_);
+  internal::PermuteMappingUsingRandomizationSeed(randomization_seed, &mapping);
+  return mapping[low_entropy_source_];
 }
 
 }  // namespace metrics
diff --git a/chrome/common/metrics/entropy_provider.h b/chrome/common/metrics/entropy_provider.h
index 6868fd8..494c689 100644
--- a/chrome/common/metrics/entropy_provider.h
+++ b/chrome/common/metrics/entropy_provider.h
@@ -9,17 +9,11 @@
 #include <string>
 #include <vector>
 
-#include "base/base_export.h"
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/metrics/field_trial.h"
-#include "base/threading/thread_checker.h"
-#include "chrome/common/metrics/proto/permuted_entropy_cache.pb.h"
 #include "third_party/mt19937ar/mt19937ar.h"
 
-class PrefService;
-class PrefRegistrySimple;
-
 namespace metrics {
 
 // Internals of entropy_provider.cc exposed for testing.
@@ -83,6 +77,11 @@
   virtual double GetEntropyForTrial(const std::string& trial_name,
                                     uint32 randomization_seed) const OVERRIDE;
 
+ protected:
+  // Performs the permutation algorithm and returns the permuted value that
+  // corresponds to |low_entropy_source_|.
+  virtual uint16 GetPermutedValue(uint32 randomization_seed) const;
+
  private:
   uint16 low_entropy_source_;
   size_t low_entropy_source_max_;
@@ -90,54 +89,6 @@
   DISALLOW_COPY_AND_ASSIGN(PermutedEntropyProvider);
 };
 
-// CachingPermutedEntropyProvider is an entropy provider that uses the same
-// algorithm as the PermutedEntropyProvider, but caches the results in Local
-// State between runs.
-class CachingPermutedEntropyProvider
-    : public base::FieldTrial::EntropyProvider {
- public:
-  // Creates a CachingPermutedEntropyProvider using the given |local_state|
-  // prefs service with the specified |low_entropy_source|, which should have a
-  // value in the range of [0, low_entropy_source_max).
-  CachingPermutedEntropyProvider(PrefService* local_state,
-                                 uint16 low_entropy_source,
-                                 size_t low_entropy_source_max);
-  virtual ~CachingPermutedEntropyProvider();
-
-  // Registers pref keys used by this class in the Local State pref registry.
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
-  // Clears the cache in local state. Should be called when the low entropy
-  // source value gets reset.
-  static void ClearCache(PrefService* local_state);
-
-  // base::FieldTrial::EntropyProvider implementation:
-  virtual double GetEntropyForTrial(const std::string& trial_name,
-                                    uint32 randomization_seed) const OVERRIDE;
-
- private:
-  // Reads the cache from local state.
-  void ReadFromLocalState() const;
-
-  // Updates local state with the state of the cache.
-  void UpdateLocalState() const;
-
-  // Adds |randomization_seed| -> |value| to the cache.
-  void AddToCache(uint32 randomization_seed, uint16 value) const;
-
-  // Finds the value corresponding to |randomization_seed|, setting |value| and
-  // returning true if found.
-  bool FindValue(uint32 randomization_seed, uint16* value) const;
-
-  base::ThreadChecker thread_checker_;
-  PrefService* local_state_;
-  uint16 low_entropy_source_;
-  size_t low_entropy_source_max_;
-  mutable PermutedEntropyCache cache_;
-
-  DISALLOW_COPY_AND_ASSIGN(CachingPermutedEntropyProvider);
-};
-
 }  // namespace metrics
 
 #endif  // CHROME_COMMON_METRICS_ENTROPY_PROVIDER_H_
diff --git a/chrome/common/metrics/entropy_provider_unittest.cc b/chrome/common/metrics/entropy_provider_unittest.cc
index 5c54128..2503d00 100644
--- a/chrome/common/metrics/entropy_provider_unittest.cc
+++ b/chrome/common/metrics/entropy_provider_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/common/metrics/entropy_provider.h"
+
 #include <cmath>
 #include <limits>
 #include <numeric>
@@ -9,10 +11,8 @@
 #include "base/basictypes.h"
 #include "base/guid.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/prefs/testing_pref_service.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/common/metrics/metrics_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -25,7 +25,7 @@
 const size_t kMaxLowEntropySize = 8000;
 
 // Field trial names used in unit tests.
-const std::string kTestTrialNames[] = { "TestTrial", "AnotherTestTrial",
+const char* const kTestTrialNames[] = { "TestTrial", "AnotherTestTrial",
                                         "NewTabButton" };
 
 // Computes the Chi-Square statistic for |values| assuming they follow a uniform
@@ -94,7 +94,7 @@
   }
 
  private:
-  const std::string& trial_name_;
+  std::string trial_name_;
 
   DISALLOW_COPY_AND_ASSIGN(SHA1EntropyGenerator);
 };
@@ -182,10 +182,7 @@
 
 }  // namespace
 
-class EntropyProviderTest : public testing::Test {
-};
-
-TEST_F(EntropyProviderTest, UseOneTimeRandomizationSHA1) {
+TEST(EntropyProviderTest, UseOneTimeRandomizationSHA1) {
   // Simply asserts that two trials using one-time randomization
   // that have different names, normally generate different results.
   //
@@ -214,7 +211,7 @@
   EXPECT_NE(trials[0]->group_name(), trials[1]->group_name());
 }
 
-TEST_F(EntropyProviderTest, UseOneTimeRandomizationPermuted) {
+TEST(EntropyProviderTest, UseOneTimeRandomizationPermuted) {
   // Simply asserts that two trials using one-time randomization
   // that have different names, normally generate different results.
   //
@@ -244,7 +241,7 @@
   EXPECT_NE(trials[0]->group_name(), trials[1]->group_name());
 }
 
-TEST_F(EntropyProviderTest, UseOneTimeRandomizationWithCustomSeedPermuted) {
+TEST(EntropyProviderTest, UseOneTimeRandomizationWithCustomSeedPermuted) {
   // Ensures that two trials with different names but the same custom seed used
   // for one time randomization produce the same group assignments.
   base::FieldTrialList field_trial_list(
@@ -271,7 +268,7 @@
   EXPECT_EQ(trials[0]->group_name(), trials[1]->group_name());
 }
 
-TEST_F(EntropyProviderTest, SHA1Entropy) {
+TEST(EntropyProviderTest, SHA1Entropy) {
   const double results[] = { GenerateSHA1Entropy("hi", "1"),
                              GenerateSHA1Entropy("there", "1") };
 
@@ -287,7 +284,7 @@
             GenerateSHA1Entropy("yo", "else"));
 }
 
-TEST_F(EntropyProviderTest, PermutedEntropy) {
+TEST(EntropyProviderTest, PermutedEntropy) {
   const double results[] = {
       GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"),
       GeneratePermutedEntropy(4321, kMaxLowEntropySize, "1") };
@@ -304,7 +301,7 @@
             GeneratePermutedEntropy(1234, kMaxLowEntropySize, "else"));
 }
 
-TEST_F(EntropyProviderTest, PermutedEntropyProviderResults) {
+TEST(EntropyProviderTest, PermutedEntropyProviderResults) {
   // Verifies that PermutedEntropyProvider produces expected results. This
   // ensures that the results are the same between platforms and ensures that
   // changes to the implementation do not regress this accidentally.
@@ -317,21 +314,21 @@
                    GeneratePermutedEntropy(5000, kMaxLowEntropySize, "Foo"));
 }
 
-TEST_F(EntropyProviderTest, SHA1EntropyIsUniform) {
+TEST(EntropyProviderTest, SHA1EntropyIsUniform) {
   for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
     SHA1EntropyGenerator entropy_generator(kTestTrialNames[i]);
     PerformEntropyUniformityTest(kTestTrialNames[i], entropy_generator);
   }
 }
 
-TEST_F(EntropyProviderTest, PermutedEntropyIsUniform) {
+TEST(EntropyProviderTest, PermutedEntropyIsUniform) {
   for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
     PermutedEntropyGenerator entropy_generator(kTestTrialNames[i]);
     PerformEntropyUniformityTest(kTestTrialNames[i], entropy_generator);
   }
 }
 
-TEST_F(EntropyProviderTest, SeededRandGeneratorIsUniform) {
+TEST(EntropyProviderTest, SeededRandGeneratorIsUniform) {
   // Verifies that SeededRandGenerator has a uniform distribution.
   //
   // Mirrors RandUtilTest.RandGeneratorIsUniform in base/rand_util_unittest.cc.
@@ -369,33 +366,4 @@
   }
 }
 
-TEST_F(EntropyProviderTest, CachingPermutedEntropyProvider) {
-  TestingPrefServiceSimple prefs;
-  CachingPermutedEntropyProvider::RegisterPrefs(prefs.registry());
-  const int kEntropyValue = 1234;
-
-  // Check that the caching provider returns the same results as the non caching
-  // one. Loop over the trial names twice, to test that caching returns the
-  // expected results.
-  PermutedEntropyProvider provider(kEntropyValue, kMaxLowEntropySize);
-  for (size_t i = 0; i < 2 * arraysize(kTestTrialNames); ++i) {
-    CachingPermutedEntropyProvider cached_provider(&prefs, kEntropyValue,
-                                                   kMaxLowEntropySize);
-    const std::string trial_name =
-        kTestTrialNames[i % arraysize(kTestTrialNames)];
-    EXPECT_EQ(provider.GetEntropyForTrial(trial_name, 0),
-              cached_provider.GetEntropyForTrial(trial_name, 0));
-  }
-
-  // Now, do the same test re-using the same caching provider.
-  CachingPermutedEntropyProvider cached_provider(&prefs, kEntropyValue,
-                                                 kMaxLowEntropySize);
-  for (size_t i = 0; i < 2 * arraysize(kTestTrialNames); ++i) {
-    const std::string trial_name =
-        kTestTrialNames[i % arraysize(kTestTrialNames)];
-    EXPECT_EQ(provider.GetEntropyForTrial(trial_name, 0),
-              cached_provider.GetEntropyForTrial(trial_name, 0));
-  }
-}
-
 }  // namespace metrics
diff --git a/chrome/common/net/net_error_info.h b/chrome/common/net/net_error_info.h
index 44730c2..e73f99e 100644
--- a/chrome/common/net/net_error_info.h
+++ b/chrome/common/net/net_error_info.h
@@ -14,7 +14,7 @@
 // 1. FINISHED_UNKNOWN must remain the first FINISHED_* value.
 // 2. FINISHED_* values must not be rearranged relative to FINISHED_UNKNOWN.
 // 3. New FINISHED_* values must be inserted at the end.
-// 4. New non-FINISHED_* values must be inserted before FINISHED_UNKNOWN.
+// 4. New non-FINISHED_* values cannot be inserted.
 enum DnsProbeStatus {
   // A DNS probe may be run for this error page.  (This status is only used on
   // the renderer side before it's received a status update from the browser.)
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 27d9d89..db42627 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -406,6 +406,31 @@
 const char kDefaultSearchProviderInstantURL[] =
     "default_search_provider.instant_url";
 
+// The URL (as understood by TemplateURLRef) the default search provider uses
+// for image search results.
+const char kDefaultSearchProviderImageURL[] =
+    "default_search_provider.image_url";
+
+// The string of post parameters (as understood by TemplateURLRef) the default
+// search provider uses for searches by using POST.
+const char kDefaultSearchProviderSearchURLPostParams[] =
+    "default_search_provider.search_url_post_params";
+
+// The string of post parameters (as understood by TemplateURLRef) the default
+// search provider uses for suggestions by using POST.
+const char kDefaultSearchProviderSuggestURLPostParams[] =
+    "default_search_provider.suggest_url_post_params";
+
+// The string of post parameters (as understood by TemplateURLRef) the default
+// search provider uses for instant results by using POST.
+const char kDefaultSearchProviderInstantURLPostParams[] =
+    "default_search_provider.instant_url_post_params";
+
+// The string of post parameters (as understood by TemplateURLRef) the default
+// search provider uses for image search results by using POST.
+const char kDefaultSearchProviderImageURLPostParams[] =
+    "default_search_provider.image_url_post_params";
+
 // The Favicon URL (as understood by TemplateURLRef) of the default search
 // provider.
 const char kDefaultSearchProviderIconURL[] =
@@ -1940,6 +1965,10 @@
 const char kRemoteAccessHostRequireCurtain[] =
     "remote_access.host_require_curtain";
 
+// Boolean controlling whether curtaining is required when connecting to a host.
+const char kRemoteAccessHostAllowClientPairing[] =
+    "remote_access.host_allow_client_pairing";
+
 // The last used printer and its settings.
 const char kPrintPreviewStickySettings[] =
     "printing.print_preview_sticky_settings";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index a9c11ee..8666cd1 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -168,6 +168,11 @@
 extern const char kDefaultSearchProviderSearchURL[];
 extern const char kDefaultSearchProviderSuggestURL[];
 extern const char kDefaultSearchProviderInstantURL[];
+extern const char kDefaultSearchProviderImageURL[];
+extern const char kDefaultSearchProviderSearchURLPostParams[];
+extern const char kDefaultSearchProviderSuggestURLPostParams[];
+extern const char kDefaultSearchProviderInstantURLPostParams[];
+extern const char kDefaultSearchProviderImageURLPostParams[];
 extern const char kDefaultSearchProviderIconURL[];
 extern const char kDefaultSearchProviderEncodings[];
 extern const char kDefaultSearchProviderName[];
@@ -697,6 +702,7 @@
 extern const char kRemoteAccessHostDomain[];
 extern const char kRemoteAccessHostTalkGadgetPrefix[];
 extern const char kRemoteAccessHostRequireCurtain[];
+extern const char kRemoteAccessHostAllowClientPairing[];
 
 extern const char kPrintPreviewStickySettings[];
 extern const char kCloudPrintRoot[];
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index 6001df2..547057c 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -366,6 +366,15 @@
 IPC_MESSAGE_ROUTED1(ChromeViewMsg_StartFrameSniffer,
                     string16 /* frame-name */)
 
+// Asks the renderer for a thumbnail of the image selected by the most
+// recently opened context menu, if there is one. If the image's area
+// is greater than thumbnail_min_area it will be downscaled to
+// be within thumbnail_max_size. The possibly downsampled image will be
+// returned in a ChromeViewHostMsg_RequestThumbnailForContextNode_ACK message.
+IPC_MESSAGE_ROUTED2(ChromeViewMsg_RequestThumbnailForContextNode,
+                    int /* thumbnail_min_area_pixels */,
+                    gfx::Size /* thumbnail_max_size_pixels */)
+
 // Notifies the renderer whether hiding/showing the top controls is enabled,
 // what the current state should be, and whether or not to animate to the
 // proper state.
@@ -374,10 +383,15 @@
                     content::TopControlsState /* current */,
                     bool /* animate */)
 
+
 // Updates the window features of the render view.
 IPC_MESSAGE_ROUTED1(ChromeViewMsg_SetWindowFeatures,
                     WebKit::WebWindowFeatures /* window_features */)
 
+IPC_MESSAGE_ROUTED1(ChromeViewHostMsg_RequestThumbnailForContextNode_ACK,
+                    SkBitmap /* thumbnail */)
+
+
 // JavaScript related messages -----------------------------------------------
 
 // Notify the JavaScript engine in the render to change its parameters
diff --git a/chrome/common/translate/language_detection_details.h b/chrome/common/translate/language_detection_details.h
index ac9ab7f..c05374c 100644
--- a/chrome/common/translate/language_detection_details.h
+++ b/chrome/common/translate/language_detection_details.h
@@ -9,7 +9,6 @@
 
 #include "base/strings/string16.h"
 #include "base/time/time.h"
-#include "ipc/ipc_message_macros.h"
 #include "url/gurl.h"
 
 struct LanguageDetectionDetails {
diff --git a/chrome/common/widevine_cdm_constants.cc b/chrome/common/widevine_cdm_constants.cc
index 8d5670e..60f487e 100644
--- a/chrome/common/widevine_cdm_constants.cc
+++ b/chrome/common/widevine_cdm_constants.cc
@@ -13,9 +13,4 @@
 const char kWidevineCdmPluginExtension[] = "";
 
 const int32 kWidevineCdmPluginPermissions = ppapi::PERMISSION_DEV |
-#if defined(OS_CHROMEOS)
-// TODO(xhwang): Make permission requirements the same on all OS.
-// See http://crbug.com/222252
-                                            ppapi::PERMISSION_FLASH |
-#endif  // !defined(OS_CHROMEOS)
                                             ppapi::PERMISSION_PRIVATE;
diff --git a/chrome/installer/launcher_support/chrome_launcher_support.cc b/chrome/installer/launcher_support/chrome_launcher_support.cc
index 1d5c506..5705e24 100644
--- a/chrome/installer/launcher_support/chrome_launcher_support.cc
+++ b/chrome/installer/launcher_support/chrome_launcher_support.cc
@@ -45,18 +45,6 @@
 const wchar_t kUninstallArgumentsField[] = L"UninstallArguments";
 const wchar_t kUninstallStringField[] = L"UninstallString";
 
-#ifndef OFFICIAL_BUILD
-base::FilePath GetDevelopmentExe(const wchar_t* exe_file) {
-  base::FilePath current_directory;
-  if (PathService::Get(base::DIR_EXE, &current_directory)) {
-    base::FilePath chrome_exe_path(current_directory.Append(exe_file));
-    if (base::PathExists(chrome_exe_path))
-      return chrome_exe_path;
-  }
-  return base::FilePath();
-}
-#endif
-
 // Reads a string value from the specified product's "ClientState" registry key.
 // Returns true iff the value is present and successfully read.
 bool GetClientStateValue(InstallationLevel level,
@@ -184,10 +172,6 @@
 
 base::FilePath GetAnyChromePath() {
   base::FilePath chrome_path;
-#ifndef OFFICIAL_BUILD
-  // For development mode, chrome.exe should be in same dir as the stub.
-  chrome_path = GetDevelopmentExe(kChromeExe);
-#endif
   if (chrome_path.empty())
     chrome_path = GetChromePathForInstallationLevel(SYSTEM_LEVEL_INSTALLATION);
   if (chrome_path.empty())
@@ -197,10 +181,6 @@
 
 base::FilePath GetAnyAppHostPath() {
   base::FilePath app_host_path;
-#ifndef OFFICIAL_BUILD
-  // For development mode, app_host.exe should be in same dir as chrome.exe.
-  app_host_path = GetDevelopmentExe(kChromeAppHostExe);
-#endif
   if (app_host_path.empty()) {
     app_host_path = GetAppHostPathForInstallationLevel(
         SYSTEM_LEVEL_INSTALLATION);
diff --git a/chrome/installer/launcher_support/chrome_launcher_support.h b/chrome/installer/launcher_support/chrome_launcher_support.h
index 5a948db..e9f6280 100644
--- a/chrome/installer/launcher_support/chrome_launcher_support.h
+++ b/chrome/installer/launcher_support/chrome_launcher_support.h
@@ -39,16 +39,12 @@
 // Returns the path to an installed chrome.exe, or an empty path. Prefers a
 // system-level installation to a user-level installation. Uses Omaha client
 // state to identify a Chrome installation location.
-// In non-official builds, to ease development, this will first look for a
-// chrome.exe in the same directory as the current executable.
 // The file path returned (if any) is guaranteed to exist.
 base::FilePath GetAnyChromePath();
 
 // Returns the path to an installed app_host.exe, or an empty path. Prefers a
 // system-level installation to a user-level installation. Uses Omaha client
 // state to identify a App Host installation location.
-// In non-official builds, to ease development, this will first look for a
-// app_host.exe in the same directory as the current executable.
 // The file path returned (if any) is guaranteed to exist.
 base::FilePath GetAnyAppHostPath();
 
diff --git a/chrome/installer/mini_installer.gypi b/chrome/installer/mini_installer.gypi
index 48ea257..ee9403c 100644
--- a/chrome/installer/mini_installer.gypi
+++ b/chrome/installer/mini_installer.gypi
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 {
   'dependencies': [
-    '<(chrome_dll_project)',
+    '<@(chrome_dll_project)',
     '../chrome.gyp:app_host',
     '../chrome.gyp:chrome',
     '../chrome.gyp:chrome_nacl_win64',
@@ -193,7 +193,7 @@
         '<(create_installer_archive_py_path)',
         '<(PRODUCT_DIR)/app_host.exe',
         '<(PRODUCT_DIR)/chrome.exe',
-        '<(chrome_dll_path)',
+        '<@(chrome_dll_path)',
         '<(PRODUCT_DIR)/nacl64.exe',
         '<(PRODUCT_DIR)/ppGoogleNaClPluginChrome.dll',
         '<(PRODUCT_DIR)/nacl_irt_x86_32.nexe',
diff --git a/chrome/installer/mini_installer_syzygy.gyp b/chrome/installer/mini_installer_syzygy.gyp
index 74050ab..6c99347 100644
--- a/chrome/installer/mini_installer_syzygy.gyp
+++ b/chrome/installer/mini_installer_syzygy.gyp
@@ -15,21 +15,51 @@
   ],
   'conditions': [
     # This target won't build in fastbuild, since there are no PDBs. 
-    ['OS=="win" and fastbuild==0 and chrome_multiple_dll==0', {
-      'targets': [
-        {
-          'target_name': 'mini_installer_syzygy',
-          'type': 'executable',
-          'product_name': 'mini_installer',
+    ['OS=="win" and fastbuild==0', {
+      'conditions': [
+        ['chrome_multiple_dll==0', {
+          'targets': [
+            {
+              'target_name': 'mini_installer_syzygy',
+              'type': 'executable',
+              'product_name': 'mini_installer',
 
-          'variables': {
-            'chrome_dll_project': '../chrome_syzygy.gyp:chrome_dll_syzygy',
-            'chrome_dll_path': '<(PRODUCT_DIR)/syzygy/chrome.dll',
-            'output_dir': '<(PRODUCT_DIR)/syzygy',
-          },
-          # Bulk of the build configuration comes from here.
-          'includes': [ 'mini_installer.gypi', ],
-        },
+              'variables': {
+                'chrome_dll_project': [
+                  '../chrome_syzygy.gyp:chrome_dll_syzygy',
+                ],
+                'chrome_dll_path': [
+                  '<(PRODUCT_DIR)/syzygy/chrome.dll',
+                ],
+                'output_dir': '<(PRODUCT_DIR)/syzygy',
+              },
+              # Bulk of the build configuration comes from here.
+              'includes': [ 'mini_installer.gypi', ],
+            },
+          ],
+        }, {
+          'targets': [
+            {
+              'target_name': 'mini_installer_syzygy',
+              'type': 'executable',
+              'product_name': 'mini_installer',
+
+              'variables': {
+                'chrome_dll_project': [
+                  '../chrome_syzygy.gyp:chrome_dll_syzygy',
+                  '../chrome_syzygy.gyp:chrome_child_dll_syzygy',
+                ],
+                'chrome_dll_path': [
+                  '<(PRODUCT_DIR)/syzygy/chrome.dll',
+                  '<(PRODUCT_DIR)/syzygy/chrome_child.dll',
+                ],
+                'output_dir': '<(PRODUCT_DIR)/syzygy',
+              },
+              # Bulk of the build configuration comes from here.
+              'includes': [ 'mini_installer.gypi', ],
+            },
+          ],
+        }],
       ],
     },{
       'targets': [],
diff --git a/chrome/nacl.gypi b/chrome/nacl.gypi
index fc4bd7a..09cfbaf 100644
--- a/chrome/nacl.gypi
+++ b/chrome/nacl.gypi
@@ -24,18 +24,18 @@
           # .cc, .h, and .mm files under nacl that are used on all
           # platforms, including both 32-bit and 64-bit Windows.
           # Test files are also not included.
-          'nacl/nacl_ipc_adapter.cc',
-          'nacl/nacl_ipc_adapter.h',
-          'nacl/nacl_main.cc',
-          'nacl/nacl_main_platform_delegate.h',
-          'nacl/nacl_main_platform_delegate_linux.cc',
-          'nacl/nacl_main_platform_delegate_mac.mm',
-          'nacl/nacl_main_platform_delegate_win.cc',
-          'nacl/nacl_listener.cc',
-          'nacl/nacl_listener.h',
-          'nacl/nacl_validation_db.h',
-          'nacl/nacl_validation_query.cc',
-          'nacl/nacl_validation_query.h',
+          '../components/nacl/loader/nacl_ipc_adapter.cc',
+          '../components/nacl/loader/nacl_ipc_adapter.h',
+          '../components/nacl/loader/nacl_main.cc',
+          '../components/nacl/loader/nacl_main_platform_delegate.h',
+          '../components/nacl/loader/nacl_main_platform_delegate_linux.cc',
+          '../components/nacl/loader/nacl_main_platform_delegate_mac.mm',
+          '../components/nacl/loader/nacl_main_platform_delegate_win.cc',
+          '../components/nacl/loader/nacl_listener.cc',
+          '../components/nacl/loader/nacl_listener.h',
+          '../components/nacl/loader/nacl_validation_db.h',
+          '../components/nacl/loader/nacl_validation_query.cc',
+          '../components/nacl/loader/nacl_validation_query.h',
         ],
         # TODO(gregoryd): consider switching NaCl to use Chrome OS defines
         'conditions': [
@@ -155,8 +155,8 @@
               ],
               'sources': [
                 'nacl/nacl_helper_linux.cc',
-                'nacl/nacl_sandbox_linux.cc',
                 '../base/posix/unix_domain_socket_linux.cc',
+                '../components/nacl/loader/nacl_sandbox_linux.cc',
                 '../content/common/child_process_sandbox_support_impl_shm_linux.cc',
                 '../content/common/sandbox_init_linux.cc',
                 '../content/common/sandbox_seccomp_bpf_linux.cc',
diff --git a/chrome/nacl/DEPS b/chrome/nacl/DEPS
index a97afd9..c819506 100644
--- a/chrome/nacl/DEPS
+++ b/chrome/nacl/DEPS
@@ -4,16 +4,6 @@
   "+components/breakpad",
   "+components/nacl",
   "+content/public/app/startup_helper_win.h",
-  "+sandbox/linux/seccomp-bpf",
   "+sandbox/linux/services",
   "+sandbox/win/src",
-  "+native_client/src",
-  "+ppapi/c",  # header files only
-
-  # For handle conversion in nacl_ipc_adapter.cc:
-  "+ppapi/proxy/handle_converter.h",
-  "+ppapi/proxy/serialized_handle.h",
-
-  # For sending PpapiHostMsg_ChannelCreated in nacl_ipc_adapter.cc:
-  "+ppapi/proxy/ppapi_messages.h"
 ]
diff --git a/chrome/nacl/OWNERS b/chrome/nacl/OWNERS
index 4d8e916..8345057 100644
--- a/chrome/nacl/OWNERS
+++ b/chrome/nacl/OWNERS
@@ -4,6 +4,3 @@
 mseaborn@chromium.org
 noelallen@chromium.org
 sehr@chromium.org
-
-per-file nacl_sandbox_linux.*=jln@chromium.org
-per-file nacl_sandbox_linux.*=mseaborn@chromium.org
diff --git a/chrome/nacl/nacl_exe_win_64.cc b/chrome/nacl/nacl_exe_win_64.cc
index 2a810e8..4e480d5 100644
--- a/chrome/nacl/nacl_exe_win_64.cc
+++ b/chrome/nacl/nacl_exe_win_64.cc
@@ -8,6 +8,7 @@
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
 #include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_device_source.h"
 #include "base/process/launch.h"
 #include "base/process/memory.h"
 #include "base/strings/string_util.h"
@@ -16,11 +17,11 @@
 #include "chrome/app/chrome_breakpad_client.h"
 #include "chrome/common/chrome_result_codes.h"
 #include "chrome/common/logging_chrome.h"
-#include "chrome/nacl/nacl_listener.h"
-#include "chrome/nacl/nacl_main_platform_delegate.h"
 #include "components/breakpad/breakpad_client.h"
 #include "components/nacl/broker/nacl_broker_listener.h"
 #include "components/nacl/common/nacl_switches.h"
+#include "components/nacl/loader/nacl_listener.h"
+#include "components/nacl/loader/nacl_main_platform_delegate.h"
 #include "content/public/app/startup_helper_win.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/main_function_params.h"
@@ -44,7 +45,9 @@
   base::MessageLoopForIO main_message_loop;
   base::PlatformThread::SetName("CrNaClBrokerMain");
 
-  base::PowerMonitor power_monitor;
+  scoped_ptr<base::PowerMonitorSource> power_monitor_source(
+      new base::PowerMonitorDeviceSource());
+  base::PowerMonitor power_monitor(power_monitor_source.Pass());
   base::HighResolutionTimerManager hi_res_timer_manager;
 
   NaClBrokerListener listener;
diff --git a/chrome/nacl/nacl_helper_linux.cc b/chrome/nacl/nacl_helper_linux.cc
index 1718c86..9041793 100644
--- a/chrome/nacl/nacl_helper_linux.cc
+++ b/chrome/nacl/nacl_helper_linux.cc
@@ -27,8 +27,8 @@
 #include "base/posix/global_descriptors.h"
 #include "base/posix/unix_domain_socket_linux.h"
 #include "base/rand_util.h"
-#include "chrome/nacl/nacl_listener.h"
-#include "chrome/nacl/nacl_sandbox_linux.h"
+#include "components/nacl/loader/nacl_listener.h"
+#include "components/nacl/loader/nacl_sandbox_linux.h"
 #include "crypto/nss_util.h"
 #include "ipc/ipc_descriptors.h"
 #include "ipc/ipc_switches.h"
diff --git a/chrome/nacl/nacl_ipc_adapter.cc b/chrome/nacl/nacl_ipc_adapter.cc
deleted file mode 100644
index b143cf4..0000000
--- a/chrome/nacl/nacl_ipc_adapter.cc
+++ /dev/null
@@ -1,592 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/nacl/nacl_ipc_adapter.h"
-
-#include <limits.h>
-#include <string.h>
-
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/shared_memory.h"
-#include "build/build_config.h"
-#include "ipc/ipc_channel.h"
-#include "ipc/ipc_platform_file.h"
-#include "native_client/src/trusted/desc/nacl_desc_base.h"
-#include "native_client/src/trusted/desc/nacl_desc_custom.h"
-#include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
-#include "native_client/src/trusted/desc/nacl_desc_io.h"
-#include "native_client/src/trusted/desc/nacl_desc_sync_socket.h"
-#include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
-#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
-#include "ppapi/c/ppb_file_io.h"
-#include "ppapi/proxy/ppapi_messages.h"
-#include "ppapi/proxy/serialized_handle.h"
-
-namespace {
-
-enum BufferSizeStatus {
-  // The buffer contains a full message with no extra bytes.
-  MESSAGE_IS_COMPLETE,
-
-  // The message doesn't fit and the buffer contains only some of it.
-  MESSAGE_IS_TRUNCATED,
-
-  // The buffer contains a full message + extra data.
-  MESSAGE_HAS_EXTRA_DATA
-};
-
-BufferSizeStatus GetBufferStatus(const char* data, size_t len) {
-  if (len < sizeof(NaClIPCAdapter::NaClMessageHeader))
-    return MESSAGE_IS_TRUNCATED;
-
-  const NaClIPCAdapter::NaClMessageHeader* header =
-      reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(data);
-  uint32 message_size =
-      sizeof(NaClIPCAdapter::NaClMessageHeader) + header->payload_size;
-
-  if (len == message_size)
-    return MESSAGE_IS_COMPLETE;
-  if (len > message_size)
-    return MESSAGE_HAS_EXTRA_DATA;
-  return MESSAGE_IS_TRUNCATED;
-}
-
-// This object allows the NaClDesc to hold a reference to a NaClIPCAdapter and
-// forward calls to it.
-struct DescThunker {
-  explicit DescThunker(NaClIPCAdapter* adapter_param)
-      : adapter(adapter_param) {
-  }
-  scoped_refptr<NaClIPCAdapter> adapter;
-};
-
-NaClIPCAdapter* ToAdapter(void* handle) {
-  return static_cast<DescThunker*>(handle)->adapter.get();
-}
-
-// NaClDescCustom implementation.
-void NaClDescCustomDestroy(void* handle) {
-  delete static_cast<DescThunker*>(handle);
-}
-
-ssize_t NaClDescCustomSendMsg(void* handle, const NaClImcTypedMsgHdr* msg,
-                              int /* flags */) {
-  return static_cast<ssize_t>(ToAdapter(handle)->Send(msg));
-}
-
-ssize_t NaClDescCustomRecvMsg(void* handle, NaClImcTypedMsgHdr* msg,
-                              int /* flags */) {
-  return static_cast<ssize_t>(ToAdapter(handle)->BlockingReceive(msg));
-}
-
-NaClDesc* MakeNaClDescCustom(NaClIPCAdapter* adapter) {
-  NaClDescCustomFuncs funcs = NACL_DESC_CUSTOM_FUNCS_INITIALIZER;
-  funcs.Destroy = NaClDescCustomDestroy;
-  funcs.SendMsg = NaClDescCustomSendMsg;
-  funcs.RecvMsg = NaClDescCustomRecvMsg;
-  // NaClDescMakeCustomDesc gives us a reference on the returned NaClDesc.
-  return NaClDescMakeCustomDesc(new DescThunker(adapter), &funcs);
-}
-
-void DeleteChannel(IPC::Channel* channel) {
-  delete channel;
-}
-
-// Translates Pepper's read/write open flags into the NaCl equivalents.
-// Since the host has already opened the file, flags such as O_CREAT, O_TRUNC,
-// and O_EXCL don't make sense, so we filter those out. If no read or write
-// flags are set, the function returns NACL_ABI_O_RDONLY as a safe fallback.
-int TranslatePepperFileReadWriteOpenFlags(int32_t pp_open_flags) {
-  bool read = (pp_open_flags & PP_FILEOPENFLAG_READ) != 0;
-  bool write = (pp_open_flags & PP_FILEOPENFLAG_WRITE) != 0;
-  bool append = (pp_open_flags & PP_FILEOPENFLAG_APPEND) != 0;
-
-  int nacl_open_flag = NACL_ABI_O_RDONLY;  // NACL_ABI_O_RDONLY == 0.
-  if (read && (write || append)) {
-    nacl_open_flag = NACL_ABI_O_RDWR;
-  } else if (write || append) {
-    nacl_open_flag = NACL_ABI_O_WRONLY;
-  } else if (!read) {
-    DLOG(WARNING) << "One of PP_FILEOPENFLAG_READ, PP_FILEOPENFLAG_WRITE, "
-                  << "or PP_FILEOPENFLAG_APPEND should be set.";
-  }
-  if (append)
-    nacl_open_flag |= NACL_ABI_O_APPEND;
-
-  return nacl_open_flag;
-}
-
-class NaClDescWrapper {
- public:
-  explicit NaClDescWrapper(NaClDesc* desc): desc_(desc) {}
-  ~NaClDescWrapper() {
-    NaClDescUnref(desc_);
-  }
-
-  NaClDesc* desc() { return desc_; }
-
- private:
-  NaClDesc* desc_;
-  DISALLOW_COPY_AND_ASSIGN(NaClDescWrapper);
-};
-
-}  // namespace
-
-class NaClIPCAdapter::RewrittenMessage
-    : public base::RefCounted<RewrittenMessage> {
- public:
-  RewrittenMessage();
-
-  bool is_consumed() const { return data_read_cursor_ == data_len_; }
-
-  void SetData(const NaClIPCAdapter::NaClMessageHeader& header,
-               const void* payload, size_t payload_length);
-
-  int Read(NaClImcTypedMsgHdr* msg);
-
-  void AddDescriptor(NaClDescWrapper* desc) { descs_.push_back(desc); }
-
-  size_t desc_count() const { return descs_.size(); }
-
- private:
-  friend class base::RefCounted<RewrittenMessage>;
-  ~RewrittenMessage() {}
-
-  scoped_ptr<char[]> data_;
-  size_t data_len_;
-
-  // Offset into data where the next read will happen. This will be equal to
-  // data_len_ when all data has been consumed.
-  size_t data_read_cursor_;
-
-  // Wrapped descriptors for transfer to untrusted code.
-  ScopedVector<NaClDescWrapper> descs_;
-};
-
-NaClIPCAdapter::RewrittenMessage::RewrittenMessage()
-    : data_len_(0),
-      data_read_cursor_(0) {
-}
-
-void NaClIPCAdapter::RewrittenMessage::SetData(
-    const NaClIPCAdapter::NaClMessageHeader& header,
-    const void* payload,
-    size_t payload_length) {
-  DCHECK(!data_.get() && data_len_ == 0);
-  size_t header_len = sizeof(NaClIPCAdapter::NaClMessageHeader);
-  data_len_ = header_len + payload_length;
-  data_.reset(new char[data_len_]);
-
-  memcpy(data_.get(), &header, sizeof(NaClIPCAdapter::NaClMessageHeader));
-  memcpy(&data_[header_len], payload, payload_length);
-}
-
-int NaClIPCAdapter::RewrittenMessage::Read(NaClImcTypedMsgHdr* msg) {
-  CHECK(data_len_ >= data_read_cursor_);
-  char* dest_buffer = static_cast<char*>(msg->iov[0].base);
-  size_t dest_buffer_size = msg->iov[0].length;
-  size_t bytes_to_write = std::min(dest_buffer_size,
-                                   data_len_ - data_read_cursor_);
-  if (bytes_to_write == 0)
-    return 0;
-
-  memcpy(dest_buffer, &data_[data_read_cursor_], bytes_to_write);
-  data_read_cursor_ += bytes_to_write;
-
-  // Once all data has been consumed, transfer any file descriptors.
-  if (is_consumed()) {
-    nacl_abi_size_t desc_count = static_cast<nacl_abi_size_t>(descs_.size());
-    CHECK(desc_count <= msg->ndesc_length);
-    msg->ndesc_length = desc_count;
-    for (nacl_abi_size_t i = 0; i < desc_count; i++) {
-      // Copy the NaClDesc to the buffer and add a ref so it won't be freed
-      // when we clear our ScopedVector.
-      msg->ndescv[i] = descs_[i]->desc();
-      NaClDescRef(descs_[i]->desc());
-    }
-    descs_.clear();
-  } else {
-    msg->ndesc_length = 0;
-  }
-  return static_cast<int>(bytes_to_write);
-}
-
-NaClIPCAdapter::LockedData::LockedData()
-    : channel_closed_(false) {
-}
-
-NaClIPCAdapter::LockedData::~LockedData() {
-}
-
-NaClIPCAdapter::IOThreadData::IOThreadData() {
-}
-
-NaClIPCAdapter::IOThreadData::~IOThreadData() {
-}
-
-NaClIPCAdapter::NaClIPCAdapter(const IPC::ChannelHandle& handle,
-                               base::TaskRunner* runner)
-    : lock_(),
-      cond_var_(&lock_),
-      task_runner_(runner),
-      locked_data_() {
-  io_thread_data_.channel_.reset(
-      new IPC::Channel(handle, IPC::Channel::MODE_SERVER, this));
-  // Note, we can not PostTask for ConnectChannelOnIOThread here. If we did,
-  // and that task ran before this constructor completes, the reference count
-  // would go to 1 and then to 0 because of the Task, before we've been returned
-  // to the owning scoped_refptr, which is supposed to give us our first
-  // ref-count.
-}
-
-NaClIPCAdapter::NaClIPCAdapter(scoped_ptr<IPC::Channel> channel,
-                               base::TaskRunner* runner)
-    : lock_(),
-      cond_var_(&lock_),
-      task_runner_(runner),
-      locked_data_() {
-  io_thread_data_.channel_ = channel.Pass();
-}
-
-void NaClIPCAdapter::ConnectChannel() {
-  task_runner_->PostTask(FROM_HERE,
-      base::Bind(&NaClIPCAdapter::ConnectChannelOnIOThread, this));
-}
-
-// Note that this message is controlled by the untrusted code. So we should be
-// skeptical of anything it contains and quick to give up if anything is fishy.
-int NaClIPCAdapter::Send(const NaClImcTypedMsgHdr* msg) {
-  if (msg->iov_length != 1)
-    return -1;
-
-  base::AutoLock lock(lock_);
-
-  const char* input_data = static_cast<char*>(msg->iov[0].base);
-  size_t input_data_len = msg->iov[0].length;
-  if (input_data_len > IPC::Channel::kMaximumMessageSize) {
-    ClearToBeSent();
-    return -1;
-  }
-
-  // current_message[_len] refers to the total input data received so far.
-  const char* current_message;
-  size_t current_message_len;
-  bool did_append_input_data;
-  if (locked_data_.to_be_sent_.empty()) {
-    // No accumulated data, we can avoid a copy by referring to the input
-    // buffer (the entire message fitting in one call is the common case).
-    current_message = input_data;
-    current_message_len = input_data_len;
-    did_append_input_data = false;
-  } else {
-    // We've already accumulated some data, accumulate this new data and
-    // point to the beginning of the buffer.
-
-    // Make sure our accumulated message size doesn't overflow our max. Since
-    // we know that data_len < max size (checked above) and our current
-    // accumulated value is also < max size, we just need to make sure that
-    // 2x max size can never overflow.
-    COMPILE_ASSERT(IPC::Channel::kMaximumMessageSize < (UINT_MAX / 2),
-                   MaximumMessageSizeWillOverflow);
-    size_t new_size = locked_data_.to_be_sent_.size() + input_data_len;
-    if (new_size > IPC::Channel::kMaximumMessageSize) {
-      ClearToBeSent();
-      return -1;
-    }
-
-    locked_data_.to_be_sent_.append(input_data, input_data_len);
-    current_message = &locked_data_.to_be_sent_[0];
-    current_message_len = locked_data_.to_be_sent_.size();
-    did_append_input_data = true;
-  }
-
-  // Check the total data we've accumulated so far to see if it contains a full
-  // message.
-  switch (GetBufferStatus(current_message, current_message_len)) {
-    case MESSAGE_IS_COMPLETE: {
-      // Got a complete message, can send it out. This will be the common case.
-      bool success = SendCompleteMessage(current_message, current_message_len);
-      ClearToBeSent();
-      return success ? static_cast<int>(input_data_len) : -1;
-    }
-    case MESSAGE_IS_TRUNCATED:
-      // For truncated messages, just accumulate the new data (if we didn't
-      // already do so above) and go back to waiting for more.
-      if (!did_append_input_data)
-        locked_data_.to_be_sent_.append(input_data, input_data_len);
-      return static_cast<int>(input_data_len);
-    case MESSAGE_HAS_EXTRA_DATA:
-    default:
-      // When the plugin gives us too much data, it's an error.
-      ClearToBeSent();
-      return -1;
-  }
-}
-
-int NaClIPCAdapter::BlockingReceive(NaClImcTypedMsgHdr* msg) {
-  if (msg->iov_length != 1)
-    return -1;
-
-  int retval = 0;
-  {
-    base::AutoLock lock(lock_);
-    while (locked_data_.to_be_received_.empty() &&
-           !locked_data_.channel_closed_)
-      cond_var_.Wait();
-    if (locked_data_.channel_closed_) {
-      retval = -1;
-    } else {
-      retval = LockedReceive(msg);
-      DCHECK(retval > 0);
-    }
-  }
-  cond_var_.Signal();
-  return retval;
-}
-
-void NaClIPCAdapter::CloseChannel() {
-  {
-    base::AutoLock lock(lock_);
-    locked_data_.channel_closed_ = true;
-  }
-  cond_var_.Signal();
-
-  task_runner_->PostTask(FROM_HERE,
-      base::Bind(&NaClIPCAdapter::CloseChannelOnIOThread, this));
-}
-
-NaClDesc* NaClIPCAdapter::MakeNaClDesc() {
-  return MakeNaClDescCustom(this);
-}
-
-#if defined(OS_POSIX)
-int NaClIPCAdapter::TakeClientFileDescriptor() {
-  return io_thread_data_.channel_->TakeClientFileDescriptor();
-}
-#endif
-
-bool NaClIPCAdapter::OnMessageReceived(const IPC::Message& msg) {
-  {
-    base::AutoLock lock(lock_);
-
-    scoped_refptr<RewrittenMessage> rewritten_msg(new RewrittenMessage);
-
-    typedef std::vector<ppapi::proxy::SerializedHandle> Handles;
-    Handles handles;
-    scoped_ptr<IPC::Message> new_msg_ptr;
-    bool success = locked_data_.handle_converter_.ConvertNativeHandlesToPosix(
-        msg, &handles, &new_msg_ptr);
-    if (!success)
-      return false;
-
-    // Now add any descriptors we found to rewritten_msg. |handles| is usually
-    // empty, unless we read a message containing a FD or handle.
-    for (Handles::const_iterator iter = handles.begin();
-         iter != handles.end();
-         ++iter) {
-      scoped_ptr<NaClDescWrapper> nacl_desc;
-      switch (iter->type()) {
-        case ppapi::proxy::SerializedHandle::SHARED_MEMORY: {
-          const base::SharedMemoryHandle& shm_handle = iter->shmem();
-          uint32_t size = iter->size();
-          nacl_desc.reset(new NaClDescWrapper(NaClDescImcShmMake(
-#if defined(OS_WIN)
-              shm_handle,
-#else
-              shm_handle.fd,
-#endif
-              static_cast<size_t>(size))));
-          break;
-        }
-        case ppapi::proxy::SerializedHandle::SOCKET: {
-          nacl_desc.reset(new NaClDescWrapper(NaClDescSyncSocketMake(
-#if defined(OS_WIN)
-              iter->descriptor()
-#else
-              iter->descriptor().fd
-#endif
-          )));
-          break;
-        }
-        case ppapi::proxy::SerializedHandle::CHANNEL_HANDLE: {
-          // Check that this came from a PpapiMsg_CreateNaClChannel message.
-          // This code here is only appropriate for that message.
-          DCHECK(msg.type() == PpapiMsg_CreateNaClChannel::ID);
-          IPC::ChannelHandle channel_handle =
-              IPC::Channel::GenerateVerifiedChannelID("nacl");
-          scoped_refptr<NaClIPCAdapter> ipc_adapter(
-              new NaClIPCAdapter(channel_handle, task_runner_.get()));
-          ipc_adapter->ConnectChannel();
-#if defined(OS_POSIX)
-          channel_handle.socket = base::FileDescriptor(
-              ipc_adapter->TakeClientFileDescriptor(), true);
-#endif
-          nacl_desc.reset(new NaClDescWrapper(ipc_adapter->MakeNaClDesc()));
-          // Send back a message that the channel was created.
-          scoped_ptr<IPC::Message> response(
-              new PpapiHostMsg_ChannelCreated(channel_handle));
-          task_runner_->PostTask(FROM_HERE,
-              base::Bind(&NaClIPCAdapter::SendMessageOnIOThread, this,
-                         base::Passed(&response)));
-          break;
-        }
-        case ppapi::proxy::SerializedHandle::FILE:
-          // IMPORTANT: The NaClDescIoDescFromHandleAllocCtor function creates
-          // a NaClDesc that checks file flags before reading and writing. This
-          // is essential since PPB_FileIO now sends a file descriptor to the
-          // plugin which may have write capabilities. We can't allow the plugin
-          // to write with it since it could bypass quota checks, which still
-          // happen in the host.
-          nacl_desc.reset(new NaClDescWrapper(NaClDescIoDescFromHandleAllocCtor(
-#if defined(OS_WIN)
-              iter->descriptor(),
-#else
-              iter->descriptor().fd,
-#endif
-              TranslatePepperFileReadWriteOpenFlags(iter->open_flag()))));
-          break;
-        case ppapi::proxy::SerializedHandle::INVALID: {
-          // Nothing to do. TODO(dmichael): Should we log this? Or is it
-          // sometimes okay to pass an INVALID handle?
-          break;
-        }
-        // No default, so the compiler will warn us if new types get added.
-      }
-      if (nacl_desc.get())
-        rewritten_msg->AddDescriptor(nacl_desc.release());
-    }
-    if (new_msg_ptr && !handles.empty())
-      SaveMessage(*new_msg_ptr, rewritten_msg.get());
-    else
-      SaveMessage(msg, rewritten_msg.get());
-  }
-  cond_var_.Signal();
-  return true;
-}
-
-void NaClIPCAdapter::OnChannelConnected(int32 peer_pid) {
-}
-
-void NaClIPCAdapter::OnChannelError() {
-  CloseChannel();
-}
-
-NaClIPCAdapter::~NaClIPCAdapter() {
-  // Make sure the channel is deleted on the IO thread.
-  task_runner_->PostTask(FROM_HERE,
-      base::Bind(&DeleteChannel, io_thread_data_.channel_.release()));
-}
-
-int NaClIPCAdapter::LockedReceive(NaClImcTypedMsgHdr* msg) {
-  lock_.AssertAcquired();
-
-  if (locked_data_.to_be_received_.empty())
-    return 0;
-  scoped_refptr<RewrittenMessage> current =
-      locked_data_.to_be_received_.front();
-
-  int retval = current->Read(msg);
-
-  // When a message is entirely consumed, remove if from the waiting queue.
-  if (current->is_consumed())
-    locked_data_.to_be_received_.pop();
-
-  return retval;
-}
-
-bool NaClIPCAdapter::SendCompleteMessage(const char* buffer,
-                                         size_t buffer_len) {
-  lock_.AssertAcquired();
-  // The message will have already been validated, so we know it's large enough
-  // for our header.
-  const NaClMessageHeader* header =
-      reinterpret_cast<const NaClMessageHeader*>(buffer);
-
-  // Length of the message not including the body. The data passed to us by the
-  // plugin should match that in the message header. This should have already
-  // been validated by GetBufferStatus.
-  int body_len = static_cast<int>(buffer_len - sizeof(NaClMessageHeader));
-  DCHECK(body_len == static_cast<int>(header->payload_size));
-
-  // We actually discard the flags and only copy the ones we care about. This
-  // is just because message doesn't have a constructor that takes raw flags.
-  scoped_ptr<IPC::Message> msg(
-      new IPC::Message(header->routing, header->type,
-                       IPC::Message::PRIORITY_NORMAL));
-  if (header->flags & IPC::Message::SYNC_BIT)
-    msg->set_sync();
-  if (header->flags & IPC::Message::REPLY_BIT)
-    msg->set_reply();
-  if (header->flags & IPC::Message::REPLY_ERROR_BIT)
-    msg->set_reply_error();
-  if (header->flags & IPC::Message::UNBLOCK_BIT)
-    msg->set_unblock(true);
-
-  msg->WriteBytes(&buffer[sizeof(NaClMessageHeader)], body_len);
-
-  // Technically we didn't have to do any of the previous work in the lock. But
-  // sometimes our buffer will point to the to_be_sent_ string which is
-  // protected by the lock, and it's messier to factor Send() such that it can
-  // unlock for us. Holding the lock for the message construction, which is
-  // just some memcpys, shouldn't be a big deal.
-  lock_.AssertAcquired();
-  if (locked_data_.channel_closed_)
-    return false;  // TODO(brettw) clean up handles here when we add support!
-
-  if (msg->is_sync()) {
-    locked_data_.handle_converter_.RegisterSyncMessageForReply(*msg);
-  }
-  // Actual send must be done on the I/O thread.
-  task_runner_->PostTask(FROM_HERE,
-      base::Bind(&NaClIPCAdapter::SendMessageOnIOThread, this,
-                 base::Passed(&msg)));
-  return true;
-}
-
-void NaClIPCAdapter::ClearToBeSent() {
-  lock_.AssertAcquired();
-
-  // Don't let the string keep its buffer behind our back.
-  std::string empty;
-  locked_data_.to_be_sent_.swap(empty);
-}
-
-void NaClIPCAdapter::ConnectChannelOnIOThread() {
-  if (!io_thread_data_.channel_->Connect())
-    NOTREACHED();
-}
-
-void NaClIPCAdapter::CloseChannelOnIOThread() {
-  io_thread_data_.channel_->Close();
-}
-
-void NaClIPCAdapter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
-  io_thread_data_.channel_->Send(message.release());
-}
-
-void NaClIPCAdapter::SaveMessage(const IPC::Message& msg,
-                                 RewrittenMessage* rewritten_msg) {
-  lock_.AssertAcquired();
-  // There is some padding in this structure (the "padding" member is 16
-  // bits but this then gets padded to 32 bits). We want to be sure not to
-  // leak data to the untrusted plugin, so zero everything out first.
-  NaClMessageHeader header;
-  memset(&header, 0, sizeof(NaClMessageHeader));
-
-  header.payload_size = static_cast<uint32>(msg.payload_size());
-  header.routing = msg.routing_id();
-  header.type = msg.type();
-  header.flags = msg.flags();
-  header.num_fds = static_cast<int>(rewritten_msg->desc_count());
-
-  rewritten_msg->SetData(header, msg.payload(), msg.payload_size());
-  locked_data_.to_be_received_.push(rewritten_msg);
-}
-
-int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags) {
-  return TranslatePepperFileReadWriteOpenFlags(pp_open_flags);
-}
diff --git a/chrome/nacl/nacl_ipc_adapter.h b/chrome/nacl/nacl_ipc_adapter.h
deleted file mode 100644
index 3fe9aa0..0000000
--- a/chrome/nacl/nacl_ipc_adapter.h
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_NACL_NACL_IPC_ADAPTER_H_
-#define CHROME_NACL_NACL_IPC_ADAPTER_H_
-
-#include <map>
-#include <queue>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-#include "base/memory/shared_memory.h"
-#include "base/pickle.h"
-#include "base/synchronization/condition_variable.h"
-#include "base/synchronization/lock.h"
-#include "base/task_runner.h"
-#include "ipc/ipc_listener.h"
-#include "ppapi/c/pp_stdint.h"
-#include "ppapi/proxy/handle_converter.h"
-
-struct NaClDesc;
-struct NaClImcTypedMsgHdr;
-struct PP_Size;
-
-namespace IPC {
-class Channel;
-struct ChannelHandle;
-}
-
-namespace ppapi {
-class HostResource;
-}
-
-// Adapts a Chrome IPC channel to an IPC channel that we expose to Native
-// Client. This provides a mapping in both directions, so when IPC messages
-// come in from another process, we rewrite them and allow them to be received
-// via a recvmsg-like interface in the NaCl code. When NaCl code calls sendmsg,
-// we implement that as sending IPC messages on the channel.
-//
-// This object also provides the necessary logic for rewriting IPC messages.
-// NaCl code is platform-independent and runs in a Posix-like enviroment, but
-// some formatting in the message and the way handles are transferred varies
-// by platform. This class bridges that gap to provide what looks like a
-// normal platform-specific IPC implementation to Chrome, and a Posix-like
-// version on every platform to NaCl.
-//
-// This object must be threadsafe since the nacl environment determines which
-// thread every function is called on.
-class NaClIPCAdapter : public base::RefCountedThreadSafe<NaClIPCAdapter>,
-                       public IPC::Listener {
- public:
-  // Chrome's IPC message format varies by platform, NaCl's does not. In
-  // particular, the header has some extra fields on Posix platforms. Since
-  // NaCl is a Posix environment, it gets that version of the header. This
-  // header is duplicated here so we have a cross-platform definition of the
-  // header we're exposing to NaCl.
-#pragma pack(push, 4)
-  struct NaClMessageHeader : public Pickle::Header {
-    int32 routing;
-    uint32 type;
-    uint32 flags;
-    uint16 num_fds;
-    uint16 pad;
-  };
-#pragma pack(pop)
-
-  // Creates an adapter, using the thread associated with the given task
-  // runner for posting messages. In normal use, the task runner will post to
-  // the I/O thread of the process.
-  //
-  // If you use this constructor, you MUST call ConnectChannel after the
-  // NaClIPCAdapter is constructed, or the NaClIPCAdapter's channel will not be
-  // connected.
-  NaClIPCAdapter(const IPC::ChannelHandle& handle, base::TaskRunner* runner);
-
-  // Initializes with a given channel that's already created for testing
-  // purposes. This function will take ownership of the given channel.
-  NaClIPCAdapter(scoped_ptr<IPC::Channel> channel, base::TaskRunner* runner);
-
-  // Connect the channel. This must be called after the constructor that accepts
-  // an IPC::ChannelHandle, and causes the Channel to be connected on the IO
-  // thread.
-  void ConnectChannel();
-
-  // Implementation of sendmsg. Returns the number of bytes written or -1 on
-  // failure.
-  int Send(const NaClImcTypedMsgHdr* msg);
-
-  // Implementation of recvmsg. Returns the number of bytes read or -1 on
-  // failure. This will block until there's an error or there is data to
-  // read.
-  int BlockingReceive(NaClImcTypedMsgHdr* msg);
-
-  // Closes the IPC channel.
-  void CloseChannel();
-
-  // Make a NaClDesc that refers to this NaClIPCAdapter. Note that the returned
-  // NaClDesc is reference-counted, and a reference is returned.
-  NaClDesc* MakeNaClDesc();
-
-#if defined(OS_POSIX)
-  int TakeClientFileDescriptor();
-#endif
-
-  // Listener implementation.
-  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
-  virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
-  virtual void OnChannelError() OVERRIDE;
-
- private:
-  friend class base::RefCountedThreadSafe<NaClIPCAdapter>;
-
-  class RewrittenMessage;
-
-  // This is the data that must only be accessed inside the lock. This struct
-  // just separates it so it's easier to see.
-  struct LockedData {
-    LockedData();
-    ~LockedData();
-
-    // Messages that we have read off of the Chrome IPC channel that are waiting
-    // to be received by the plugin.
-    std::queue< scoped_refptr<RewrittenMessage> > to_be_received_;
-
-    ppapi::proxy::HandleConverter handle_converter_;
-
-    // Data that we've queued from the plugin to send, but doesn't consist of a
-    // full message yet. The calling code can break apart the message into
-    // smaller pieces, and we need to send the message to the other process in
-    // one chunk.
-    //
-    // The IPC channel always starts a new send() at the beginning of each
-    // message, so we don't need to worry about arbitrary message boundaries.
-    std::string to_be_sent_;
-
-    bool channel_closed_;
-  };
-
-  // This is the data that must only be accessed on the I/O thread (as defined
-  // by TaskRunner). This struct just separates it so it's easier to see.
-  struct IOThreadData {
-    IOThreadData();
-    ~IOThreadData();
-
-    scoped_ptr<IPC::Channel> channel_;
-  };
-
-  virtual ~NaClIPCAdapter();
-
-  // Returns 0 if nothing is waiting.
-  int LockedReceive(NaClImcTypedMsgHdr* msg);
-
-  // Sends a message that we know has been completed to the Chrome process.
-  bool SendCompleteMessage(const char* buffer, size_t buffer_len);
-
-  // Clears the LockedData.to_be_sent_ structure in a way to make sure that
-  // the memory is deleted. std::string can sometimes hold onto the buffer
-  // for future use which we don't want.
-  void ClearToBeSent();
-
-  void ConnectChannelOnIOThread();
-  void CloseChannelOnIOThread();
-  void SendMessageOnIOThread(scoped_ptr<IPC::Message> message);
-
-  // Saves the message to forward to NaCl. This method assumes that the caller
-  // holds the lock for locked_data_.
-  void SaveMessage(const IPC::Message& message,
-                   RewrittenMessage* rewritten_message);
-
-  base::Lock lock_;
-  base::ConditionVariable cond_var_;
-
-  scoped_refptr<base::TaskRunner> task_runner_;
-
-  // To be accessed inside of lock_ only.
-  LockedData locked_data_;
-
-  // To be accessed on the I/O thread (via task runner) only.
-  IOThreadData io_thread_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(NaClIPCAdapter);
-};
-
-// Export TranslatePepperFileReadWriteOpenFlags for testing.
-int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags);
-
-#endif  // CHROME_NACL_NACL_IPC_ADAPTER_H_
diff --git a/chrome/nacl/nacl_ipc_adapter_unittest.cc b/chrome/nacl/nacl_ipc_adapter_unittest.cc
deleted file mode 100644
index 9697d58..0000000
--- a/chrome/nacl/nacl_ipc_adapter_unittest.cc
+++ /dev/null
@@ -1,357 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/nacl/nacl_ipc_adapter.h"
-
-#include <string.h>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_loop_proxy.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/simple_thread.h"
-#include "ipc/ipc_test_sink.h"
-#include "native_client/src/trusted/desc/nacl_desc_custom.h"
-#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
-#include "ppapi/c/ppb_file_io.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-class NaClIPCAdapterTest : public testing::Test {
- public:
-  NaClIPCAdapterTest() {}
-
-  // testing::Test implementation.
-  virtual void SetUp() OVERRIDE {
-    sink_ = new IPC::TestSink;
-
-    // Takes ownership of the sink_ pointer. Note we provide the current message
-    // loop instead of using a real IO thread. This should work OK since we do
-    // not need real IPC for the tests.
-    adapter_ = new NaClIPCAdapter(scoped_ptr<IPC::Channel>(sink_),
-                                  base::MessageLoopProxy::current().get());
-  }
-  virtual void TearDown() OVERRIDE {
-    sink_ = NULL;  // This pointer is actually owned by the IPCAdapter.
-    adapter_ = NULL;
-    // The adapter destructor has to post a task to destroy the Channel on the
-    // IO thread. For the purposes of the test, we just need to make sure that
-    // task gets run, or it will appear as a leak.
-    message_loop_.RunUntilIdle();
-  }
-
- protected:
-  int BlockingReceive(void* buf, size_t buf_size) {
-    NaClImcMsgIoVec iov = {buf, buf_size};
-    NaClImcTypedMsgHdr msg = {&iov, 1};
-    return adapter_->BlockingReceive(&msg);
-  }
-
-  int Send(void* buf, size_t buf_size) {
-    NaClImcMsgIoVec iov = {buf, buf_size};
-    NaClImcTypedMsgHdr msg = {&iov, 1};
-    return adapter_->Send(&msg);
-  }
-
-  base::MessageLoop message_loop_;
-
-  scoped_refptr<NaClIPCAdapter> adapter_;
-
-  // Messages sent from nacl to the adapter end up here. Note that we create
-  // this pointer and pass ownership of it to the IPC adapter, who will keep
-  // it alive as long as the adapter is alive. This means that when the
-  // adapter goes away, this pointer will become invalid.
-  //
-  // In real life the adapter needs to take ownership so the channel can be
-  // destroyed on the right thread.
-  IPC::TestSink* sink_;
-};
-
-}  // namespace
-
-// Tests a simple message getting rewritten sent from native code to NaCl.
-TEST_F(NaClIPCAdapterTest, SimpleReceiveRewriting) {
-  int routing_id = 0x89898989;
-  uint32 type = 0x55555555;
-  IPC::Message input(routing_id, type, IPC::Message::PRIORITY_NORMAL);
-  uint32 flags = input.flags();
-
-  int value = 0x12345678;
-  input.WriteInt(value);
-  adapter_->OnMessageReceived(input);
-
-  // Buffer just need to be big enough for our message with one int.
-  const int kBufSize = 64;
-  char buf[kBufSize];
-
-  int bytes_read = BlockingReceive(buf, kBufSize);
-  EXPECT_EQ(sizeof(NaClIPCAdapter::NaClMessageHeader) + sizeof(int),
-            static_cast<size_t>(bytes_read));
-
-  const NaClIPCAdapter::NaClMessageHeader* output_header =
-      reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(buf);
-  EXPECT_EQ(sizeof(int), output_header->payload_size);
-  EXPECT_EQ(routing_id, output_header->routing);
-  EXPECT_EQ(type, output_header->type);
-  EXPECT_EQ(flags, output_header->flags);
-  EXPECT_EQ(0u, output_header->num_fds);
-  EXPECT_EQ(0u, output_header->pad);
-
-  // Validate the payload.
-  EXPECT_EQ(value,
-            *reinterpret_cast<const int*>(&buf[
-                sizeof(NaClIPCAdapter::NaClMessageHeader)]));
-}
-
-// Tests a simple message getting rewritten sent from NaCl to native code.
-TEST_F(NaClIPCAdapterTest, SendRewriting) {
-  int routing_id = 0x89898989;
-  uint32 type = 0x55555555;
-  int value = 0x12345678;
-
-  // Send a message with one int inside it.
-  const int buf_size = sizeof(NaClIPCAdapter::NaClMessageHeader) + sizeof(int);
-  char buf[buf_size] = {0};
-
-  NaClIPCAdapter::NaClMessageHeader* header =
-      reinterpret_cast<NaClIPCAdapter::NaClMessageHeader*>(buf);
-  header->payload_size = sizeof(int);
-  header->routing = routing_id;
-  header->type = type;
-  header->flags = 0;
-  header->num_fds = 0;
-  *reinterpret_cast<int*>(
-      &buf[sizeof(NaClIPCAdapter::NaClMessageHeader)]) = value;
-
-  int result = Send(buf, buf_size);
-  EXPECT_EQ(buf_size, result);
-
-  // Check that the message came out the other end in the test sink
-  // (messages are posted, so we have to pump).
-  message_loop_.RunUntilIdle();
-  ASSERT_EQ(1u, sink_->message_count());
-  const IPC::Message* msg = sink_->GetMessageAt(0);
-
-  EXPECT_EQ(sizeof(int), msg->payload_size());
-  EXPECT_EQ(header->routing, msg->routing_id());
-  EXPECT_EQ(header->type, msg->type());
-
-  // Now test the partial send case. We should be able to break the message
-  // into two parts and it should still work.
-  sink_->ClearMessages();
-  int first_chunk_size = 7;
-  result = Send(buf, first_chunk_size);
-  EXPECT_EQ(first_chunk_size, result);
-
-  // First partial send should not have made any messages.
-  message_loop_.RunUntilIdle();
-  ASSERT_EQ(0u, sink_->message_count());
-
-  // Second partial send should do the same.
-  int second_chunk_size = 2;
-  result = Send(&buf[first_chunk_size], second_chunk_size);
-  EXPECT_EQ(second_chunk_size, result);
-  message_loop_.RunUntilIdle();
-  ASSERT_EQ(0u, sink_->message_count());
-
-  // Send the rest of the message in a third chunk.
-  int third_chunk_size = buf_size - first_chunk_size - second_chunk_size;
-  result = Send(&buf[first_chunk_size + second_chunk_size],
-                          third_chunk_size);
-  EXPECT_EQ(third_chunk_size, result);
-
-  // Last send should have generated one message.
-  message_loop_.RunUntilIdle();
-  ASSERT_EQ(1u, sink_->message_count());
-  msg = sink_->GetMessageAt(0);
-  EXPECT_EQ(sizeof(int), msg->payload_size());
-  EXPECT_EQ(header->routing, msg->routing_id());
-  EXPECT_EQ(header->type, msg->type());
-}
-
-// Tests when a buffer is too small to receive the entire message.
-TEST_F(NaClIPCAdapterTest, PartialReceive) {
-  int routing_id_1 = 0x89898989;
-  uint32 type_1 = 0x55555555;
-  IPC::Message input_1(routing_id_1, type_1, IPC::Message::PRIORITY_NORMAL);
-  int value_1 = 0x12121212;
-  input_1.WriteInt(value_1);
-  adapter_->OnMessageReceived(input_1);
-
-  int routing_id_2 = 0x90909090;
-  uint32 type_2 = 0x66666666;
-  IPC::Message input_2(routing_id_2, type_2, IPC::Message::PRIORITY_NORMAL);
-  int value_2 = 0x23232323;
-  input_2.WriteInt(value_2);
-  adapter_->OnMessageReceived(input_2);
-
-  const int kBufSize = 64;
-  char buf[kBufSize];
-
-  // Read part of the first message.
-  int bytes_requested = 7;
-  int bytes_read = BlockingReceive(buf, bytes_requested);
-  ASSERT_EQ(bytes_requested, bytes_read);
-
-  // Read the rest, this should give us the rest of the first message only.
-  bytes_read += BlockingReceive(&buf[bytes_requested],
-                                        kBufSize - bytes_requested);
-  EXPECT_EQ(sizeof(NaClIPCAdapter::NaClMessageHeader) + sizeof(int),
-            static_cast<size_t>(bytes_read));
-
-  // Make sure we got the right message.
-  const NaClIPCAdapter::NaClMessageHeader* output_header =
-      reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(buf);
-  EXPECT_EQ(sizeof(int), output_header->payload_size);
-  EXPECT_EQ(routing_id_1, output_header->routing);
-  EXPECT_EQ(type_1, output_header->type);
-
-  // Read the second message to make sure we went on to it.
-  bytes_read = BlockingReceive(buf, kBufSize);
-  EXPECT_EQ(sizeof(NaClIPCAdapter::NaClMessageHeader) + sizeof(int),
-            static_cast<size_t>(bytes_read));
-  output_header =
-      reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(buf);
-  EXPECT_EQ(sizeof(int), output_header->payload_size);
-  EXPECT_EQ(routing_id_2, output_header->routing);
-  EXPECT_EQ(type_2, output_header->type);
-}
-
-// Tests sending messages that are too large. We test sends that are too
-// small implicitly here and in the success case because in that case it
-// succeeds and buffers the data.
-TEST_F(NaClIPCAdapterTest, SendOverflow) {
-  int routing_id = 0x89898989;
-  uint32 type = 0x55555555;
-  int value = 0x12345678;
-
-  // Make a message with one int inside it. Reserve some extra space so
-  // we can test what happens when we send too much data.
-  const int buf_size = sizeof(NaClIPCAdapter::NaClMessageHeader) + sizeof(int);
-  const int big_buf_size = buf_size + 4;
-  char buf[big_buf_size] = {0};
-
-  NaClIPCAdapter::NaClMessageHeader* header =
-      reinterpret_cast<NaClIPCAdapter::NaClMessageHeader*>(buf);
-  header->payload_size = sizeof(int);
-  header->routing = routing_id;
-  header->type = type;
-  header->flags = 0;
-  header->num_fds = 0;
-  *reinterpret_cast<int*>(
-      &buf[sizeof(NaClIPCAdapter::NaClMessageHeader)]) = value;
-
-  // Send too much data and make sure that the send fails.
-  int result = Send(buf, big_buf_size);
-  EXPECT_EQ(-1, result);
-  message_loop_.RunUntilIdle();
-  ASSERT_EQ(0u, sink_->message_count());
-
-  // Send too much data in two chunks and make sure that the send fails.
-  int first_chunk_size = 7;
-  result = Send(buf, first_chunk_size);
-  EXPECT_EQ(first_chunk_size, result);
-
-  // First partial send should not have made any messages.
-  message_loop_.RunUntilIdle();
-  ASSERT_EQ(0u, sink_->message_count());
-
-  int second_chunk_size = big_buf_size - first_chunk_size;
-  result = Send(&buf[first_chunk_size], second_chunk_size);
-  EXPECT_EQ(-1, result);
-  message_loop_.RunUntilIdle();
-  ASSERT_EQ(0u, sink_->message_count());
-}
-
-// Tests that when the IPC channel reports an error, that waiting reads are
-// unblocked and return a -1 error code.
-TEST_F(NaClIPCAdapterTest, ReadWithChannelError) {
-  // Have a background thread that waits a bit and calls the channel error
-  // handler. This should wake up any waiting threads and immediately return
-  // -1. There is an inherent race condition in that we can't be sure if the
-  // other thread is actually waiting when this happens. This is OK, since the
-  // behavior (which we also explicitly test later) is to return -1 if the
-  // channel has already had an error when you start waiting.
-  class MyThread : public base::SimpleThread {
-   public:
-    explicit MyThread(NaClIPCAdapter* adapter)
-        : SimpleThread("NaClIPCAdapterThread"),
-          adapter_(adapter) {}
-    virtual void Run() OVERRIDE {
-      base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
-      adapter_->OnChannelError();
-    }
-   private:
-    scoped_refptr<NaClIPCAdapter> adapter_;
-  };
-  MyThread thread(adapter_.get());
-
-  // IMPORTANT: do not return early from here down (including ASSERT_*) because
-  // the thread needs to joined or it will assert.
-  thread.Start();
-
-  // Request data. This will normally (modulo races) block until data is
-  // received or there is an error, and the thread above will wake us up
-  // after 1s.
-  const int kBufSize = 64;
-  char buf[kBufSize];
-  int result = BlockingReceive(buf, kBufSize);
-  EXPECT_EQ(-1, result);
-
-  // Test the "previously had an error" case. BlockingReceive should return
-  // immediately if there was an error.
-  result = BlockingReceive(buf, kBufSize);
-  EXPECT_EQ(-1, result);
-
-  thread.Join();
-}
-
-// Tests that TranslatePepperFileOpenFlags translates pepper read/write open
-// flags into NaCl open flags correctly.
-TEST_F(NaClIPCAdapterTest, TranslatePepperFileReadWriteOpenFlags) {
-  EXPECT_EQ(NACL_ABI_O_RDONLY,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(PP_FILEOPENFLAG_READ));
-  EXPECT_EQ(NACL_ABI_O_WRONLY,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(PP_FILEOPENFLAG_WRITE));
-  EXPECT_EQ(NACL_ABI_O_WRONLY | NACL_ABI_O_APPEND,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(
-          PP_FILEOPENFLAG_APPEND));
-  EXPECT_EQ(NACL_ABI_O_RDWR,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(
-          PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE));
-  EXPECT_EQ(NACL_ABI_O_WRONLY | NACL_ABI_O_APPEND,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(
-          PP_FILEOPENFLAG_APPEND));
-  EXPECT_EQ(NACL_ABI_O_RDWR | NACL_ABI_O_APPEND,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(
-          PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_APPEND));
-
-  // Flags other than PP_FILEOPENFLAG_READ, PP_FILEOPENFLAG_WRITE, and
-  // PP_FILEOPENFLAG_APPEND are discarded.
-  EXPECT_EQ(NACL_ABI_O_WRONLY,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(
-          PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE));
-  EXPECT_EQ(NACL_ABI_O_WRONLY,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(
-          PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_TRUNCATE));
-  EXPECT_EQ(NACL_ABI_O_WRONLY,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(
-          PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_EXCLUSIVE));
-
-  // If none of PP_FILEOPENFLAG_READ, PP_FILEOPENFLAG_WRITE, and
-  // PP_FILEOPENFLAG_APPEND are set, the result should fall back to
-  // NACL_ABI_O_READONLY.
-  EXPECT_EQ(NACL_ABI_O_RDONLY,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(0));
-  EXPECT_EQ(NACL_ABI_O_RDONLY,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(
-          PP_FILEOPENFLAG_CREATE));
-  EXPECT_EQ(NACL_ABI_O_RDONLY,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(
-          PP_FILEOPENFLAG_TRUNCATE));
-  EXPECT_EQ(NACL_ABI_O_RDONLY,
-      TranslatePepperFileReadWriteOpenFlagsForTesting(
-          PP_FILEOPENFLAG_EXCLUSIVE));
-}
diff --git a/chrome/nacl/nacl_listener.cc b/chrome/nacl/nacl_listener.cc
deleted file mode 100644
index 7562daa..0000000
--- a/chrome/nacl/nacl_listener.cc
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/nacl/nacl_listener.h"
-
-#include <errno.h>
-#include <stdlib.h>
-
-#if defined(OS_POSIX)
-#include <unistd.h>
-#endif
-
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/rand_util.h"
-#include "chrome/nacl/nacl_ipc_adapter.h"
-#include "chrome/nacl/nacl_validation_db.h"
-#include "chrome/nacl/nacl_validation_query.h"
-#include "components/nacl/common/nacl_messages.h"
-#include "ipc/ipc_channel_handle.h"
-#include "ipc/ipc_switches.h"
-#include "ipc/ipc_sync_channel.h"
-#include "ipc/ipc_sync_message_filter.h"
-#include "native_client/src/trusted/service_runtime/sel_main_chrome.h"
-#include "native_client/src/trusted/validator/nacl_file_info.h"
-
-#if defined(OS_POSIX)
-#include "base/file_descriptor_posix.h"
-#endif
-
-#if defined(OS_LINUX)
-#include "content/public/common/child_process_sandbox_support_linux.h"
-#endif
-
-#if defined(OS_WIN)
-#include <fcntl.h>
-#include <io.h>
-
-#include "content/public/common/sandbox_init.h"
-#endif
-
-namespace {
-#if defined(OS_MACOSX)
-
-// On Mac OS X, shm_open() works in the sandbox but does not give us
-// an FD that we can map as PROT_EXEC.  Rather than doing an IPC to
-// get an executable SHM region when CreateMemoryObject() is called,
-// we preallocate one on startup, since NaCl's sel_ldr only needs one
-// of them.  This saves a round trip.
-
-base::subtle::Atomic32 g_shm_fd = -1;
-
-int CreateMemoryObject(size_t size, int executable) {
-  if (executable && size > 0) {
-    int result_fd = base::subtle::NoBarrier_AtomicExchange(&g_shm_fd, -1);
-    if (result_fd != -1) {
-      // ftruncate() is disallowed by the Mac OS X sandbox and
-      // returns EPERM.  Luckily, we can get the same effect with
-      // lseek() + write().
-      if (lseek(result_fd, size - 1, SEEK_SET) == -1) {
-        LOG(ERROR) << "lseek() failed: " << errno;
-        return -1;
-      }
-      if (write(result_fd, "", 1) != 1) {
-        LOG(ERROR) << "write() failed: " << errno;
-        return -1;
-      }
-      return result_fd;
-    }
-  }
-  // Fall back to NaCl's default implementation.
-  return -1;
-}
-
-#elif defined(OS_LINUX)
-
-int CreateMemoryObject(size_t size, int executable) {
-  return content::MakeSharedMemorySegmentViaIPC(size, executable);
-}
-
-#elif defined(OS_WIN)
-
-NaClListener* g_listener;
-
-// We wrap the function to convert the bool return value to an int.
-int BrokerDuplicateHandle(NaClHandle source_handle,
-                          uint32_t process_id,
-                          NaClHandle* target_handle,
-                          uint32_t desired_access,
-                          uint32_t options) {
-  return content::BrokerDuplicateHandle(source_handle, process_id,
-                                        target_handle, desired_access,
-                                        options);
-}
-
-int AttachDebugExceptionHandler(const void* info, size_t info_size) {
-  std::string info_string(reinterpret_cast<const char*>(info), info_size);
-  bool result = false;
-  if (!g_listener->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
-           info_string, &result)))
-    return false;
-  return result;
-}
-
-#endif
-
-}  // namespace
-
-class BrowserValidationDBProxy : public NaClValidationDB {
- public:
-  explicit BrowserValidationDBProxy(NaClListener* listener)
-      : listener_(listener) {
-  }
-
-  virtual bool QueryKnownToValidate(const std::string& signature) OVERRIDE {
-    // Initialize to false so that if the Send fails to write to the return
-    // value we're safe.  For example if the message is (for some reason)
-    // dispatched as an async message the return parameter will not be written.
-    bool result = false;
-    if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature,
-                                                                 &result))) {
-      LOG(ERROR) << "Failed to query NaCl validation cache.";
-      result = false;
-    }
-    return result;
-  }
-
-  virtual void SetKnownToValidate(const std::string& signature) OVERRIDE {
-    // Caching is optional: NaCl will still work correctly if the IPC fails.
-    if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) {
-      LOG(ERROR) << "Failed to update NaCl validation cache.";
-    }
-  }
-
-  virtual bool ResolveFileToken(struct NaClFileToken* file_token,
-                                int32* fd, std::string* path) OVERRIDE {
-    *fd = -1;
-    *path = "";
-    if (file_token->lo == 0 && file_token->hi == 0) {
-      return false;
-    }
-    IPC::PlatformFileForTransit ipc_fd = IPC::InvalidPlatformFileForTransit();
-    base::FilePath ipc_path;
-    if (!listener_->Send(new NaClProcessMsg_ResolveFileToken(file_token->lo,
-                                                             file_token->hi,
-                                                             &ipc_fd,
-                                                             &ipc_path))) {
-      return false;
-    }
-    if (ipc_fd == IPC::InvalidPlatformFileForTransit()) {
-      return false;
-    }
-    base::PlatformFile handle =
-        IPC::PlatformFileForTransitToPlatformFile(ipc_fd);
-#if defined(OS_WIN)
-    // On Windows, valid handles are 32 bit unsigned integers so this is safe.
-    *fd = reinterpret_cast<uintptr_t>(handle);
-#else
-    *fd = handle;
-#endif
-    // It doesn't matter if the path is invalid UTF8 as long as it's consistent
-    // and unforgeable.
-    *path = ipc_path.AsUTF8Unsafe();
-    return true;
-  }
-
- private:
-  // The listener never dies, otherwise this might be a dangling reference.
-  NaClListener* listener_;
-};
-
-
-NaClListener::NaClListener() : shutdown_event_(true, false),
-                               io_thread_("NaCl_IOThread"),
-#if defined(OS_LINUX)
-                               prereserved_sandbox_size_(0),
-#endif
-#if defined(OS_POSIX)
-                               number_of_cores_(-1),  // unknown/error
-#endif
-                               main_loop_(NULL) {
-  io_thread_.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
-#if defined(OS_WIN)
-  DCHECK(g_listener == NULL);
-  g_listener = this;
-#endif
-}
-
-NaClListener::~NaClListener() {
-  NOTREACHED();
-  shutdown_event_.Signal();
-#if defined(OS_WIN)
-  g_listener = NULL;
-#endif
-}
-
-bool NaClListener::Send(IPC::Message* msg) {
-  DCHECK(main_loop_ != NULL);
-  if (base::MessageLoop::current() == main_loop_) {
-    // This thread owns the channel.
-    return channel_->Send(msg);
-  } else {
-    // This thread does not own the channel.
-    return filter_->Send(msg);
-  }
-}
-
-void NaClListener::Listen() {
-  std::string channel_name =
-      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          switches::kProcessChannelID);
-  channel_.reset(new IPC::SyncChannel(
-      this, io_thread_.message_loop_proxy().get(), &shutdown_event_));
-  filter_ = new IPC::SyncMessageFilter(&shutdown_event_);
-  channel_->AddFilter(filter_.get());
-  channel_->Init(channel_name, IPC::Channel::MODE_CLIENT, true);
-  main_loop_ = base::MessageLoop::current();
-  main_loop_->Run();
-}
-
-bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(NaClListener, msg)
-      IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart)
-      IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-void NaClListener::OnStart(const nacl::NaClStartParams& params) {
-  struct NaClChromeMainArgs *args = NaClChromeMainArgsCreate();
-  if (args == NULL) {
-    LOG(ERROR) << "NaClChromeMainArgsCreate() failed";
-    return;
-  }
-
-  if (params.enable_ipc_proxy) {
-    // Create the initial PPAPI IPC channel between the NaCl IRT and the
-    // browser process. The IRT uses this channel to communicate with the
-    // browser and to create additional IPC channels to renderer processes.
-    IPC::ChannelHandle handle =
-        IPC::Channel::GenerateVerifiedChannelID("nacl");
-    scoped_refptr<NaClIPCAdapter> ipc_adapter(
-        new NaClIPCAdapter(handle, io_thread_.message_loop_proxy().get()));
-    ipc_adapter->ConnectChannel();
-
-    // Pass a NaClDesc to the untrusted side. This will hold a ref to the
-    // NaClIPCAdapter.
-    args->initial_ipc_desc = ipc_adapter->MakeNaClDesc();
-#if defined(OS_POSIX)
-    handle.socket = base::FileDescriptor(
-        ipc_adapter->TakeClientFileDescriptor(), true);
-#endif
-    if (!Send(new NaClProcessHostMsg_PpapiChannelCreated(handle)))
-      LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
-  }
-
-  std::vector<nacl::FileDescriptor> handles = params.handles;
-
-#if defined(OS_LINUX) || defined(OS_MACOSX)
-  args->urandom_fd = dup(base::GetUrandomFD());
-  if (args->urandom_fd < 0) {
-    LOG(ERROR) << "Failed to dup() the urandom FD";
-    return;
-  }
-  args->number_of_cores = number_of_cores_;
-  args->create_memory_object_func = CreateMemoryObject;
-# if defined(OS_MACOSX)
-  CHECK(handles.size() >= 1);
-  g_shm_fd = nacl::ToNativeHandle(handles[handles.size() - 1]);
-  handles.pop_back();
-# endif
-#endif
-
-  if (params.uses_irt) {
-    CHECK(handles.size() >= 1);
-    NaClHandle irt_handle = nacl::ToNativeHandle(handles[handles.size() - 1]);
-    handles.pop_back();
-
-#if defined(OS_WIN)
-    args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle),
-                                   _O_RDONLY | _O_BINARY);
-    if (args->irt_fd < 0) {
-      LOG(ERROR) << "_open_osfhandle() failed";
-      return;
-    }
-#else
-    args->irt_fd = irt_handle;
-#endif
-  } else {
-    // Otherwise, the IRT handle is not even sent.
-    args->irt_fd = -1;
-  }
-
-  if (params.validation_cache_enabled) {
-    // SHA256 block size.
-    CHECK_EQ(params.validation_cache_key.length(), (size_t) 64);
-    // The cache structure is not freed and exists until the NaCl process exits.
-    args->validation_cache = CreateValidationCache(
-        new BrowserValidationDBProxy(this), params.validation_cache_key,
-        params.version);
-  }
-
-  CHECK(handles.size() == 1);
-  args->imc_bootstrap_handle = nacl::ToNativeHandle(handles[0]);
-  args->enable_exception_handling = params.enable_exception_handling;
-  args->enable_debug_stub = params.enable_debug_stub;
-  args->enable_dyncode_syscalls = params.enable_dyncode_syscalls;
-  if (!params.enable_dyncode_syscalls) {
-    // Bound the initial nexe's code segment size under PNaCl to
-    // reduce the chance of a code spraying attack succeeding (see
-    // https://code.google.com/p/nativeclient/issues/detail?id=3572).
-    // We assume that !params.enable_dyncode_syscalls is synonymous
-    // with PNaCl.  We can't apply this arbitrary limit outside of
-    // PNaCl because it might break existing NaCl apps, and this limit
-    // is only useful if the dyncode syscalls are disabled.
-    args->initial_nexe_max_code_bytes = 32 << 20;  // 32 MB
-  }
-#if defined(OS_LINUX) || defined(OS_MACOSX)
-  args->debug_stub_server_bound_socket_fd = nacl::ToNativeHandle(
-      params.debug_stub_server_bound_socket);
-#endif
-#if defined(OS_WIN)
-  args->broker_duplicate_handle_func = BrokerDuplicateHandle;
-  args->attach_debug_exception_handler_func = AttachDebugExceptionHandler;
-#endif
-#if defined(OS_LINUX)
-  args->prereserved_sandbox_size = prereserved_sandbox_size_;
-#endif
-  NaClChromeMainStart(args);
-  NOTREACHED();
-}
diff --git a/chrome/nacl/nacl_listener.h b/chrome/nacl/nacl_listener.h
deleted file mode 100644
index 148cc71..0000000
--- a/chrome/nacl/nacl_listener.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_NACL_NACL_LISTENER_H_
-#define CHROME_NACL_NACL_LISTENER_H_
-
-#include <vector>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
-#include "components/nacl/common/nacl_types.h"
-#include "ipc/ipc_listener.h"
-
-namespace IPC {
-class SyncChannel;
-class SyncMessageFilter;
-}
-
-// The NaClListener is an IPC channel listener that waits for a
-// request to start a NaCl module.
-class NaClListener : public IPC::Listener {
- public:
-  NaClListener();
-  virtual ~NaClListener();
-  // Listen for a request to launch a NaCl module.
-  void Listen();
-
-  bool Send(IPC::Message* msg);
-
-#if defined(OS_LINUX)
-  void set_prereserved_sandbox_size(size_t prereserved_sandbox_size) {
-    prereserved_sandbox_size_ = prereserved_sandbox_size;
-  }
-#endif
-#if defined(OS_POSIX)
-  void set_number_of_cores(int number_of_cores) {
-    number_of_cores_ = number_of_cores;
-  }
-#endif
-
- private:
-  void OnStart(const nacl::NaClStartParams& params);
-  virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
-
-  // A channel back to the browser.
-  scoped_ptr<IPC::SyncChannel> channel_;
-
-  // A filter that allows other threads to use the channel.
-  scoped_refptr<IPC::SyncMessageFilter> filter_;
-
-  base::WaitableEvent shutdown_event_;
-  base::Thread io_thread_;
-
-#if defined(OS_LINUX)
-  size_t prereserved_sandbox_size_;
-#endif
-#if defined(OS_POSIX)
-  // The outer sandbox on Linux and OSX prevents
-  // sysconf(_SC_NPROCESSORS) from working; in Windows, there are no
-  // problems with invoking GetSystemInfo.  Therefore, only in
-  // OS_POSIX do we need to supply the number of cores into the
-  // NaClChromeMainArgs object.
-  int number_of_cores_;
-#endif
-
-  // Used to identify what thread we're on.
-  base::MessageLoop* main_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(NaClListener);
-};
-
-#endif  // CHROME_NACL_NACL_LISTENER_H_
diff --git a/chrome/nacl/nacl_main.cc b/chrome/nacl/nacl_main.cc
deleted file mode 100644
index 24f96f2..0000000
--- a/chrome/nacl/nacl_main.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "build/build_config.h"
-
-#include "base/command_line.h"
-#include "base/message_loop/message_loop.h"
-#include "base/power_monitor/power_monitor.h"
-#include "base/timer/hi_res_timer_manager.h"
-#include "chrome/common/chrome_result_codes.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/logging_chrome.h"
-#include "chrome/nacl/nacl_listener.h"
-#include "chrome/nacl/nacl_main_platform_delegate.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/common/main_function_params.h"
-
-// main() routine for the NaCl loader process.
-int NaClMain(const content::MainFunctionParams& parameters) {
-  const CommandLine& parsed_command_line = parameters.command_line;
-
-  // The main thread of the plugin services IO.
-  base::MessageLoopForIO main_message_loop;
-  base::PlatformThread::SetName("CrNaClMain");
-
-  base::PowerMonitor power_monitor;
-  base::HighResolutionTimerManager hi_res_timer_manager;
-
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
-  NaClMainPlatformDelegate platform(parameters);
-
-  platform.PlatformInitialize();
-  bool no_sandbox = parsed_command_line.HasSwitch(switches::kNoSandbox);
-  platform.InitSandboxTests(no_sandbox);
-
-#if defined(OS_POSIX)
-  // The number of cores must be obtained before the invocation of
-  // platform.EnableSandbox(), so cannot simply be inlined below.
-  int number_of_cores = sysconf(_SC_NPROCESSORS_ONLN);
-#endif
-
-  if (!no_sandbox) {
-    platform.EnableSandbox();
-  }
-  bool sandbox_test_result = platform.RunSandboxTests();
-
-  if (sandbox_test_result) {
-    NaClListener listener;
-#if defined(OS_POSIX)
-    listener.set_number_of_cores(number_of_cores);
-#endif
-    listener.Listen();
-  } else {
-    // This indirectly prevents the test-harness-success-cookie from being set,
-    // as a way of communicating test failure, because the nexe won't reply.
-    // TODO(jvoung): find a better way to indicate failure that doesn't
-    // require waiting for a timeout.
-    VLOG(1) << "Sandbox test failed: Not launching NaCl process";
-  }
-#else
-  NOTIMPLEMENTED() << " not implemented startup, plugin startup dialog etc.";
-#endif
-
-  platform.PlatformUninitialize();
-  return 0;
-}
diff --git a/chrome/nacl/nacl_main_platform_delegate.h b/chrome/nacl/nacl_main_platform_delegate.h
deleted file mode 100644
index a2d019e..0000000
--- a/chrome/nacl/nacl_main_platform_delegate.h
+++ /dev/null
@@ -1,44 +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.
-
-#ifndef CHROME_NACL_NACL_MAIN_PLATFORM_DELEGATE_H_
-#define CHROME_NACL_NACL_MAIN_PLATFORM_DELEGATE_H_
-
-#include "base/native_library.h"
-#include "content/public/common/main_function_params.h"
-
-typedef bool (*RunNaClLoaderTests)(void);
-const char kNaClLoaderTestCall[] = "RunNaClLoaderTests";
-
-class NaClMainPlatformDelegate {
- public:
-  explicit NaClMainPlatformDelegate(
-      const content::MainFunctionParams& parameters);
-  ~NaClMainPlatformDelegate();
-
-  // Called first thing and last thing in the process' lifecycle, i.e. before
-  // the sandbox is enabled.
-  void PlatformInitialize();
-  void PlatformUninitialize();
-
-  // Gives us an opportunity to initialize state used for tests before enabling
-  // the sandbox.
-  void InitSandboxTests(bool no_sandbox);
-
-  // Initiate Lockdown.
-  void EnableSandbox();
-
-  // Runs the sandbox tests for the NaCl Loader, if tests supplied.
-  // Cannot run again, after this (resources freed).
-  // Returns false if the tests are supplied and fail.
-  bool RunSandboxTests();
-
- private:
-  const content::MainFunctionParams& parameters_;
-  base::NativeLibrary sandbox_test_module_;
-
-  DISALLOW_COPY_AND_ASSIGN(NaClMainPlatformDelegate);
-};
-
-#endif  // CHROME_NACL_NACL_MAIN_PLATFORM_DELEGATE_H_
diff --git a/chrome/nacl/nacl_main_platform_delegate_linux.cc b/chrome/nacl/nacl_main_platform_delegate_linux.cc
deleted file mode 100644
index 583206a..0000000
--- a/chrome/nacl/nacl_main_platform_delegate_linux.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/nacl/nacl_main_platform_delegate.h"
-
-#include "base/command_line.h"
-
-NaClMainPlatformDelegate::NaClMainPlatformDelegate(
-    const content::MainFunctionParams& parameters)
-    : parameters_(parameters), sandbox_test_module_(NULL) {
-}
-
-NaClMainPlatformDelegate::~NaClMainPlatformDelegate() {
-}
-
-void NaClMainPlatformDelegate::PlatformInitialize() {
-}
-
-void NaClMainPlatformDelegate::PlatformUninitialize() {
-}
-
-void NaClMainPlatformDelegate::InitSandboxTests(bool no_sandbox) {
-  // The sandbox is started in the zygote process: zygote_main_linux.cc
-  // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
-  return;
-}
-
-void NaClMainPlatformDelegate::EnableSandbox() {
-  // The setuid sandbox is started in the zygote process: zygote_main_linux.cc
-  // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
-  //
-  // The seccomp sandbox is started in the renderer.
-  // http://code.google.com/p/seccompsandbox/
-  // seccomp is currently disabled for nacl.
-  // http://code.google.com/p/chromium/issues/detail?id=59423
-  // See the code in chrome/renderer/renderer_main_platform_delegate_linux.cc
-  // for how to turn seccomp on.
-  //
-  // The seccomp sandbox should not be enabled for Native Client until
-  // all of these issues are fixed:
-  // http://code.google.com/p/nativeclient/issues/list?q=label:Seccomp
-  // At best, NaCl will not work.  At worst, enabling the seccomp sandbox
-  // could create a hole in the NaCl sandbox.
-}
-
-bool NaClMainPlatformDelegate::RunSandboxTests() {
-  // The sandbox is started in the zygote process: zygote_main_linux.cc
-  // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
-  return true;
-}
diff --git a/chrome/nacl/nacl_main_platform_delegate_mac.mm b/chrome/nacl/nacl_main_platform_delegate_mac.mm
deleted file mode 100644
index 37abc28..0000000
--- a/chrome/nacl/nacl_main_platform_delegate_mac.mm
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2011 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.
-
-#include "chrome/nacl/nacl_main_platform_delegate.h"
-
-#import <Cocoa/Cocoa.h>
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/native_library.h"
-#include "components/nacl/common/nacl_sandbox_type_mac.h"
-#include "components/nacl/common/nacl_switches.h"
-#include "content/public/common/sandbox_init.h"
-
-NaClMainPlatformDelegate::NaClMainPlatformDelegate(
-    const content::MainFunctionParams& parameters)
-    : parameters_(parameters), sandbox_test_module_(NULL) {
-}
-
-NaClMainPlatformDelegate::~NaClMainPlatformDelegate() {
-}
-
-// TODO(jvoung): see if this old comment (from renderer_main_platform...)
-// is relevant to the nacl loader.
-// TODO(mac-port): Any code needed to initialize a process for purposes of
-// running a NaClLoader needs to also be reflected in chrome_main.cc for
-// --single-process support.
-void NaClMainPlatformDelegate::PlatformInitialize() {
-}
-
-void NaClMainPlatformDelegate::PlatformUninitialize() {
-}
-
-void NaClMainPlatformDelegate::InitSandboxTests(bool no_sandbox) {
-  const CommandLine& command_line = parameters_.command_line;
-
-  DVLOG(1) << "Started NaClLdr with ";
-  const std::vector<std::string>& argstrings = command_line.argv();
-  for (std::vector<std::string>::const_iterator ii = argstrings.begin();
-       ii != argstrings.end(); ++ii)
-    DVLOG(1) << *ii;
-
-  // Be sure not to load the sandbox test DLL if the sandbox isn't on.
-  // Comment-out guard and recompile if you REALLY want to test w/out the SB.
-  // TODO(jvoung): allow testing without sandbox, but change expected ret vals.
-  if (!no_sandbox) {
-    base::FilePath test_dll_name =
-      command_line.GetSwitchValuePath(switches::kTestNaClSandbox);
-    if (!test_dll_name.empty()) {
-      sandbox_test_module_ = base::LoadNativeLibrary(test_dll_name, NULL);
-      CHECK(sandbox_test_module_);
-    }
-  }
-}
-
-void NaClMainPlatformDelegate::EnableSandbox() {
-  CHECK(content::InitializeSandbox(NACL_SANDBOX_TYPE_NACL_LOADER,
-                                   base::FilePath()))
-      << "Error initializing sandbox for " << switches::kNaClLoaderProcess;
-}
-
-bool NaClMainPlatformDelegate::RunSandboxTests() {
-  // TODO(jvoung): Win and mac should share this identical code.
-  bool result = true;
-  if (sandbox_test_module_) {
-    RunNaClLoaderTests run_security_tests =
-      reinterpret_cast<RunNaClLoaderTests>(
-        base::GetFunctionPointerFromNativeLibrary(sandbox_test_module_,
-                                                  kNaClLoaderTestCall));
-    if (run_security_tests) {
-      DVLOG(1) << "Running NaCl Loader security tests";
-      result = (*run_security_tests)();
-    } else {
-      VLOG(1) << "Failed to get NaCl sandbox test function";
-      result = false;
-    }
-    base::UnloadNativeLibrary(sandbox_test_module_);
-    sandbox_test_module_ = NULL;
-  }
-  return result;
-}
diff --git a/chrome/nacl/nacl_main_platform_delegate_win.cc b/chrome/nacl/nacl_main_platform_delegate_win.cc
deleted file mode 100644
index be37857..0000000
--- a/chrome/nacl/nacl_main_platform_delegate_win.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) 2011 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.
-
-#include "chrome/nacl/nacl_main_platform_delegate.h"
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/native_library.h"
-#include "components/nacl/common/nacl_switches.h"
-#include "sandbox/win/src/sandbox.h"
-
-NaClMainPlatformDelegate::NaClMainPlatformDelegate(
-    const content::MainFunctionParams& parameters)
-    : parameters_(parameters), sandbox_test_module_(NULL) {
-}
-
-NaClMainPlatformDelegate::~NaClMainPlatformDelegate() {
-}
-
-void NaClMainPlatformDelegate::PlatformInitialize() {
-  // Be mindful of what resources you acquire here. They can be used by
-  // malicious code if the renderer gets compromised.
-}
-
-void NaClMainPlatformDelegate::PlatformUninitialize() {
-}
-
-void NaClMainPlatformDelegate::InitSandboxTests(bool no_sandbox) {
-  const CommandLine& command_line = parameters_.command_line;
-
-  DVLOG(1) << "Started NaClLdr with " << command_line.GetCommandLineString();
-
-  sandbox::TargetServices* target_services =
-      parameters_.sandbox_info->target_services;
-
-  if (target_services && !no_sandbox) {
-    base::FilePath test_dll_name =
-      command_line.GetSwitchValuePath(switches::kTestNaClSandbox);
-    if (!test_dll_name.empty()) {
-      // At this point, hack on the suffix according to with bitness
-      // of your windows process.
-#if defined(_WIN64)
-      DVLOG(1) << "Using 64-bit test dll\n";
-      test_dll_name = test_dll_name.InsertBeforeExtension(L"64");
-      test_dll_name = test_dll_name.ReplaceExtension(L"dll");
-#else
-      DVLOG(1) << "Using 32-bit test dll\n";
-      test_dll_name = test_dll_name.ReplaceExtension(L"dll");
-#endif
-      DVLOG(1) << "Loading test lib " << test_dll_name.value() << "\n";
-      sandbox_test_module_ = base::LoadNativeLibrary(test_dll_name, NULL);
-      CHECK(sandbox_test_module_);
-      VLOG(1) << "Testing NaCl sandbox\n";
-    }
-  }
-}
-
-void NaClMainPlatformDelegate::EnableSandbox() {
-  sandbox::TargetServices* target_services =
-      parameters_.sandbox_info->target_services;
-
-  CHECK(target_services) << "NaCl-Win EnableSandbox: No Target Services!";
-  // Cause advapi32 to load before the sandbox is turned on.
-  unsigned int dummy_rand;
-  rand_s(&dummy_rand);
-  // Warm up language subsystems before the sandbox is turned on.
-  ::GetUserDefaultLangID();
-  ::GetUserDefaultLCID();
-  // Turn the sandbox on.
-  target_services->LowerToken();
-}
-
-bool NaClMainPlatformDelegate::RunSandboxTests() {
-  // TODO(jvoung): Win and mac should share this code.
-  bool result = true;
-  if (sandbox_test_module_) {
-    RunNaClLoaderTests run_security_tests =
-      reinterpret_cast<RunNaClLoaderTests>(
-        base::GetFunctionPointerFromNativeLibrary(sandbox_test_module_,
-                                                  kNaClLoaderTestCall));
-    if (run_security_tests) {
-      DVLOG(1) << "Running NaCl Loader security tests";
-      result = (*run_security_tests)();
-    } else {
-      VLOG(1) << "Failed to get NaCl sandbox test function";
-      result = false;
-    }
-    base::UnloadNativeLibrary(sandbox_test_module_);
-    sandbox_test_module_ = NULL;
-  }
-  return result;
-}
diff --git a/chrome/nacl/nacl_sandbox_linux.cc b/chrome/nacl/nacl_sandbox_linux.cc
deleted file mode 100644
index 168f591..0000000
--- a/chrome/nacl/nacl_sandbox_linux.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2013 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.
-
-#include "chrome/nacl/nacl_sandbox_linux.h"
-
-#include <signal.h>
-#include <sys/ptrace.h>
-
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-#include "build/build_config.h"
-#include "content/public/common/sandbox_init.h"
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
-#include "sandbox/linux/services/linux_syscalls.h"
-
-using playground2::ErrorCode;
-using playground2::Sandbox;
-
-namespace {
-
-inline bool IsPlatformX86() {
-#if defined(__x86_64__) || defined(__i386__)
-  return true;
-#else
-  return false;
-#endif
-}
-
-// On ARM and x86_64, System V shared memory calls have each their own system
-// call, while on i386 they are multiplexed.
-#if defined(__x86_64__) || defined(__arm__)
-bool IsSystemVSharedMemory(int sysno) {
-  switch (sysno) {
-    case __NR_shmat:
-    case __NR_shmctl:
-    case __NR_shmdt:
-    case __NR_shmget:
-      return true;
-    default:
-      return false;
-  }
-}
-#endif
-
-#if defined(__i386__)
-// Big system V multiplexing system call.
-bool IsSystemVIpc(int sysno) {
-  switch (sysno) {
-    case __NR_ipc:
-      return true;
-    default:
-      return false;
-  }
-}
-#endif
-
-ErrorCode NaClBpfSandboxPolicy(
-    playground2::Sandbox* sb, int sysno, void* aux) {
-  const playground2::BpfSandboxPolicyCallback baseline_policy =
-      content::GetBpfSandboxBaselinePolicy();
-  switch (sysno) {
-    // TODO(jln): NaCl's GDB debug stub uses the following socket system calls,
-    // see if it can be restricted a bit.
-#if defined(__x86_64__) || defined(__arm__)
-    // transport_common.cc needs this.
-    case __NR_accept:
-    case __NR_setsockopt:
-#elif defined(__i386__)
-    case __NR_socketcall:
-#endif
-    // trusted/service_runtime/linux/thread_suspension.c needs sigwait() and is
-    // used by NaCl's GDB debug stub.
-    case __NR_rt_sigtimedwait:
-#if defined(__i386__)
-    // Needed on i386 to set-up the custom segments.
-    case __NR_modify_ldt:
-#endif
-    // NaClAddrSpaceBeforeAlloc needs prlimit64.
-    case __NR_prlimit64:
-    // NaCl uses custom signal stacks.
-    case __NR_sigaltstack:
-    // Below is fairly similar to the policy for a Chromium renderer.
-    // TODO(jln): restrict clone(), ioctl() and prctl().
-    case __NR_ioctl:
-#if defined(__i386__) || defined(__x86_64__)
-    case __NR_getrlimit:
-#endif
-#if defined(__i386__) || defined(__arm__)
-    case __NR_ugetrlimit:
-#endif
-    case __NR_pread64:
-    case __NR_pwrite64:
-    case __NR_sched_get_priority_max:
-    case __NR_sched_get_priority_min:
-    case __NR_sched_getaffinity:
-    case __NR_sched_getparam:
-    case __NR_sched_getscheduler:
-    case __NR_sched_setscheduler:
-    case __NR_setpriority:
-    case __NR_sysinfo:
-    // __NR_times needed as clock() is called by CommandBufferHelper, which is
-    // used by NaCl applications that use Pepper's 3D interfaces.
-    // See crbug.com/264856 for details.
-    case __NR_times:
-    case __NR_uname:
-      return ErrorCode(ErrorCode::ERR_ALLOWED);
-    case __NR_ptrace:
-      return ErrorCode(EPERM);
-    default:
-      // TODO(jln): look into getting rid of System V shared memory:
-      // platform_qualify/linux/sysv_shm_and_mmap.c makes it a requirement, but
-      // it may not be needed in all cases. Chromium renderers don't need
-      // System V shared memory on Aura.
-#if defined(__x86_64__) || defined(__arm__)
-      if (IsSystemVSharedMemory(sysno))
-        return ErrorCode(ErrorCode::ERR_ALLOWED);
-#elif defined(__i386__)
-      if (IsSystemVIpc(sysno))
-        return ErrorCode(ErrorCode::ERR_ALLOWED);
-#endif
-      return baseline_policy.Run(sb, sysno, aux);
-  }
-  NOTREACHED();
-  // GCC wants this.
-  return ErrorCode(EPERM);
-}
-
-void RunSandboxSanityChecks() {
-  errno = 0;
-  // Make a ptrace request with an invalid PID.
-  long ptrace_ret = ptrace(PTRACE_PEEKUSER, -1 /* pid */, NULL, NULL);
-  CHECK_EQ(-1, ptrace_ret);
-  // Without the sandbox on, this ptrace call would ESRCH instead.
-  CHECK_EQ(EPERM, errno);
-}
-
-}  // namespace
-
-bool InitializeBpfSandbox() {
-  // TODO(jln): enable the sandbox on ARM as well.
-  if (!IsPlatformX86())
-    return false;
-  bool sandbox_is_initialized =
-      content::InitializeSandbox(NaClBpfSandboxPolicy);
-  if (sandbox_is_initialized) {
-    RunSandboxSanityChecks();
-    // TODO(jln): Find a way to fix this.
-    // The sandbox' SIGSYS handler trips NaCl, so we disable it.
-    // If SIGSYS is triggered it'll now execute the default action
-    // (CORE). This will make it hard to track down bugs and sandbox violations.
-    CHECK(signal(SIGSYS, SIG_DFL) != SIG_ERR);
-    return true;
-  }
-  return false;
-}
diff --git a/chrome/nacl/nacl_sandbox_linux.h b/chrome/nacl/nacl_sandbox_linux.h
deleted file mode 100644
index 6c1662f..0000000
--- a/chrome/nacl/nacl_sandbox_linux.h
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (c) 2013 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.
-
-#ifndef CHROME_NACL_NACL_SANDBOX_LINUX_H_
-#define CHROME_NACL_NACL_SANDBOX_LINUX_H_
-
-bool InitializeBpfSandbox();
-
-#endif  // CHROME_NACL_NACL_SANDBOX_LINUX_H_
diff --git a/chrome/nacl/nacl_validation_db.h b/chrome/nacl/nacl_validation_db.h
deleted file mode 100644
index 81351d2..0000000
--- a/chrome/nacl/nacl_validation_db.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_NACL_NACL_VALIDATION_DB_H_
-#define CHROME_NACL_NACL_VALIDATION_DB_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-
-struct NaClFileToken;
-
-class NaClValidationDB {
- public:
-  NaClValidationDB() {}
-  virtual ~NaClValidationDB() {}
-
-  virtual bool QueryKnownToValidate(const std::string& signature) = 0;
-  virtual void SetKnownToValidate(const std::string& signature) = 0;
-  virtual bool ResolveFileToken(struct NaClFileToken* file_token,
-                                int32* fd, std::string* path) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NaClValidationDB);
-};
-
-#endif  // CHROME_NACL_NACL_VALIDATION_DB_H_
diff --git a/chrome/nacl/nacl_validation_query.cc b/chrome/nacl/nacl_validation_query.cc
deleted file mode 100644
index 0ff831c..0000000
--- a/chrome/nacl/nacl_validation_query.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/nacl/nacl_validation_query.h"
-
-#include "base/logging.h"
-#include "crypto/nss_util.h"
-#include "chrome/nacl/nacl_validation_db.h"
-#include "native_client/src/include/portability.h"
-#include "native_client/src/trusted/validator/nacl_file_info.h"
-#include "native_client/src/trusted/validator/validation_cache.h"
-
-NaClValidationQueryContext::NaClValidationQueryContext(
-    NaClValidationDB* db,
-    const std::string& profile_key,
-    const std::string& nacl_version)
-    : db_(db),
-      profile_key_(profile_key),
-      nacl_version_(nacl_version) {
-
-  // Sanity checks.
-  CHECK(profile_key.length() >= 8);
-  CHECK(nacl_version.length() >= 4);
-}
-
-NaClValidationQuery* NaClValidationQueryContext::CreateQuery() {
-  NaClValidationQuery* query = new NaClValidationQuery(db_, profile_key_);
-  // Changing the version effectively invalidates existing hashes.
-  query->AddData(nacl_version_);
-  return query;
-}
-
-bool NaClValidationQueryContext::ResolveFileToken(
-    struct NaClFileToken* file_token,
-    int32* fd,
-    std::string* path) {
-  return db_->ResolveFileToken(file_token, fd, path);
-}
-
-NaClValidationQuery::NaClValidationQuery(NaClValidationDB* db,
-                                         const std::string& profile_key)
-    : state_(READY),
-      hasher_(crypto::HMAC::SHA256),
-      db_(db),
-      buffer_length_(0) {
-  // Without this line on Linux, HMAC::Init will instantiate a singleton that
-  // in turn attempts to open a file.  Disabling this behavior avoids a ~70 ms
-  // stall the first time HMAC is used.
-  // This function is also called in nacl_helper_linux.cc, but nacl_helper may
-  // not be used in all cases.
-  // TODO(ncbray) remove when nacl_helper becomes the only code path.
-  // http://code.google.com/p/chromium/issues/detail?id=118263
-#if defined(USE_NSS)
-  crypto::ForceNSSNoDBInit();
-#endif
-  CHECK(hasher_.Init(profile_key));
-}
-
-void NaClValidationQuery::AddData(const char* data, size_t length) {
-  CHECK(state_ == READY);
-  CHECK(buffer_length_ <= sizeof(buffer_));
-  // Chrome's HMAC class doesn't support incremental signing.  Work around
-  // this by using a (small) temporary buffer to accumulate data.
-  // Check if there is space in the buffer.
-  if (buffer_length_ + kDigestLength > sizeof(buffer_)) {
-    // Hash the buffer to make space.
-    CompressBuffer();
-  }
-  // Hash the input data into the buffer.  Assumes that sizeof(buffer_) >=
-  // kDigestLength * 2 (the buffer can store at least two digests.)
-  CHECK(hasher_.Sign(base::StringPiece(data, length),
-                     reinterpret_cast<unsigned char*>(buffer_ + buffer_length_),
-                     kDigestLength));
-  buffer_length_ += kDigestLength;
-}
-
-void NaClValidationQuery::AddData(const unsigned char* data, size_t length) {
-  AddData(reinterpret_cast<const char*>(data), length);
-}
-
-void NaClValidationQuery::AddData(const base::StringPiece& data) {
-  AddData(data.data(), data.length());
-}
-
-int NaClValidationQuery::QueryKnownToValidate() {
-  CHECK(state_ == READY);
-  // It is suspicious if we have less than a digest's worth of data.
-  CHECK(buffer_length_ >= kDigestLength);
-  CHECK(buffer_length_ <= sizeof(buffer_));
-  state_ = GET_CALLED;
-  // Ensure the buffer contains only one digest worth of data.
-  CompressBuffer();
-  return db_->QueryKnownToValidate(std::string(buffer_, buffer_length_));
-}
-
-void NaClValidationQuery::SetKnownToValidate() {
-  CHECK(state_ == GET_CALLED);
-  CHECK(buffer_length_ == kDigestLength);
-  state_ = SET_CALLED;
-  db_->SetKnownToValidate(std::string(buffer_, buffer_length_));
-}
-
-// Reduce the size of the data in the buffer by hashing it and writing it back
-// to the buffer.
-void NaClValidationQuery::CompressBuffer() {
-  // Calculate the digest into a temp buffer.  It is likely safe to calculate it
-  // directly back into the buffer, but this is an "accidental" semantic we're
-  // avoiding depending on.
-  unsigned char temp[kDigestLength];
-  CHECK(hasher_.Sign(base::StringPiece(buffer_, buffer_length_), temp,
-                     kDigestLength));
-  memcpy(buffer_, temp, kDigestLength);
-  buffer_length_ = kDigestLength;
-}
-
-// OO wrappers
-
-static void* CreateQuery(void* handle) {
-  return static_cast<NaClValidationQueryContext*>(handle)->CreateQuery();
-}
-
-static void AddData(void* query, const uint8* data, size_t length) {
-  static_cast<NaClValidationQuery*>(query)->AddData(data, length);
-}
-
-static int QueryKnownToValidate(void* query) {
-  return static_cast<NaClValidationQuery*>(query)->QueryKnownToValidate();
-}
-
-static void SetKnownToValidate(void* query) {
-  static_cast<NaClValidationQuery*>(query)->SetKnownToValidate();
-}
-
-static void DestroyQuery(void* query) {
-  delete static_cast<NaClValidationQuery*>(query);
-}
-
-static int ResolveFileToken(void* handle, struct NaClFileToken* file_token,
-                            int32* fd, char** file_path,
-                            uint32* file_path_length) {
-  std::string path;
-  *file_path = NULL;
-  *file_path_length = 0;
-  bool ok = static_cast<NaClValidationQueryContext*>(handle)->
-      ResolveFileToken(file_token, fd, &path);
-  if (ok) {
-    *file_path = static_cast<char*>(malloc(path.length() + 1));
-    CHECK(*file_path);
-    memcpy(*file_path, path.data(), path.length());
-    (*file_path)[path.length()] = 0;
-    *file_path_length = static_cast<uint32>(path.length());
-  }
-  return ok;
-}
-
-struct NaClValidationCache* CreateValidationCache(
-    NaClValidationDB* db, const std::string& profile_key,
-    const std::string& nacl_version) {
-  NaClValidationCache* cache =
-      static_cast<NaClValidationCache*>(malloc(sizeof(NaClValidationCache)));
-  // Make sure any fields introduced in a cross-repo change are zeroed.
-  memset(cache, 0, sizeof(*cache));
-  cache->handle = new NaClValidationQueryContext(db, profile_key, nacl_version);
-  cache->CreateQuery = CreateQuery;
-  cache->AddData = AddData;
-  cache->QueryKnownToValidate = QueryKnownToValidate;
-  cache->SetKnownToValidate = SetKnownToValidate;
-  cache->DestroyQuery = DestroyQuery;
-  cache->ResolveFileToken = ResolveFileToken;
-  return cache;
-}
diff --git a/chrome/nacl/nacl_validation_query.h b/chrome/nacl/nacl_validation_query.h
deleted file mode 100644
index a849b81..0000000
--- a/chrome/nacl/nacl_validation_query.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef CHROME_NACL_NACL_VALIDATION_QUERY_H_
-#define CHROME_NACL_NACL_VALIDATION_QUERY_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/strings/string_piece.h"
-#include "crypto/hmac.h"
-
-struct NaClFileToken;
-struct NaClValidationCache;
-class NaClValidationDB;
-class NaClValidationQuery;
-
-class NaClValidationQueryContext {
- public:
-  NaClValidationQueryContext(NaClValidationDB* db,
-                             const std::string& profile_key,
-                             const std::string& nacl_version);
-
-  NaClValidationQuery* CreateQuery();
-
-  bool ResolveFileToken(struct NaClFileToken* file_token, int32* fd,
-                        std::string* path);
-
- private:
-  NaClValidationDB* db_;
-
-  // A key used by HMAC that is specific to this installation of Chrome.
-  std::string profile_key_;
-
-  // Bytes indicating the "version" of the validator being used.  This is used
-  // to implicitly invalidate the cache - changing the version will change the
-  // hashes that are produced.
-  std::string nacl_version_;
-};
-
-class NaClValidationQuery {
- public:
-  // SHA256 digest size.
-  static const size_t kDigestLength = 32;
-
-  NaClValidationQuery(NaClValidationDB* db, const std::string& profile_key);
-
-  void AddData(const char* data, size_t length);
-  void AddData(const unsigned char* data, size_t length);
-  void AddData(const base::StringPiece& data);
-
-  int QueryKnownToValidate();
-
-  void SetKnownToValidate();
-
- private:
-  enum QueryState {
-    READY,
-    GET_CALLED,
-    SET_CALLED
-  };
-
-  // The HMAC interface currently does not support incremental signing.  To work
-  // around this, each piece of data is signed and the signature is added to a
-  // buffer.  If there is not enough space in the buffer to accommodate new
-  // data, the buffer contents are signed and the new signature replaces the
-  // contents of the buffer.  CompressBuffer performs this operation.  In
-  // affect, a hash tree is constructed to emulate incremental signing.
-  void CompressBuffer();
-
-  // Track the state of the query to detect suspicious method calls.
-  QueryState state_;
-
-  crypto::HMAC hasher_;
-  NaClValidationDB* db_;
-
-  // The size of buffer_ is a somewhat arbitrary choice.  It needs to be at
-  // at least kDigestLength * 2, but it can be arbitrarily large.  In practice
-  // there are 4 calls to AddData (version, architechture, cpu features, and
-  // code), so 4 times digest length means the buffer will not need to be
-  // compressed as an intermediate step in the expected use cases.
-  char buffer_[kDigestLength * 4];
-  size_t buffer_length_;
-
-  DISALLOW_COPY_AND_ASSIGN(NaClValidationQuery);
-};
-
-// Create a validation cache interface for use by sel_ldr.
-struct NaClValidationCache* CreateValidationCache(
-    NaClValidationDB* db, const std::string& profile_key,
-    const std::string& nacl_version);
-
-#endif  // CHROME_NACL_NACL_VALIDATION_QUERY_H_
diff --git a/chrome/nacl/nacl_validation_query_unittest.cc b/chrome/nacl/nacl_validation_query_unittest.cc
deleted file mode 100644
index 511000d..0000000
--- a/chrome/nacl/nacl_validation_query_unittest.cc
+++ /dev/null
@@ -1,283 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "chrome/nacl/nacl_validation_db.h"
-#include "chrome/nacl/nacl_validation_query.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-// This test makes sure that validation signature generation is performed
-// correctly.  In effect, this means that we are checking all of the data
-// (and no other data) we are passing the signature generator affects the final
-// signature.  To avoid tying the tests to a particular implementation, each
-// test generates two signatures and compares them rather than trying to compare
-// against a specified signature.
-
-namespace {
-
-const char kKey[] = "bogus key for HMAC...";
-const char kKeyAlt[] = "bogus key for HMAC!!!";
-
-const char kVersion[] = "bogus version";
-const char kVersionAlt[] = "bogus!version";
-
-
-const char kShortData[] = "Short data 1234567890";
-const char kAltShortData[] = "Short!data 1234567890";
-
-const char kLongData[] = "Long data."
-    "1234567890123456789012345678901234567890123456789012345678901234567890"
-    "1234567890123456789012345678901234567890123456789012345678901234567890"
-    "1234567890123456789012345678901234567890123456789012345678901234567890"
-    "1234567890123456789012345678901234567890123456789012345678901234567890";
-
-class MockValidationDB : public NaClValidationDB {
- public:
-  MockValidationDB()
-    : did_query_(false),
-      did_set_(false),
-      status_(true) {
-  }
-
-  virtual bool QueryKnownToValidate(const std::string& signature) OVERRIDE {
-    // The typecast is needed to work around gtest trying to take the address
-    // of a constant.
-    EXPECT_EQ((int) NaClValidationQuery::kDigestLength,
-              (int) signature.length());
-    EXPECT_FALSE(did_query_);
-    EXPECT_FALSE(did_set_);
-    did_query_ = true;
-    memcpy(query_signature_, signature.data(),
-           NaClValidationQuery::kDigestLength);
-    return status_;
-  }
-
-  virtual void SetKnownToValidate(const std::string& signature) OVERRIDE {
-    // The typecast is needed to work around gtest trying to take the address
-    // of a constant.
-    ASSERT_EQ((int) NaClValidationQuery::kDigestLength,
-              (int) signature.length());
-    EXPECT_TRUE(did_query_);
-    EXPECT_FALSE(did_set_);
-    did_set_ = true;
-    memcpy(set_signature_, signature.data(),
-           NaClValidationQuery::kDigestLength);
-    // Signatures should be the same.
-    EXPECT_EQ(0, memcmp(query_signature_, set_signature_,
-                        NaClValidationQuery::kDigestLength));
-  }
-
-  virtual bool ResolveFileToken(struct NaClFileToken* file_token, int32* fd,
-                                std::string* path) OVERRIDE {
-    *fd = -1;
-    *path = "";
-    return false;
-  }
-
-  bool did_query_;
-  bool did_set_;
-  bool status_;
-
-  uint8 query_signature_[NaClValidationQuery::kDigestLength];
-  uint8 set_signature_[NaClValidationQuery::kDigestLength];
-};
-
-class TestQuery {
- public:
-  TestQuery(const char* key, const char* version) {
-    db.reset(new MockValidationDB());
-    context.reset(new NaClValidationQueryContext(db.get(), key, version));
-    query.reset(context->CreateQuery());
-  }
-
-  scoped_ptr<MockValidationDB> db;
-  scoped_ptr<NaClValidationQueryContext> context;
-  scoped_ptr<NaClValidationQuery> query;
-};
-
-class NaClValidationQueryTest : public ::testing::Test {
- protected:
-  scoped_ptr<TestQuery> query1;
-  scoped_ptr<TestQuery> query2;
-
-  virtual void SetUp() {
-    query1.reset(new TestQuery(kKey, kVersion));
-    query2.reset(new TestQuery(kKey, kVersion));
-  }
-
-  void AssertQuerySame() {
-    ASSERT_TRUE(query1->db->did_query_);
-    ASSERT_TRUE(query2->db->did_query_);
-    ASSERT_EQ(0, memcmp(query1->db->query_signature_,
-                        query2->db->query_signature_,
-                        NaClValidationQuery::kDigestLength));
-  }
-
-  void AssertQueryDifferent() {
-    ASSERT_TRUE(query1->db->did_query_);
-    ASSERT_TRUE(query2->db->did_query_);
-    ASSERT_NE(0, memcmp(query1->db->query_signature_,
-                        query2->db->query_signature_,
-                        NaClValidationQuery::kDigestLength));
-  }
-};
-
-TEST_F(NaClValidationQueryTest, Sanity) {
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  ASSERT_FALSE(query1->db->did_query_);
-  ASSERT_FALSE(query1->db->did_set_);
-  ASSERT_EQ(1, query1->query->QueryKnownToValidate());
-  ASSERT_TRUE(query1->db->did_query_);
-  ASSERT_FALSE(query1->db->did_set_);
-  query1->query->SetKnownToValidate();
-  ASSERT_TRUE(query1->db->did_query_);
-  ASSERT_TRUE(query1->db->did_set_);
-}
-
-TEST_F(NaClValidationQueryTest, ConsistentShort) {
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->QueryKnownToValidate();
-
-  query2->query->AddData(kShortData, sizeof(kShortData));
-  query2->query->QueryKnownToValidate();
-
-  AssertQuerySame();
-}
-
-TEST_F(NaClValidationQueryTest, InconsistentShort) {
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->QueryKnownToValidate();
-
-  query2->query->AddData(kAltShortData, sizeof(kAltShortData));
-  query2->query->QueryKnownToValidate();
-
-  AssertQueryDifferent();
-}
-
-// Test for a bug caught during development where AddData would accidently
-// overwrite previously written data and add uninitialzied memory to the hash.
-TEST_F(NaClValidationQueryTest, ConsistentShortBug) {
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->QueryKnownToValidate();
-
-  query2->query->AddData(kShortData, sizeof(kShortData));
-  query2->query->AddData(kShortData, sizeof(kShortData));
-  query2->query->QueryKnownToValidate();
-
-  AssertQuerySame();
-}
-
-// Test for a bug caught during development where AddData would accidently
-// overwrite previously written data and add uninitialzed memory to the hash.
-TEST_F(NaClValidationQueryTest, InconsistentShortBug1) {
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->QueryKnownToValidate();
-
-  query2->query->AddData(kAltShortData, sizeof(kAltShortData));
-  query2->query->AddData(kShortData, sizeof(kShortData));
-  query2->query->QueryKnownToValidate();
-
-  AssertQueryDifferent();
-}
-
-// Make sure we don't ignore the second bit of data.
-TEST_F(NaClValidationQueryTest, InconsistentShort2) {
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->QueryKnownToValidate();
-
-  query2->query->AddData(kShortData, sizeof(kShortData));
-  query2->query->AddData(kAltShortData, sizeof(kAltShortData));
-  query2->query->QueryKnownToValidate();
-
-  AssertQueryDifferent();
-}
-
-TEST_F(NaClValidationQueryTest, InconsistentZeroSizedAdd) {
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->QueryKnownToValidate();
-
-  query2->query->AddData(kShortData, sizeof(kShortData));
-  query2->query->AddData(kShortData, 0);
-  query2->query->QueryKnownToValidate();
-
-  AssertQueryDifferent();
-}
-
-TEST_F(NaClValidationQueryTest, ConsistentZeroSizedAdd) {
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->AddData("a", 0);
-  query1->query->QueryKnownToValidate();
-
-  query2->query->AddData(kShortData, sizeof(kShortData));
-  query2->query->AddData("b", 0);
-  query2->query->QueryKnownToValidate();
-
-  AssertQuerySame();
-}
-
-TEST_F(NaClValidationQueryTest, ConsistentRepeatedShort) {
-  for (int i = 0; i < 30; i++) {
-    query1->query->AddData(kShortData, sizeof(kShortData));
-  }
-  query1->query->QueryKnownToValidate();
-
-  for (int i = 0; i < 30; i++) {
-    query2->query->AddData(kShortData, sizeof(kShortData));
-  }
-  query2->query->QueryKnownToValidate();
-
-  AssertQuerySame();
-}
-
-TEST_F(NaClValidationQueryTest, ConsistentLong) {
-  query1->query->AddData(kLongData, sizeof(kLongData));
-  query1->query->QueryKnownToValidate();
-
-  query2->query->AddData(kLongData, sizeof(kLongData));
-  query2->query->QueryKnownToValidate();
-
-  AssertQuerySame();
-}
-
-TEST_F(NaClValidationQueryTest, ConsistentRepeatedLong) {
-  for (int i = 0; i < 30; i++) {
-    query1->query->AddData(kLongData, sizeof(kLongData));
-  }
-  query1->query->QueryKnownToValidate();
-
-  for (int i = 0; i < 30; i++) {
-    query2->query->AddData(kLongData, sizeof(kLongData));
-  }
-  query2->query->QueryKnownToValidate();
-
-  AssertQuerySame();
-}
-
-TEST_F(NaClValidationQueryTest, PerturbKey) {
-  query2.reset(new TestQuery(kKeyAlt, kVersion));
-
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->QueryKnownToValidate();
-
-  query2->query->AddData(kShortData, sizeof(kShortData));
-  query2->query->QueryKnownToValidate();
-
-  AssertQueryDifferent();
-}
-
-TEST_F(NaClValidationQueryTest, PerturbVersion) {
-  query2.reset(new TestQuery(kKey, kVersionAlt));
-
-  query1->query->AddData(kShortData, sizeof(kShortData));
-  query1->query->QueryKnownToValidate();
-
-  query2->query->AddData(kShortData, sizeof(kShortData));
-  query2->query->QueryKnownToValidate();
-
-  AssertQueryDifferent();
-}
-
-}
diff --git a/chrome/renderer/autofill/form_autofill_browsertest.cc b/chrome/renderer/autofill/form_autofill_browsertest.cc
index da76916..68fa4c7 100644
--- a/chrome/renderer/autofill/form_autofill_browsertest.cc
+++ b/chrome/renderer/autofill/form_autofill_browsertest.cc
@@ -5,10 +5,12 @@
 #include <vector>
 
 #include "base/format_macros.h"
+#include "base/metrics/field_trial.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/test/base/chrome_render_view_test.h"
 #include "components/autofill/content/renderer/form_autofill_util.h"
 #include "components/autofill/content/renderer/form_cache.h"
@@ -36,6 +38,36 @@
 using WebKit::WebString;
 using WebKit::WebVector;
 
+namespace {
+
+struct AutofillFieldCase {
+  const char* const name;
+  const char* const initial_value;
+  const char* const autocomplete_attribute;  // The autocomplete attribute of
+                                             // the element.
+  bool should_be_autofilled;   // Whether the filed should be autofilled.
+  const char* const autofill_value;  // The value being used to fill the field.
+  const char* const expected_value;  // The expected value after Autofill
+                                     // or Preview.
+};
+
+static const char kFormHtml[] =
+    "<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">"
+    "  <INPUT type=\"text\" id=\"firstname\"/>"
+    "  <INPUT type=\"text\" id=\"lastname\"/>"
+    "  <INPUT type=\"hidden\" id=\"imhidden\"/>"
+    "  <INPUT type=\"text\" id=\"notempty\" value=\"Hi\"/>"
+    "  <INPUT type=\"text\" autocomplete=\"off\" id=\"noautocomplete\"/>"
+    "  <INPUT type=\"text\" disabled=\"disabled\" id=\"notenabled\"/>"
+    "  <INPUT type=\"text\" readonly id=\"readonly\"/>"
+    "  <INPUT type=\"text\" style=\"visibility: hidden\""
+    "         id=\"invisible\"/>"
+    "  <INPUT type=\"text\" style=\"display: none\" id=\"displaynone\"/>"
+    "  <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>"
+    "</FORM>";
+
+}  // namespace
+
 namespace autofill {
 
 class FormAutofillTest : public ChromeRenderViewTest {
@@ -109,6 +141,102 @@
     ExpectLabels(html, labels, names, values);
   }
 
+  typedef void (*FillFormFunction)(const FormData& form,
+                                   const WebInputElement& element);
+
+  typedef WebString (WebInputElement::*GetValueFunction)(void) const;
+
+  // Test FormFillxxx functions.
+  void TestFormFillFunctions(const char* html,
+                             const AutofillFieldCase* field_cases,
+                             size_t number_of_field_cases,
+                             FillFormFunction fill_form_function,
+                             GetValueFunction get_value_function) {
+    LoadHTML(html);
+
+    WebFrame* web_frame = GetMainFrame();
+    ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame);
+
+    FormCache form_cache;
+    std::vector<FormData> forms;
+    form_cache.ExtractForms(*web_frame, &forms);
+    ASSERT_EQ(1U, forms.size());
+
+    // Get the input element we want to find.
+    WebElement element = web_frame->document().getElementById("firstname");
+    WebInputElement input_element = element.to<WebInputElement>();
+
+    // Find the form that contains the input element.
+    FormData form_data;
+    FormFieldData field;
+    EXPECT_TRUE(
+        FindFormAndFieldForInputElement(input_element,
+                                        &form_data,
+                                        &field,
+                                        autofill::REQUIRE_AUTOCOMPLETE));
+    EXPECT_EQ(ASCIIToUTF16("TestForm"), form_data.name);
+    EXPECT_EQ(GURL(web_frame->document().url()), form_data.origin);
+    EXPECT_EQ(GURL("http://buh.com"), form_data.action);
+
+    const std::vector<FormFieldData>& fields = form_data.fields;
+    ASSERT_EQ(number_of_field_cases, fields.size());
+
+    FormFieldData expected;
+    expected.form_control_type = "text";
+    expected.max_length = WebInputElement::defaultMaxLength();
+
+    // Verify field's initial value.
+    for (size_t i = 0; i < number_of_field_cases; ++i) {
+      SCOPED_TRACE(base::StringPrintf("Verify initial value for field %s",
+                                      field_cases[i].name));
+      expected.name = ASCIIToUTF16(field_cases[i].name);
+      expected.value = ASCIIToUTF16(field_cases[i].initial_value);
+      expected.autocomplete_attribute = field_cases[i].autocomplete_attribute;
+      EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[i]);
+      // Fill the form_data for the field.
+      form_data.fields[i].value = ASCIIToUTF16(field_cases[i].autofill_value);
+    }
+
+    // Autofill the form using the given fill form function.
+    fill_form_function(form_data, input_element);
+
+    // Validate Autofill or Preview results.
+    for (size_t i = 0; i < number_of_field_cases; ++i) {
+      ValidteFilledField(field_cases[i], get_value_function);
+    }
+  }
+
+  // Validate an Autofilled field.
+  void ValidteFilledField(const AutofillFieldCase& field_case,
+                          GetValueFunction get_value_function) {
+    SCOPED_TRACE(base::StringPrintf("Verify autofilled value for field %s",
+                                    field_case.name));
+    WebInputElement input_element = GetMainFrame()->document().getElementById(
+        ASCIIToUTF16(field_case.name)).to<WebInputElement>();
+    EXPECT_EQ(field_case.should_be_autofilled, input_element.isAutofilled());
+    if (field_case.should_be_autofilled) {
+      EXPECT_EQ(ASCIIToUTF16(field_case.expected_value),
+                (input_element.*get_value_function)());
+    } else {
+      WebString expected_value = ASCIIToUTF16(field_case.expected_value);
+      if (expected_value.isEmpty())
+        EXPECT_TRUE((input_element.*get_value_function)().isEmpty());
+      else
+        EXPECT_EQ(expected_value, (input_element.*get_value_function)());
+    }
+  }
+
+  static void FillFormForAllFieldsWrapper(const FormData& form,
+                                       const WebInputElement& element) {
+    FillFormForAllElements(form, element.form());
+  }
+
+  static void FillFormIncludingNonFocusableElementsWrapper(
+      const FormData& form,
+      const WebInputElement& element) {
+    FillFormIncludingNonFocusableElements(form, element.form());
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(FormAutofillTest);
 };
@@ -835,230 +963,118 @@
   EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields2[2]);
 }
 
+// Test regular FillForm function.
 TEST_F(FormAutofillTest, FillForm) {
-  LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">"
-           "  <INPUT type=\"text\" id=\"firstname\"/>"
-           "  <INPUT type=\"text\" id=\"lastname\"/>"
-           "  <INPUT type=\"hidden\" id=\"imhidden\"/>"
-           "  <INPUT type=\"text\" id=\"notempty\" value=\"Hi\"/>"
-           "  <INPUT type=\"text\" autocomplete=\"off\" id=\"noautocomplete\"/>"
-           "  <INPUT type=\"text\" disabled=\"disabled\" id=\"notenabled\"/>"
-           "  <INPUT type=\"text\" readonly id=\"readonly\"/>"
-           "  <INPUT type=\"text\" style=\"visibility: hidden\""
-           "         id=\"invisible\"/>"
-           "  <INPUT type=\"text\" style=\"display: none\" id=\"displaynone\"/>"
-           "  <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>"
-           "</FORM>");
+  static const AutofillFieldCase field_cases[] = {
+      // fields: name, initial_value, autocomplete_attribute,
+      //         should_be_autofilled, autofill_value, expected_value
 
-  WebFrame* web_frame = GetMainFrame();
-  ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame);
+      // Regular empty fields (firstname & lastname) should be autofilled.
+      {"firstname", "", "", true, "filled firstname", "filled firstname"},
+      {"lastname", "", "", true, "filled lastname", "filled lastname"},
+      // hidden fields should not be extracted to form_data.
+      // Non empty fields should not be autofilled.
+      {"notempty", "Hi", "", false, "filled notempty", "Hi"},
+      // "noautocomplete" should not be extracted to form_data.
+      // Disabled fields should not be autofilled.
+      {"notenabled", "", "", false, "filled notenabled", ""},
+      // Readonly fields should not be autofilled.
+      {"readonly", "", "", false, "filled readonly", ""},
+      // Fields with "visibility: hidden" should not be autofilled.
+      {"invisible", "", "", false, "filled invisible", ""},
+      // Fields with "display:none" should not be autofilled.
+      {"displaynone", "", "", false, "filled displaynone", ""},
+  };
+  TestFormFillFunctions(kFormHtml, field_cases, arraysize(field_cases),
+                        FillForm, &WebInputElement::value);
+  // Verify preview selection.
+  WebInputElement firstname = GetMainFrame()->document().
+      getElementById("firstname").to<WebInputElement>();
+  EXPECT_EQ(16, firstname.selectionStart());
+  EXPECT_EQ(16, firstname.selectionEnd());
+}
 
-  FormCache form_cache;
-  std::vector<FormData> forms;
-  form_cache.ExtractForms(*web_frame, &forms);
-  ASSERT_EQ(1U, forms.size());
+TEST_F(FormAutofillTest, FillFormIncludingNonFocusableElements) {
+  static const AutofillFieldCase field_cases[] = {
+      // fields: name, initial_value, autocomplete_attribute,
+      //         should_be_autofilled, autofill_value, expected_value
 
-  // Get the input element we want to find.
-  WebElement element = web_frame->document().getElementById("firstname");
-  WebInputElement input_element = element.to<WebInputElement>();
+      // Regular empty fields (firstname & lastname) should be autofilled.
+      {"firstname", "", "", true, "filled firstname", "filled firstname"},
+      {"lastname", "", "", true, "filled lastname", "filled lastname"},
+      // hidden fields should not be extracted to form_data.
+      // Non empty fields should be overrided.
+      {"notempty", "Hi", "", true, "filled notempty", "filled notempty"},
+      // "noautocomplete" should not be extracted to form_data.
+      // Disabled fields should not be autofilled.
+      {"notenabled", "", "", false, "filled notenabled", ""},
+      // Readonly fields should not be autofilled.
+      {"readonly", "", "", false, "filled readonly", ""},
+      // Fields with "visibility: hidden" should also be autofilled.
+      {"invisible", "", "", true, "filled invisible", "filled invisible"},
+      // Fields with "display:none" should also be autofilled.
+      {"displaynone", "", "", true, "filled displaynone", "filled displaynone"},
+  };
+  TestFormFillFunctions(kFormHtml, field_cases, arraysize(field_cases),
+                        &FillFormIncludingNonFocusableElementsWrapper,
+                        &WebInputElement::value);
+}
 
-  // Find the form that contains the input element.
-  FormData form;
-  FormFieldData field;
-  EXPECT_TRUE(FindFormAndFieldForInputElement(input_element, &form, &field,
-                                              autofill::REQUIRE_AUTOCOMPLETE));
-  EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name);
-  EXPECT_EQ(GURL(web_frame->document().url()), form.origin);
-  EXPECT_EQ(GURL("http://buh.com"), form.action);
+TEST_F(FormAutofillTest, FillFormForAllElements) {
+  static const AutofillFieldCase field_cases[] = {
+      // fields: name, initial_value, autocomplete_attribute,
+      //         should_be_autofilled, autofill_value, expected_value
 
-  const std::vector<FormFieldData>& fields = form.fields;
-  ASSERT_EQ(7U, fields.size());
-
-  FormFieldData expected;
-  expected.form_control_type = "text";
-  expected.max_length = WebInputElement::defaultMaxLength();
-
-  expected.name = ASCIIToUTF16("firstname");
-  expected.value = string16();
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[0]);
-
-  expected.name = ASCIIToUTF16("lastname");
-  expected.value = string16();
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[1]);
-
-  expected.name = ASCIIToUTF16("notempty");
-  expected.value = ASCIIToUTF16("Hi");
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[2]);
-
-  expected.name = ASCIIToUTF16("notenabled");
-  expected.value = string16();
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[3]);
-
-  expected.name = ASCIIToUTF16("readonly");
-  expected.value = string16();
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[4]);
-
-  expected.name = ASCIIToUTF16("invisible");
-  expected.value = string16();
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[5]);
-
-  expected.name = ASCIIToUTF16("displaynone");
-  expected.value = string16();
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[6]);
-
-  // Fill the form.
-  form.fields[0].value = ASCIIToUTF16("Wyatt");
-  form.fields[1].value = ASCIIToUTF16("Earp");
-  form.fields[2].value = ASCIIToUTF16("Alpha");
-  form.fields[3].value = ASCIIToUTF16("Beta");
-  form.fields[4].value = ASCIIToUTF16("Gamma");
-  form.fields[5].value = ASCIIToUTF16("Delta");
-  form.fields[6].value = ASCIIToUTF16("Epsilon");
-  FillForm(form, input_element);
-
-  // Verify the filled elements.
-  WebDocument document = web_frame->document();
-  WebInputElement firstname =
-      document.getElementById("firstname").to<WebInputElement>();
-  EXPECT_TRUE(firstname.isAutofilled());
-  EXPECT_EQ(ASCIIToUTF16("Wyatt"), firstname.value());
-  EXPECT_EQ(5, firstname.selectionStart());
-  EXPECT_EQ(5, firstname.selectionEnd());
-
-  WebInputElement lastname =
-      document.getElementById("lastname").to<WebInputElement>();
-  EXPECT_TRUE(lastname.isAutofilled());
-  EXPECT_EQ(ASCIIToUTF16("Earp"), lastname.value());
-
-  // Non-empty fields are not filled.
-  WebInputElement notempty =
-      document.getElementById("notempty").to<WebInputElement>();
-  EXPECT_FALSE(notempty.isAutofilled());
-  EXPECT_EQ(ASCIIToUTF16("Hi"), notempty.value());
-
-  // autocomplete=off fields are not filled.
-  WebInputElement noautocomplete =
-      document.getElementById("noautocomplete").to<WebInputElement>();
-  EXPECT_FALSE(noautocomplete.isAutofilled());
-  EXPECT_TRUE(noautocomplete.value().isEmpty());
-
-  // Disabled fields are not filled.
-  WebInputElement notenabled =
-      document.getElementById("notenabled").to<WebInputElement>();
-  EXPECT_FALSE(notenabled.isAutofilled());
-  EXPECT_TRUE(notenabled.value().isEmpty());
-
-  // Read-only fields are not filled.
-  WebInputElement readonly =
-      document.getElementById("readonly").to<WebInputElement>();
-  EXPECT_FALSE(readonly.isAutofilled());
-  EXPECT_TRUE(readonly.value().isEmpty());
-
-  // |visibility:hidden| fields are not filled.
-  WebInputElement invisible =
-      document.getElementById("invisible").to<WebInputElement>();
-  EXPECT_FALSE(invisible.isAutofilled());
-  EXPECT_TRUE(invisible.value().isEmpty());
-
-  // |display:none| fields are not filled.
-  WebInputElement display_none =
-      document.getElementById("displaynone").to<WebInputElement>();
-  EXPECT_FALSE(display_none.isAutofilled());
-  EXPECT_TRUE(display_none.value().isEmpty());
+      // All fields except hidden fields (type="hidden") should be Autofilled.
+      {"firstname", "", "", true, "filled firstname", "filled firstname"},
+      {"lastname", "", "", true, "filled lastname", "filled lastname"},
+      // hidden fields should not be extracted to form_data.
+      {"notempty", "Hi", "", true, "filled notempty", "filled notempty"},
+      {"noautocomplete", "", "off", true, "filled noautocomplete",
+          "filled noautocomplete"},
+      {"notenabled", "", "", true, "filled notenabled", "filled notenabled"},
+      {"readonly", "", "", true, "filled readonly", "filled readonly"},
+      {"invisible", "", "", true, "filled invisible", "filled invisible"},
+      {"displaynone", "", "", true, "filled displaynone", "filled displaynone"},
+  };
+  // Enable Autocheckout because |FillFormForAllElements| is only used by
+  // Autocheckout.
+  base::FieldTrialList field_trial_list(
+      new metrics::SHA1EntropyProvider("foo"));
+  base::FieldTrialList::CreateFieldTrial("Autocheckout", "Yes");
+  TestFormFillFunctions(kFormHtml, field_cases, arraysize(field_cases),
+                        &FillFormForAllFieldsWrapper, &WebInputElement::value);
 }
 
 TEST_F(FormAutofillTest, PreviewForm) {
-  LoadHTML("<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">"
-           "  <INPUT type=\"text\" id=\"firstname\"/>"
-           "  <INPUT type=\"text\" id=\"lastname\"/>"
-           "  <INPUT type=\"text\" id=\"notempty\" value=\"Hi\"/>"
-           "  <INPUT type=\"text\" autocomplete=\"off\" id=\"noautocomplete\"/>"
-           "  <INPUT type=\"text\" disabled=\"disabled\" id=\"notenabled\"/>"
-           "  <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>"
-           "</FORM>");
+  static const char* html =
+      "<FORM name=\"TestForm\" action=\"http://buh.com\" method=\"post\">"
+      "  <INPUT type=\"text\" id=\"firstname\"/>"
+      "  <INPUT type=\"text\" id=\"lastname\"/>"
+      "  <INPUT type=\"text\" id=\"notempty\" value=\"Hi\"/>"
+      "  <INPUT type=\"text\" autocomplete=\"off\" id=\"noautocomplete\"/>"
+      "  <INPUT type=\"text\" disabled=\"disabled\" id=\"notenabled\"/>"
+      "  <INPUT type=\"submit\" name=\"reply-send\" value=\"Send\"/>"
+      "</FORM>";
 
-  WebFrame* web_frame = GetMainFrame();
-  ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame);
+  static const AutofillFieldCase field_cases[] = {
+      // Normal empty fields should be previewed.
+      {"firstname", "", "", true, "suggested firstname", "suggested firstname"},
+      {"lastname", "", "", true, "suggested lastname", "suggested lastname"},
+      // Non empty fields should not be previewed.
+      {"notempty", "Hi", "", false, "filled notempty", ""},
+      // "noautocomplete" should not be extracted to form_data.
+      // Disabled fields should not be previewed.
+      {"notenabled", "", "", false, "filled notenabled", ""},
+  };
+  TestFormFillFunctions(html, field_cases, arraysize(field_cases), &PreviewForm,
+                        &WebInputElement::suggestedValue);
 
-  FormCache form_cache;
-  std::vector<FormData> forms;
-  form_cache.ExtractForms(*web_frame, &forms);
-  ASSERT_EQ(1U, forms.size());
-
-  // Get the input element we want to find.
-  WebElement element = web_frame->document().getElementById("firstname");
-  WebInputElement input_element = element.to<WebInputElement>();
-
-  // Find the form that contains the input element.
-  FormData form;
-  FormFieldData field;
-  EXPECT_TRUE(FindFormAndFieldForInputElement(input_element, &form, &field,
-                                              autofill::REQUIRE_AUTOCOMPLETE));
-  EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name);
-  EXPECT_EQ(GURL(web_frame->document().url()), form.origin);
-  EXPECT_EQ(GURL("http://buh.com"), form.action);
-
-  const std::vector<FormFieldData>& fields = form.fields;
-  ASSERT_EQ(4U, fields.size());
-
-  FormFieldData expected;
-  expected.form_control_type = "text";
-  expected.max_length = WebInputElement::defaultMaxLength();
-
-  expected.name = ASCIIToUTF16("firstname");
-  expected.value = string16();
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[0]);
-
-  expected.name = ASCIIToUTF16("lastname");
-  expected.value = string16();
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[1]);
-
-  expected.name = ASCIIToUTF16("notempty");
-  expected.value = ASCIIToUTF16("Hi");
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[2]);
-
-  expected.name = ASCIIToUTF16("notenabled");
-  expected.value = string16();
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[3]);
-
-  // Preview the form.
-  form.fields[0].value = ASCIIToUTF16("Wyatt");
-  form.fields[1].value = ASCIIToUTF16("Earp");
-  form.fields[2].value = ASCIIToUTF16("Alpha");
-  form.fields[3].value = ASCIIToUTF16("Beta");
-  PreviewForm(form, input_element);
-
-  // Verify the previewed elements.
-  WebDocument document = web_frame->document();
-  WebInputElement firstname =
-      document.getElementById("firstname").to<WebInputElement>();
-  EXPECT_TRUE(firstname.isAutofilled());
-  EXPECT_EQ(ASCIIToUTF16("Wyatt"), firstname.suggestedValue());
+  // Verify preview selection.
+  WebInputElement firstname = GetMainFrame()->document().
+      getElementById("firstname").to<WebInputElement>();
   EXPECT_EQ(0, firstname.selectionStart());
-  EXPECT_EQ(5, firstname.selectionEnd());
-
-  WebInputElement lastname =
-      document.getElementById("lastname").to<WebInputElement>();
-  EXPECT_TRUE(lastname.isAutofilled());
-  EXPECT_EQ(ASCIIToUTF16("Earp"), lastname.suggestedValue());
-
-  // Non-empty fields are not previewed.
-  WebInputElement notempty =
-      document.getElementById("notempty").to<WebInputElement>();
-  EXPECT_FALSE(notempty.isAutofilled());
-  EXPECT_TRUE(notempty.suggestedValue().isEmpty());
-
-  // autocomplete=off fields are not previewed.
-  WebInputElement noautocomplete =
-      document.getElementById("noautocomplete").to<WebInputElement>();
-  EXPECT_FALSE(noautocomplete.isAutofilled());
-  EXPECT_TRUE(noautocomplete.suggestedValue().isEmpty());
-
-  // Disabled fields are not previewed.
-  WebInputElement notenabled =
-      document.getElementById("notenabled").to<WebInputElement>();
-  EXPECT_FALSE(notenabled.isAutofilled());
-  EXPECT_TRUE(notenabled.suggestedValue().isEmpty());
+  EXPECT_EQ(19, firstname.selectionEnd());
 }
 
 TEST_F(FormAutofillTest, Labels) {
@@ -2260,86 +2276,6 @@
   EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields2[2]);
 }
 
-TEST_F(FormAutofillTest, FillFormIncludingNonFocusableElements) {
-  LoadHTML("<FORM action=\"http://abc.com\" method=\"post\" id=\"formid\">"
-           "  <INPUT type=\"text\" id=\"firstname\" value=\"not-firstname\"/>"
-           "  <INPUT type=\"text\" id=\"lastname\"/>"
-           "  <INPUT type=\"text\" id=\"middlename\"/>"
-           "  <INPUT type=\"submit\" value=\"Send\"/>"
-           "</FORM>"
-           "<FORM action=\"http://abc.com\" method=\"post\">"
-           "  <INPUT type=\"text\" id=\"apple\"/>"
-           "  <INPUT type=\"text\" id=\"banana\"/>"
-           "  <INPUT type=\"text\" id=\"cantelope\"/>"
-           "  <INPUT type=\"submit\" value=\"Send\"/>"
-           "</FORM>");
-
-  WebFrame* web_frame = GetMainFrame();
-  ASSERT_NE(static_cast<WebFrame*>(NULL), web_frame);
-
-  FormCache form_cache;
-  std::vector<FormData> forms;
-  form_cache.ExtractForms(*web_frame, &forms);
-  ASSERT_EQ(2U, forms.size());
-
-  // Get the input element we want to find.
-  WebElement firstname = web_frame->document().getElementById("firstname");
-  WebInputElement input_element = firstname.to<WebInputElement>();
-  WebElement formid = web_frame->document().getElementById("formid");
-  WebFormElement formid_element = formid.to<WebFormElement>();
-
-  // Find the form that contains the input element.
-  FormData form;
-  FormFieldData field;
-  EXPECT_TRUE(FindFormAndFieldForInputElement(input_element, &form, &field,
-                                              autofill::REQUIRE_NONE));
-  EXPECT_EQ(ASCIIToUTF16("formid"), form.name);
-  EXPECT_EQ(GURL(web_frame->document().url()), form.origin);
-  EXPECT_EQ(GURL("http://abc.com"), form.action);
-  const std::vector<FormFieldData>& fields = form.fields;
-  ASSERT_EQ(3U, fields.size());
-  FormFieldData expected;
-  expected.form_control_type = "text";
-  expected.max_length = WebInputElement::defaultMaxLength();
-  expected.is_autofilled = false;
-  expected.name = ASCIIToUTF16("firstname");
-  expected.value = ASCIIToUTF16("not-firstname");
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[0]);
-  expected.name = ASCIIToUTF16("lastname");
-  expected.value = string16();
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields[1]);
-
-  // Fill the form.
-  form.fields[0].value = ASCIIToUTF16("FirstName");
-  form.fields[1].value = ASCIIToUTF16("LastName");
-  FillFormIncludingNonFocusableElements(form, formid_element);
-
-  // Find the newly-filled form that contains the input element, and verify
-  // if all fields have been filled.
-  FormData form2;
-  FormFieldData field2;
-  EXPECT_TRUE(FindFormAndFieldForInputElement(input_element, &form2, &field2,
-                                              autofill::REQUIRE_NONE));
-  EXPECT_EQ(ASCIIToUTF16("formid"), form2.name);
-  EXPECT_EQ(GURL(web_frame->document().url()), form2.origin);
-  EXPECT_EQ(GURL("http://abc.com"), form2.action);
-  std::vector<FormFieldData>& fields2 = form2.fields;
-  ASSERT_EQ(3U, fields2.size());
-  expected.form_control_type = "text";
-  expected.max_length = WebInputElement::defaultMaxLength();
-  // All of the fields should be autofilled now.
-  expected.is_autofilled = true;
-
-  // fields[0].value should have changed from not-firstname to FirstName.
-  expected.name = ASCIIToUTF16("firstname");
-  expected.value = ASCIIToUTF16("FirstName");
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields2[0]);
-
-  expected.name = ASCIIToUTF16("lastname");
-  expected.value = ASCIIToUTF16("LastName");
-  EXPECT_FORM_FIELD_DATA_EQUALS(expected, fields2[1]);
-}
-
 TEST_F(FormAutofillTest, ThreePartPhone) {
   LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">"
            "  Phone:"
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index dfe9d49..975b4f4 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -260,7 +260,8 @@
   CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
 }
 
-// Tests that changing the username does not fill a read-only password field.
+// Tests that if a password or input element is marked as readonly, neither
+// field is autofilled on page load.
 TEST_F(PasswordAutofillAgentTest, NoInitialAutocompleteForReadOnly) {
   password_element_.setAttribute(WebString::fromUTF8("readonly"),
                                  WebString::fromUTF8("true"));
@@ -269,9 +270,7 @@
   // autocomplete.
   SimulateOnFillPasswordForm(fill_data_);
 
-  // Only the username should have been autocompleted.
-  // TODO(jcivelli): may be we should not event fill the username?
-  CheckTextFieldsState(kAliceUsername, true, std::string(), false);
+  CheckTextFieldsState(std::string(), false, std::string(), false);
 }
 
 // Tests that having a non-matching username precludes the autocomplete.
@@ -286,19 +285,17 @@
   CheckTextFieldsState("bogus", false, std::string(), false);
 }
 
-// Tests that changing the username does not fill a field specifying
+// Tests that we do not autofill username/passwords if marked as
 // autocomplete="off".
 TEST_F(PasswordAutofillAgentTest, NoInitialAutocompleteForAutocompleteOff) {
-  password_element_.setAttribute(WebString::fromUTF8("autocomplete"),
+  username_element_.setAttribute(WebString::fromUTF8("autocomplete"),
                                  WebString::fromUTF8("off"));
 
   // Simulate the browser sending back the login info, it triggers the
   // autocomplete.
   SimulateOnFillPasswordForm(fill_data_);
 
-  // Only the username should have been autocompleted.
-  // TODO(jcivelli): may be we should not event fill the username?
-  CheckTextFieldsState(kAliceUsername, true, std::string(), false);
+  CheckTextFieldsState(std::string(), false, std::string(), false);
 }
 
 TEST_F(PasswordAutofillAgentTest, NoAutocompleteForTextFieldPasswords) {
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index faf742d..9f8ef8d 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -62,8 +62,6 @@
 #include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h"
 #include "chrome/renderer/searchbox/searchbox.h"
 #include "chrome/renderer/searchbox/searchbox_extension.h"
-#include "chrome/renderer/spellchecker/spellcheck.h"
-#include "chrome/renderer/spellchecker/spellcheck_provider.h"
 #include "chrome/renderer/tts_dispatcher.h"
 #include "chrome/renderer/validation_message_agent.h"
 #include "components/autofill/content/renderer/autofill_agent.h"
@@ -105,6 +103,11 @@
 #include "chrome/renderer/media/webrtc_logging_message_filter.h"
 #endif
 
+#if defined(ENABLE_SPELLCHECK)
+#include "chrome/renderer/spellchecker/spellcheck.h"
+#include "chrome/renderer/spellchecker/spellcheck_provider.h"
+#endif
+
 using autofill::AutofillAgent;
 using autofill::PasswordAutofillAgent;
 using autofill::PasswordGenerationManager;
@@ -161,6 +164,7 @@
   existing_values->swap(values);
 }
 
+#if defined(ENABLE_SPELLCHECK)
 class SpellCheckReplacer : public content::RenderViewVisitor {
  public:
   explicit SpellCheckReplacer(SpellCheck* spellcheck)
@@ -178,6 +182,7 @@
   provider->set_spellcheck(spellcheck_);
   return true;
 }
+#endif
 
 // For certain sandboxed Pepper plugins, use the JavaScript Content Settings.
 bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) {
@@ -232,7 +237,9 @@
           extension_dispatcher_.get()));
   prescient_networking_dispatcher_.reset(new PrescientNetworkingDispatcher());
   net_predictor_.reset(new RendererNetPredictor());
+#if defined(ENABLE_SPELLCHECK)
   spellcheck_.reset(new SpellCheck());
+#endif
   visited_link_slave_.reset(new visitedlink::VisitedLinkSlave());
 #if defined(FULL_SAFE_BROWSING)
   phishing_classifier_.reset(safe_browsing::PhishingClassifierFilter::Create());
@@ -250,7 +257,9 @@
 #if defined(FULL_SAFE_BROWSING)
   thread->AddObserver(phishing_classifier_.get());
 #endif
+#if defined(ENABLE_SPELLCHECK)
   thread->AddObserver(spellcheck_.get());
+#endif
   thread->AddObserver(visited_link_slave_.get());
   thread->AddObserver(prerender_dispatcher_.get());
 
@@ -353,7 +362,9 @@
 #if defined(ENABLE_PRINTING)
   new printing::PrintWebViewHelper(render_view);
 #endif
+#if defined(ENABLE_SPELLCHECK)
   new SpellCheckProvider(render_view, spellcheck_.get());
+#endif
   new prerender::PrerendererClient(render_view);
 #if defined(FULL_SAFE_BROWSING)
   safe_browsing::MalwareDOMDetails::Create(render_view);
@@ -773,7 +784,8 @@
        app_url.host() == "plus.sandbox.google.com") &&
       manifest_url.SchemeIs("https") &&
       manifest_url.host() == "ssl.gstatic.com" &&
-      (manifest_url.path().find("s2/oz/nacl/") == 1);
+      ((manifest_url.path().find("s2/oz/nacl/") == 1) ||
+       (manifest_url.path().find("photos/nacl/") == 1));
 
   bool is_extension_from_webstore =
       extension && extension->from_webstore();
@@ -1068,11 +1080,6 @@
   return visited_link_slave_->IsVisited(link_hash);
 }
 
-void ChromeContentRendererClient::PrefetchHostName(const char* hostname,
-                                                   size_t length) {
-  net_predictor_->Resolve(hostname, length);
-}
-
 WebKit::WebPrescientNetworking*
 ChromeContentRendererClient::GetPrescientNetworking() {
   return prescient_networking_dispatcher_.get();
@@ -1163,6 +1170,7 @@
       extensions, old_url, new_url, should_consider_workaround);
 }
 
+#if defined(ENABLE_SPELLCHECK)
 void ChromeContentRendererClient::SetSpellcheck(SpellCheck* spellcheck) {
   RenderThread* thread = RenderThread::Get();
   if (spellcheck_.get() && thread)
@@ -1173,10 +1181,13 @@
   if (thread)
     thread->AddObserver(spellcheck_.get());
 }
+#endif
 
 void ChromeContentRendererClient::OnPurgeMemory() {
+#if defined(ENABLE_SPELLCHECK)
   DVLOG(1) << "Resetting spellcheck in renderer client";
   SetSpellcheck(new SpellCheck());
+#endif
 }
 
 bool ChromeContentRendererClient::IsAdblockInstalled() {
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index 44f2774..94d9431 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -21,8 +21,10 @@
 class ExtensionSet;
 class PrescientNetworkingDispatcher;
 class RendererNetPredictor;
+#if defined(ENABLE_SPELLCHECK)
 class SpellCheck;
 class SpellCheckProvider;
+#endif
 
 struct ChromeViewHostMsg_GetPluginInfo_Output;
 
@@ -111,7 +113,6 @@
   virtual unsigned long long VisitedLinkHash(const char* canonical_url,
                                              size_t length) OVERRIDE;
   virtual bool IsLinkVisited(unsigned long long link_hash) OVERRIDE;
-  virtual void PrefetchHostName(const char* hostname, size_t length) OVERRIDE;
   virtual WebKit::WebPrescientNetworking* GetPrescientNetworking() OVERRIDE;
   virtual bool ShouldOverridePageVisibilityState(
       const content::RenderView* render_view,
@@ -139,9 +140,11 @@
   // For testing.
   void SetExtensionDispatcher(extensions::Dispatcher* extension_dispatcher);
 
+#if defined(ENABLE_SPELLCHECK)
   // Sets a new |spellcheck|. Used for low-mem restart and testing only.
   // Takes ownership of |spellcheck|.
   void SetSpellcheck(SpellCheck* spellcheck);
+#endif
 
   // Called in low-memory conditions to dump the memory used by the spellchecker
   // and start over.
@@ -189,7 +192,9 @@
       permissions_policy_delegate_;
   scoped_ptr<PrescientNetworkingDispatcher> prescient_networking_dispatcher_;
   scoped_ptr<RendererNetPredictor> net_predictor_;
+#if defined(ENABLE_SPELLCHECK)
   scoped_ptr<SpellCheck> spellcheck_;
+#endif
   scoped_ptr<visitedlink::VisitedLinkSlave> visited_link_slave_;
   scoped_ptr<safe_browsing::PhishingClassifierFilter> phishing_classifier_;
   scoped_ptr<prerender::PrerenderDispatcher> prerender_dispatcher_;
diff --git a/chrome/renderer/chrome_content_renderer_client_unittest.cc b/chrome/renderer/chrome_content_renderer_client_unittest.cc
index d26b452..6eb6c3f 100644
--- a/chrome/renderer/chrome_content_renderer_client_unittest.cc
+++ b/chrome/renderer/chrome_content_renderer_client_unittest.cc
@@ -39,7 +39,8 @@
 
 const char kAllowedNaClAppURL1[] = "https://plus.google.com";
 const char kAllowedNaClAppURL2[] = "https://plus.sandbox.google.com";
-const char kAllowedNaClManifestURL[] = "https://ssl.gstatic.com/s2/oz/nacl/foo";
+const char kAllowedNaClManifestURL1[] = "https://ssl.gstatic.com/s2/oz/nacl/foo";
+const char kAllowedNaClManifestURL2[] = "https://ssl.gstatic.com/photos/nacl/foo";
 
 bool AllowsDevInterfaces(const WebPluginParams& params) {
   for (size_t i = 0; i < params.attributeNames.size(); ++i) {
@@ -202,17 +203,25 @@
   // interfaces. There is a whitelist for the app URL and the manifest URL.
   {
     WebPluginParams params;
-    // Whitelisted manifest URL, whitelisted app URL root #1 is allowed.
+    // Whitelisted manifest URL #1, whitelisted app URL root #1 is allowed.
     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kAllowedNaClManifestURL),
+        GURL(kAllowedNaClManifestURL1),
         GURL(kAllowedNaClAppURL1),
         kNaClRestricted,
         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
         &params));
     EXPECT_FALSE(AllowsDevInterfaces(params));
-    // Whitelisted manifest URL, whitelisted app URL root #2 is allowed.
+    // Whitelisted manifest URL #2, whitelisted app URL root #1 is allowed.
     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kAllowedNaClManifestURL),
+        GURL(kAllowedNaClManifestURL1),
+        GURL(kAllowedNaClAppURL1),
+        kNaClRestricted,
+        CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
+        &params));
+    EXPECT_FALSE(AllowsDevInterfaces(params));
+    // Whitelisted manifest URL #1, whitelisted app URL root #2 is allowed.
+    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
+        GURL(kAllowedNaClManifestURL1),
         GURL(kAllowedNaClAppURL2),
         kNaClRestricted,
         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
@@ -221,19 +230,19 @@
 
     // Whitelisted manifest URL, bad app URLs, NOT allowed.
     EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kAllowedNaClManifestURL),
+        GURL(kAllowedNaClManifestURL1),
         GURL("http://plus.google.com/foo"),  // http scheme
         kNaClRestricted,
         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
         &params));
     EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kAllowedNaClManifestURL),
+        GURL(kAllowedNaClManifestURL1),
         GURL("http://plus.sandbox.google.com/foo"),  // http scheme
         kNaClRestricted,
         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
         &params));
     EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kAllowedNaClManifestURL),
+        GURL(kAllowedNaClManifestURL1),
         GURL("https://plus.google.evil.com/foo"),  // bad host
         kNaClRestricted,
         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
@@ -262,7 +271,7 @@
   {
     WebPluginParams params;
     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kAllowedNaClManifestURL),
+        GURL(kAllowedNaClManifestURL1),
         GURL(kAllowedNaClAppURL1),
         kNaClUnrestricted,
         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
@@ -275,7 +284,7 @@
     WebPluginParams params;
     AddFakeDevAttribute(&params);
     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kAllowedNaClManifestURL),
+        GURL(kAllowedNaClManifestURL1),
         GURL(kAllowedNaClAppURL1),
         kNaClRestricted,
         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
diff --git a/chrome/renderer/chrome_render_view_observer.cc b/chrome/renderer/chrome_render_view_observer.cc
index 5d67159..c6a45fd 100644
--- a/chrome/renderer/chrome_render_view_observer.cc
+++ b/chrome/renderer/chrome_render_view_observer.cc
@@ -30,6 +30,7 @@
 #include "content/public/renderer/render_view.h"
 #include "extensions/common/constants.h"
 #include "net/base/data_url.h"
+#include "skia/ext/image_operations.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/WebKit/public/platform/WebCString.h"
 #include "third_party/WebKit/public/platform/WebRect.h"
@@ -49,6 +50,7 @@
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/gfx/favicon_size.h"
 #include "ui/gfx/size.h"
+#include "ui/gfx/size_f.h"
 #include "ui/gfx/skbitmap_operations.h"
 #include "v8/include/v8-testing.h"
 #include "webkit/glue/webkit_glue.h"
@@ -169,6 +171,42 @@
   replacements.ClearRef();
   return url.ReplaceComponents(replacements);
 }
+
+// If the source image is null or occupies less area than
+// |thumbnail_min_area_pixels|, we return the image unmodified.  Otherwise, we
+// scale down the image so that the width and height do not exceed
+// |thumbnail_max_size_pixels|, preserving the original aspect ratio.
+static SkBitmap Downscale(WebKit::WebImage image,
+                          int thumbnail_min_area_pixels,
+                          gfx::Size thumbnail_max_size_pixels) {
+  if (image.isNull())
+    return SkBitmap();
+
+  gfx::Size image_size = image.size();
+
+  if (image_size.GetArea() < thumbnail_min_area_pixels)
+    return image.getSkBitmap();
+
+  if (image_size.width() <= thumbnail_max_size_pixels.width() &&
+      image_size.height() <= thumbnail_max_size_pixels.height())
+    return image.getSkBitmap();
+
+  gfx::SizeF scaled_size = image_size;
+
+  if (scaled_size.width() > thumbnail_max_size_pixels.width()) {
+    scaled_size.Scale(thumbnail_max_size_pixels.width() / scaled_size.width());
+  }
+
+  if (scaled_size.height() > thumbnail_max_size_pixels.height()) {
+    scaled_size.Scale(
+        thumbnail_max_size_pixels.height() / scaled_size.height());
+  }
+
+  return skia::ImageOperations::Resize(image.getSkBitmap(),
+                                       skia::ImageOperations::RESIZE_GOOD,
+                                       static_cast<int>(scaled_size.width()),
+                                       static_cast<int>(scaled_size.height()));
+}
 }  // namespace
 
 ChromeRenderViewObserver::ChromeRenderViewObserver(
@@ -211,6 +249,8 @@
                         OnSetClientSidePhishingDetection)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SetVisuallyDeemphasized,
                         OnSetVisuallyDeemphasized)
+    IPC_MESSAGE_HANDLER(ChromeViewMsg_RequestThumbnailForContextNode,
+                        OnRequestThumbnailForContextNode)
 #if defined(OS_CHROMEOS)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_StartFrameSniffer, OnStartFrameSniffer)
 #endif
@@ -336,6 +376,20 @@
   }
 }
 
+void ChromeRenderViewObserver::OnRequestThumbnailForContextNode(
+    int thumbnail_min_area_pixels, gfx::Size thumbnail_max_size_pixels) {
+  WebNode context_node = render_view()->GetContextMenuNode();
+  SkBitmap thumbnail;
+  if (context_node.isElementNode()) {
+    WebKit::WebImage image = context_node.to<WebElement>().imageContents();
+    thumbnail = Downscale(image,
+                          thumbnail_min_area_pixels,
+                          thumbnail_max_size_pixels);
+  }
+  Send(new ChromeViewHostMsg_RequestThumbnailForContextNode_ACK(routing_id(),
+                                                                thumbnail));
+}
+
 void ChromeRenderViewObserver::OnStartFrameSniffer(const string16& frame_name) {
   new FrameSniffer(render_view(), frame_name);
 }
diff --git a/chrome/renderer/chrome_render_view_observer.h b/chrome/renderer/chrome_render_view_observer.h
index 288a910..82f36c0 100644
--- a/chrome/renderer/chrome_render_view_observer.h
+++ b/chrome/renderer/chrome_render_view_observer.h
@@ -16,6 +16,7 @@
 #include "content/public/common/top_controls_state.h"
 #include "content/public/renderer/render_view_observer.h"
 #include "third_party/WebKit/public/web/WebPermissionClient.h"
+#include "ui/gfx/size.h"
 #include "url/gurl.h"
 
 class ChromeRenderProcessObserver;
@@ -130,6 +131,8 @@
   void OnSetAllowRunningInsecureContent(bool allow);
   void OnSetClientSidePhishingDetection(bool enable_phishing_detection);
   void OnSetVisuallyDeemphasized(bool deemphasized);
+  void OnRequestThumbnailForContextNode(int thumbnail_min_area_pixels,
+                                        gfx::Size thumbnail_max_size_pixels);
   void OnStartFrameSniffer(const string16& frame_name);
   void OnGetFPS();
   void OnAddStrictSecurityHost(const std::string& host);
diff --git a/chrome/renderer/extensions/console.cc b/chrome/renderer/extensions/console.cc
index b5dbd89..cefe2cb 100644
--- a/chrome/renderer/extensions/console.cc
+++ b/chrome/renderer/extensions/console.cc
@@ -62,6 +62,30 @@
   CHECK(false) << message;
 }
 
+typedef void (*LogMethod)(v8::Handle<v8::Context> context,
+                          const std::string& message);
+
+void BoundLogMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  LogMethod log_method = reinterpret_cast<LogMethod>(
+      info.Data().As<v8::External>()->Value());
+  std::string message;
+  for (int i = 0; i < info.Length(); ++i) {
+    if (i > 0)
+      message += " ";
+    message += *v8::String::AsciiValue(info[i]);
+  }
+  (*log_method)(v8::Context::GetCalling(), message);
+}
+
+void BindLogMethod(v8::Local<v8::Object> target,
+                   const std::string& name,
+                   LogMethod log_method) {
+  v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(
+      &BoundLogMethodCallback,
+      v8::External::New(reinterpret_cast<void*>(log_method)));
+  target->Set(v8::String::New(name.c_str()), tmpl->GetFunction());
+}
+
 }  // namespace
 
 void Debug(content::RenderView* render_view, const std::string& message) {
@@ -147,5 +171,15 @@
   AddMessage(render_view, level, message);
 }
 
+v8::Local<v8::Object> AsV8Object() {
+  v8::HandleScope handle_scope;
+  v8::Local<v8::Object> console_object = v8::Object::New();
+  BindLogMethod(console_object, "debug", &Debug);
+  BindLogMethod(console_object, "log", &Log);
+  BindLogMethod(console_object, "warn", &Warn);
+  BindLogMethod(console_object, "error", &Error);
+  return handle_scope.Close(console_object);
+}
+
 }  // namespace console
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/console.h b/chrome/renderer/extensions/console.h
index 9ad3e3f..a1a6e28 100644
--- a/chrome/renderer/extensions/console.h
+++ b/chrome/renderer/extensions/console.h
@@ -45,6 +45,12 @@
                 content::ConsoleMessageLevel level,
                 const std::string& message);
 
+// Returns a new v8::Object with each standard log method (Debug/Log/Warn/Error)
+// bound to respective debug/log/warn/error methods. This is a direct drop-in
+// replacement for the standard devtools console.* methods usually accessible
+// from JS.
+v8::Local<v8::Object> AsV8Object();
+
 }  // namespace console
 
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/dispatcher.cc b/chrome/renderer/extensions/dispatcher.cc
index a4f6688..5b7f827 100644
--- a/chrome/renderer/extensions/dispatcher.cc
+++ b/chrome/renderer/extensions/dispatcher.cc
@@ -9,6 +9,8 @@
 #include "base/debug/alias.h"
 #include "base/json/json_reader.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -18,6 +20,7 @@
 #include "chrome/common/extensions/api/extension_api.h"
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
 #include "chrome/common/extensions/extension_messages.h"
 #include "chrome/common/extensions/features/base_feature_provider.h"
@@ -1099,7 +1102,20 @@
     // The API will be automatically set up when first used.
     if (extension->HasAPIPermission(APIPermission::kWebView)) {
       module_system->Require("webView");
-      if (Feature::GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV)
+      bool includeExperimental =
+          Feature::GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV ||
+          extension->id() == extension_misc::kIdentityApiUiAppId;
+      if (!includeExperimental) {
+        // TODO(asargent) We need a whitelist for webview experimental.
+        // crbug.com/264852
+        std::string id_hash = base::SHA1HashString(extension->id());
+        std::string hexencoded_id_hash = base::HexEncode(id_hash.c_str(),
+                                                         id_hash.length());
+        if (hexencoded_id_hash == "8C3741E3AF0B93B6E8E0DDD499BB0B74839EA578" ||
+            hexencoded_id_hash == "E703483CEF33DEC18B4B6DD84B5C776FB9182BDB")
+          includeExperimental = true;
+      }
+      if (includeExperimental)
         module_system->Require("webViewExperimental");
     } else {
       module_system->Require("denyWebView");
diff --git a/chrome/renderer/extensions/json_schema_unittest.cc b/chrome/renderer/extensions/json_schema_unittest.cc
index 35de3d9..3c70460 100644
--- a/chrome/renderer/extensions/json_schema_unittest.cc
+++ b/chrome/renderer/extensions/json_schema_unittest.cc
@@ -91,4 +91,8 @@
   TestFunction("testCheckSchemaOverlap");
 }
 
+TEST_F(JsonSchemaTest, TestInstanceOf) {
+  TestFunction("testInstanceOf");
+}
+
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/module_system.cc b/chrome/renderer/extensions/module_system.cc
index ab6a15f..bf3dd70 100644
--- a/chrome/renderer/extensions/module_system.cc
+++ b/chrome/renderer/extensions/module_system.cc
@@ -117,8 +117,7 @@
                             error_message.c_str());
 }
 
-ModuleSystem::ModuleSystem(ChromeV8Context* context,
-                           SourceMap* source_map)
+ModuleSystem::ModuleSystem(ChromeV8Context* context, SourceMap* source_map)
     : ObjectBackedNativeHandler(context),
       context_(context),
       source_map_(source_map),
@@ -237,6 +236,8 @@
     natives->Get(v8::String::NewSymbol("require")),
     natives->Get(v8::String::NewSymbol("requireNative")),
     exports,
+    // Libraries that we magically expose to every module.
+    console::AsV8Object(),
     // Each safe builtin. Keep in order with the arguments in WrapSource.
     context_->safe_builtins()->GetArray(),
     context_->safe_builtins()->GetFunction(),
@@ -521,7 +522,8 @@
   v8::HandleScope handle_scope;
   // Keep in order with the arguments in RequireForJsInner.
   v8::Handle<v8::String> left = v8::String::New(
-      "(function(require, requireNative, exports,"
+      "(function(require, requireNative, exports, "
+                "console, "
                 "$Array, $Function, $JSON, $Object, $RegExp, $String) {"
        "'use strict';");
   v8::Handle<v8::String> right = v8::String::New("\n})");
diff --git a/chrome/renderer/net/net_error_helper.cc b/chrome/renderer/net/net_error_helper.cc
index 5df8611..73e63b7 100644
--- a/chrome/renderer/net/net_error_helper.cc
+++ b/chrome/renderer/net/net_error_helper.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/json/json_writer.h"
+#include "base/metrics/histogram.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/common/localized_error.h"
@@ -234,6 +235,10 @@
 
   string16 frame_xpath;
   render_view()->EvaluateScript(frame_xpath, js16, 0, false);
+
+  UMA_HISTOGRAM_ENUMERATION("DnsProbe.ErrorPageUpdateStatus",
+                            last_probe_status_,
+                            chrome_common_net::DNS_PROBE_MAX);
 }
 
 WebKit::WebURLError NetErrorHelper::GetUpdatedError() const {
diff --git a/chrome/renderer/pepper/pepper_flash_menu_host.cc b/chrome/renderer/pepper/pepper_flash_menu_host.cc
index 3d4b905..a948ce5 100644
--- a/chrome/renderer/pepper/pepper_flash_menu_host.cc
+++ b/chrome/renderer/pepper/pepper_flash_menu_host.cc
@@ -8,6 +8,7 @@
 #include "content/public/common/context_menu_params.h"
 #include "content/public/renderer/render_view.h"
 #include "content/public/renderer/renderer_ppapi_host.h"
+#include "ipc/ipc_message.h"
 #include "ppapi/c/private/ppb_flash_menu.h"
 #include "ppapi/host/dispatch_host_message.h"
 #include "ppapi/host/ppapi_host.h"
@@ -196,7 +197,7 @@
 void PepperFlashMenuHost::SendMenuReply(int32_t result, int action) {
   ppapi::host::ReplyMessageContext reply_context(
       ppapi::proxy::ResourceMessageReplyParams(pp_resource(), 0),
-      NULL);
+      NULL, MSG_ROUTING_NONE);
   reply_context.params.set_result(result);
   host()->SendReply(reply_context,
                     PpapiPluginMsg_FlashMenu_ShowReply(action));
diff --git a/chrome/renderer/pepper/pepper_flash_renderer_host.cc b/chrome/renderer/pepper/pepper_flash_renderer_host.cc
index e305946..7e8b421 100644
--- a/chrome/renderer/pepper/pepper_flash_renderer_host.cc
+++ b/chrome/renderer/pepper/pepper_flash_renderer_host.cc
@@ -220,20 +220,16 @@
       ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
   host_dispatcher->set_allow_plugin_reentrancy();
 
-  // Grab a weak pointer to ourselves on the stack so we can check if we are
-  // still alive.
-  base::WeakPtr<PepperFlashRendererHost> weak_ptr = weak_factory_.GetWeakPtr();
   // Keep track of reply contexts in case we are destroyed during a Navigate
   // call. Even if we are destroyed, we still need to send these replies to
   // unblock the plugin process.
   navigate_replies_.push_back(host_context->MakeReplyMessageContext());
-  plugin_instance->Navigate(data, target.c_str(), from_user_action);
-  // This object might have been destroyed by this point. If it is destroyed
-  // the reply will be sent in the destructor. Otherwise send the reply here.
-  if (weak_ptr.get()) {
-    SendReply(navigate_replies_.back(), IPC::Message());
-    navigate_replies_.pop_back();
-  }
+
+  // If the object is destroyed before the callback is invoked, the reply will
+  // be sent by the destructor.
+  plugin_instance->Navigate(data, target.c_str(), from_user_action,
+      base::Bind(&PepperFlashRendererHost::DidNavigate,
+                 weak_factory_.GetWeakPtr()));
 
   // Return PP_OK_COMPLETIONPENDING so that no reply is automatically sent.
   return PP_OK_COMPLETIONPENDING;
@@ -256,4 +252,9 @@
   return PP_OK;
 }
 
+void PepperFlashRendererHost::DidNavigate(int32_t unused) {
+  SendReply(navigate_replies_.back(), IPC::Message());
+  navigate_replies_.pop_back();
+}
+
 }  // namespace chrome
diff --git a/chrome/renderer/pepper/pepper_flash_renderer_host.h b/chrome/renderer/pepper/pepper_flash_renderer_host.h
index 299b109..ede076b 100644
--- a/chrome/renderer/pepper/pepper_flash_renderer_host.h
+++ b/chrome/renderer/pepper/pepper_flash_renderer_host.h
@@ -59,6 +59,8 @@
                           const PP_Rect& rect);
   int32_t OnInvokePrinting(ppapi::host::HostMessageContext* host_context);
 
+  void DidNavigate(int32_t unused);
+
   base::WeakPtrFactory<PepperFlashRendererHost> weak_factory_;
   // A stack of ReplyMessageContexts to track Navigate() calls which have not
   // yet been replied to.
diff --git a/chrome/renderer/resources/extensions/app_window_custom_bindings.js b/chrome/renderer/resources/extensions/app_window_custom_bindings.js
index 944189a..a4d6b2b 100644
--- a/chrome/renderer/resources/extensions/app_window_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/app_window_custom_bindings.js
@@ -136,6 +136,16 @@
           bounds1.width == bounds2.width && bounds1.height == bounds2.height);
 }
 
+function dispatchEventIfExists(target, name) {
+  // Sometimes apps like to put their own properties on the window which
+  // break our assumptions.
+  var event = target[name];
+  if (event && (typeof event.dispatch == 'function'))
+    event.dispatch();
+  else
+    console.warn('Could not dispatch ' + name + ', event has been clobbered');
+}
+
 function updateAppWindowProperties(update) {
   if (!appWindowData)
     return;
@@ -147,25 +157,25 @@
   var currentWindow = currentAppWindow;
 
   if (!boundsEqual(oldData.bounds, update.bounds))
-    currentWindow["onBoundsChanged"].dispatch();
+    dispatchEventIfExists(currentWindow, "onBoundsChanged");
 
   if (!oldData.fullscreen && update.fullscreen)
-    currentWindow["onFullscreened"].dispatch();
+    dispatchEventIfExists(currentWindow, "onFullscreened");
   if (!oldData.minimized && update.minimized)
-    currentWindow["onMinimized"].dispatch();
+    dispatchEventIfExists(currentWindow, "onMinimized");
   if (!oldData.maximized && update.maximized)
-    currentWindow["onMaximized"].dispatch();
+    dispatchEventIfExists(currentWindow, "onMaximized");
 
   if ((oldData.fullscreen && !update.fullscreen) ||
       (oldData.minimized && !update.minimized) ||
       (oldData.maximized && !update.maximized))
-    currentWindow["onRestored"].dispatch();
+    dispatchEventIfExists(currentWindow, "onRestored");
 };
 
 function onAppWindowClosed() {
   if (!currentAppWindow)
     return;
-  currentAppWindow.onClosed.dispatch();
+  dispatchEventIfExists(currentAppWindow, "onClosed");
 }
 
 exports.binding = appWindow.generate();
diff --git a/chrome/renderer/resources/extensions/file_system_custom_bindings.js b/chrome/renderer/resources/extensions/file_system_custom_bindings.js
index f66c20d..921ff23 100644
--- a/chrome/renderer/resources/extensions/file_system_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_system_custom_bindings.js
@@ -30,30 +30,60 @@
         var callback = request.callback;
         request.callback = null;
 
-        var fileSystemId = response.fileSystemId;
-        var baseName = response.baseName;
-        var id = response.id;
-        var fs = GetIsolatedFileSystem(fileSystemId);
+        var entries = [];
+        var hasError = false;
 
-        try {
-          // TODO(koz): fs.root.getFile() makes a trip to the browser process,
-          // but it might be possible avoid that by calling
-          // WebFrame::createFileEntry().
-          fs.root.getFile(baseName, {}, function(fileEntry) {
-            entryIdManager.registerEntry(id, fileEntry);
-            callback(fileEntry);
-          }, function(fileError) {
-            lastError.run('fileSystem.' + functionName,
-                          'Error getting fileEntry, code: ' + fileError.code,
-                          request.stack,
-                          callback);
-          });
-        } catch (e) {
-          lastError.run('fileSystem.' + functionName,
-                        'Error in event handler for onLaunched: ' + e.stack,
-                        request.stack,
-                        callback);
-        }
+        // Loop through the response entries and asynchronously get the
+        // FileEntry for each. We use hasError to ensure that only the first
+        // error is reported. Note that an error can occur either during the
+        // loop or in the asynchronous error callback to getFile.
+        $Array.forEach(response.entries, function(entry) {
+          if (hasError)
+            return;
+          var fileSystemId = entry.fileSystemId;
+          var baseName = entry.baseName;
+          var id = entry.id;
+          var fs = GetIsolatedFileSystem(fileSystemId);
+
+          try {
+            // TODO(koz): fs.root.getFile() makes a trip to the browser process,
+            // but it might be possible avoid that by calling
+            // WebFrame::createFileEntry().
+            fs.root.getFile(baseName, {}, function(fileEntry) {
+              if (hasError)
+                return;
+              entryIdManager.registerEntry(id, fileEntry);
+              entries.push(fileEntry);
+              // Once all entries are ready, pass them to the callback. In the
+              // event of an error, this condition will never be satisfied so
+              // the callback will not be called with any entries.
+              if (entries.length == response.entries.length) {
+                if (response.multiple) {
+                  callback(entries);
+                } else {
+                  callback(entries[0]);
+                }
+              }
+            }, function(fileError) {
+              if (!hasError) {
+                hasError = true;
+                lastError.run(
+                    'fileSystem.' + functionName,
+                    'Error getting fileEntry, code: ' + fileError.code,
+                    request.stack,
+                    callback);
+              }
+            });
+          } catch (e) {
+            if (!hasError) {
+              hasError = true;
+              lastError.run('fileSystem.' + functionName,
+                            'Error getting fileEntry: ' + e.stack,
+                            request.stack,
+                            callback);
+            }
+          }
+        });
       }
     });
   };
diff --git a/chrome/renderer/resources/extensions/json_schema.js b/chrome/renderer/resources/extensions/json_schema.js
index c894ef2..4b8996a 100644
--- a/chrome/renderer/resources/extensions/json_schema.js
+++ b/chrome/renderer/resources/extensions/json_schema.js
@@ -42,13 +42,11 @@
 var CHECK = requireNative('logging').CHECK;
 
 function isInstanceOfClass(instance, className) {
-  if (!instance)
-    return false;
-
-  if (Object.prototype.toString.call(instance) == "[object " + className + "]")
-    return true;
-
-  return isInstanceOfClass($Object.getPrototypeOf(instance), className);
+  while ((instance = instance.__proto__)) {
+    if (instance.constructor.name == className)
+      return true;
+  }
+  return false;
 }
 
 function isOptionalValue(value) {
diff --git a/chrome/renderer/resources/neterror.html b/chrome/renderer/resources/neterror.html
index 33eb8cd..e29dd39 100644
--- a/chrome/renderer/resources/neterror.html
+++ b/chrome/renderer/resources/neterror.html
@@ -311,7 +311,12 @@
 }
 
 function diagnoseErrors() {
-  location = "chrome-extension://kodldpbjkkmmnilagfdheibampofhaom/index.html";
+  var extension_id = "idddmepepmjcgiedknnmlbadcokidhoa";
+  var diagnose_frame = document.getElementById('diagnose-frame');
+  diagnose_frame.innerHTML =
+      '<iframe src="+chrome-extension://' + extension_id +
+      '/index.html"></iframe>';
+
 }
 
 // Subframes use a different layout but the same html file.  This is to make it
@@ -326,6 +331,16 @@
   jstProcess(context, document.getElementById('details'));
 }
 
+<if expr="is_macosx or is_linux or is_android">
+//Re-orders buttons. Used on Mac, Linux, and Android, where reload should go on the right.
+function swapButtonOrder() {
+  reloadButton = document.getElementById('reload-button');
+  moreLessButton = document.getElementById('more-less-button');
+  reloadButton.parentNode.insertBefore(moreLessButton, reloadButton);
+}
+document.addEventListener("DOMContentLoaded", swapButtonOrder);
+</if>
+
 </script>
 
 <body id="t">
@@ -354,6 +369,7 @@
     </div>
     <button id="diagnose-button" onclick="diagnoseErrors()"
       jscontent="diagnose" jsdisplay="diagnose"></button>
+    <div id="diagnose-frame" class="hidden"></div>
     <div class="error-code" jscontent="errorCode"></div>
    </div>
   </div>
diff --git a/chrome/test/base/chrome_render_view_test.cc b/chrome/test/base/chrome_render_view_test.cc
index 665590a..9838dee 100644
--- a/chrome/test/base/chrome_render_view_test.cc
+++ b/chrome/test/base/chrome_render_view_test.cc
@@ -56,7 +56,9 @@
   content::SetRendererClientForTesting(&chrome_content_renderer_client_);
   extension_dispatcher_ = new extensions::Dispatcher();
   chrome_content_renderer_client_.SetExtensionDispatcher(extension_dispatcher_);
+#if defined(ENABLE_SPELLCHECK)
   chrome_content_renderer_client_.SetSpellcheck(new SpellCheck());
+#endif
 
   content::RenderViewTest::SetUp();
 
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 46cd611..9ac7c38 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -53,9 +53,7 @@
 #include "net/test/spawned_test_server/spawned_test_server.h"
 #include "ui/compositor/compositor_switches.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/audio/audio_handler.h"
-#elif defined(OS_MACOSX)
+#if defined(OS_MACOSX)
 #include "base/mac/scoped_nsautorelease_pool.h"
 #endif
 
@@ -72,6 +70,10 @@
 #include "chrome/browser/captive_portal/captive_portal_service.h"
 #endif
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+#include "chrome/browser/storage_monitor/test_storage_monitor.h"
+#endif
+
 namespace {
 
 // Passed as value of kTestType.
@@ -427,6 +429,12 @@
         browser_->tab_strip_model()->GetActiveWebContents());
   }
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+  // Do not use the real StorageMonitor for tests, which introduces another
+  // source of variability and potential slowness.
+  ASSERT_TRUE(chrome::test::TestStorageMonitor::CreateForBrowserTests());
+#endif
+
   // Pump any pending events that were created as a result of creating a
   // browser.
   content::RunAllPendingInMessageLoop();
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 7611a93..3d9511d 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -53,6 +53,7 @@
 #include "chrome/browser/webdata/web_data_service_factory.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/bookmark_load_observer.h"
 #include "chrome/test/base/history_index_restore_observer.h"
@@ -545,6 +546,10 @@
   return this;
 }
 
+bool TestingProfile::IsManaged() {
+  return GetPrefs()->GetBoolean(prefs::kProfileIsManaged);
+}
+
 ExtensionService* TestingProfile::GetExtensionService() {
   return extensions::ExtensionSystem::Get(this)->extension_service();
 }
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 91a2a22..8e63e5a 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -194,6 +194,7 @@
   virtual void DestroyOffTheRecordProfile() OVERRIDE {}
   virtual bool HasOffTheRecordProfile() OVERRIDE;
   virtual Profile* GetOriginalProfile() OVERRIDE;
+  virtual bool IsManaged() OVERRIDE;
   virtual ExtensionService* GetExtensionService() OVERRIDE;
   void SetExtensionSpecialStoragePolicy(
       ExtensionSpecialStoragePolicy* extension_special_storage_policy);
diff --git a/chrome/test/base/testing_profile_manager.cc b/chrome/test/base/testing_profile_manager.cc
index 44bb0b5..b0c6604 100644
--- a/chrome/test/base/testing_profile_manager.cc
+++ b/chrome/test/base/testing_profile_manager.cc
@@ -48,7 +48,8 @@
     const std::string& profile_name,
     scoped_ptr<PrefServiceSyncable> prefs,
     const string16& user_name,
-    int avatar_id) {
+    int avatar_id,
+    bool is_managed) {
   DCHECK(called_set_up_);
 
   // Create a path for the profile based on the name.
@@ -66,8 +67,11 @@
   // Update the user metadata.
   ProfileInfoCache& cache = profile_manager_->GetProfileInfoCache();
   size_t index = cache.GetIndexOfProfileWithPath(profile_path);
-  cache.SetNameOfProfileAtIndex(index, user_name);
   cache.SetAvatarIconOfProfileAtIndex(index, avatar_id);
+  cache.SetProfileIsManagedAtIndex(index, is_managed);
+  // SetNameOfProfileAtIndex may reshuffle the list of profiles, so we do it
+  // last.
+  cache.SetNameOfProfileAtIndex(index, user_name);
 
   testing_profiles_.insert(std::make_pair(profile_name, profile));
 
@@ -78,7 +82,7 @@
     const std::string& name) {
   DCHECK(called_set_up_);
   return CreateTestingProfile(name, scoped_ptr<PrefServiceSyncable>(),
-                              UTF8ToUTF16(name), 0);
+                              UTF8ToUTF16(name), 0, false);
 }
 
 void TestingProfileManager::DeleteTestingProfile(const std::string& name) {
diff --git a/chrome/test/base/testing_profile_manager.h b/chrome/test/base/testing_profile_manager.h
index 99d7458..0e27dce 100644
--- a/chrome/test/base/testing_profile_manager.h
+++ b/chrome/test/base/testing_profile_manager.h
@@ -43,14 +43,15 @@
   // profile_name, which is a non-user-visible key for the test environment.
   // |prefs| is the PrefService used by the profile. If it is NULL, the profile
   // creates a PrefService on demand.
-  // |user_name| and |avatar_id| are passed along to the ProfileInfoCache and
-  // provide the user-visible profile metadata. This will register the
-  // TestingProfile with the profile subsystem as well. The subsystem owns the
-  // Profile and returns a weak pointer.
+  // |user_name|, |avatar_id| and |is_managed| are passed along to the
+  // ProfileInfoCache and provide the user-visible profile metadata. This will
+  // register the TestingProfile with the profile subsystem as well. The
+  // subsystem owns the Profile and returns a weak pointer.
   TestingProfile* CreateTestingProfile(const std::string& profile_name,
                                        scoped_ptr<PrefServiceSyncable> prefs,
                                        const string16& user_name,
-                                       int avatar_id);
+                                       int avatar_id,
+                                       bool is_managed);
 
   // Small helper for creating testing profiles. Just forwards to above.
   TestingProfile* CreateTestingProfile(const std::string& name);
diff --git a/chrome/test/chromedriver/README.txt b/chrome/test/chromedriver/README.txt
index 03154fe..ef3d959 100644
--- a/chrome/test/chromedriver/README.txt
+++ b/chrome/test/chromedriver/README.txt
@@ -37,18 +37,17 @@
 communicates with the WebDriver client via the WebDriver wire protocol, which
 is essentially synchronous JSON commands over HTTP. WebDriver clients are
 available in many languages, and many are available from the open source
-selenium/webdriver project: http://code.google.com/p/selenium.
+selenium/webdriver project: http://code.google.com/p/selenium. ChromeDriver
+uses the webserver from net/server.
 
-ChromeDriver has several threads. The webserver code, third_party/mongoose,
-spawns a thread for the server socket and a certain amount of request handling
-threads. When a request is received, the command is processed on the message
-loop of the main thread, also called the command thread. Commands may be handled
-asynchronously on the command thread, but the request handler threads
-will block waiting for the response. One of the commands allows the user to
-create a session, which includes spawning a dedicated session thread. Session
-commands will be dispatched to the session thread and handled synchronously
-there. Lastly, there is an IO/net thread on which the net/ code operates.
-This is used to keep reading incoming data from Chrome in the background.
+ChromeDriver has a main thread, called the command thread, an IO thread,
+and a thread per session. The webserver receives a request on the IO thread,
+which is sent to a handler on the command thread. The handler executes the
+appropriate command function, which completes asynchronously. The create
+session command may create a new thread for subsequent session-related commands,
+which will execute on the dedicated session thread synchronously. When a
+command is finished, it will invoke a callback, which will eventually make its
+way back to the IO thread as a HTTP response for the server to send.
 
 =====Code structure=====
 1) chrome/test/chromedriver
@@ -77,9 +76,6 @@
 8) chrome/test/chromedriver/third_party
 Third party libraries used by chromedriver.
 
-9) third_party/mongoose
-The webserver for chromedriver.
-
 =====Testing=====
 There are 4 test suites for verifying ChromeDriver's correctness:
 
diff --git a/chrome/test/chromedriver/js/call_function.js b/chrome/test/chromedriver/js/call_function.js
index d9aa957..f9b1d0a 100644
--- a/chrome/test/chromedriver/js/call_function.js
+++ b/chrome/test/chromedriver/js/call_function.js
@@ -51,7 +51,7 @@
       if (item == this.cache_[i])
         return i;
     }
-    var id = this.idPrefix_  + ':' + this.nextId_;
+    var id = this.idPrefix_  + '-' + this.nextId_;
     this.cache_[id] = item;
     this.nextId_++;
     return id;
@@ -98,8 +98,7 @@
  */
 function getPageCache(opt_doc) {
   var doc = opt_doc || document;
-  // We use the same key as selenium's javascript/atoms/inject.js.
-  var key = '$wdc_';
+  var key = '$cdc_asdjflasutopfhvcZLmcfl_';
   if (!(key in doc))
     doc[key] = new Cache();
   return doc[key];
diff --git a/chrome/test/chromedriver/js/focus.js b/chrome/test/chromedriver/js/focus.js
index a25ae49..2945a34 100644
--- a/chrome/test/chromedriver/js/focus.js
+++ b/chrome/test/chromedriver/js/focus.js
@@ -21,7 +21,8 @@
   //     keys to work. Not sure why
   //   - You cannot focus a descendant of a content editable node
   //   - V8 throws a TypeError when calling setSelectionRange for a non-text
-  //     input, which still have setSelectionRange defined.
+  //     input, which still have setSelectionRange defined. For chrome 29+, V8
+  //     throws a DOMException with code InvalidStateError.
   var doc = element.ownerDocument || element;
   var prevActiveElement = doc.activeElement;
   if (element != prevActiveElement && prevActiveElement)
@@ -32,7 +33,8 @@
     try {
       element.setSelectionRange(element.value.length, element.value.length);
     } catch (error) {
-      if (!(error instanceof TypeError))
+      if (!(error instanceof TypeError) && !(error instanceof DOMException &&
+          error.code == DOMException.INVALID_STATE_ERR))
         throw error;
     }
   }
diff --git a/chrome/test/chromedriver/run_py_tests.py b/chrome/test/chromedriver/run_py_tests.py
index 7cefbc5..35d6c9d 100755
--- a/chrome/test/chromedriver/run_py_tests.py
+++ b/chrome/test/chromedriver/run_py_tests.py
@@ -143,19 +143,14 @@
         chrome_paths.GetTestData())
     if _ANDROID_PACKAGE:
       ChromeDriverTest._adb = android_commands.AndroidCommands()
-      forwarder.Forwarder.KillHost('Debug')
-      ChromeDriverTest._forwarder = forwarder.Forwarder(ChromeDriverTest._adb,
-                                                        'Debug')
       host_port = ChromeDriverTest._http_server._server.server_port
-      ChromeDriverTest._forwarder.Run(
-          [(host_port, host_port)], valgrind_tools.BaseTool())
+      forwarder.Forwarder.Map(
+          [(host_port, host_port)], ChromeDriverTest._adb)
 
   @staticmethod
   def GlobalTearDown():
     if _ANDROID_PACKAGE:
-      forwarder.Forwarder.KillDevice(ChromeDriverTest._adb,
-                                     valgrind_tools.BaseTool())
-      ChromeDriverTest._forwarder.Close()
+      forwarder.Forwarder.UnmapAllDevicePorts(ChromeDriverTest._adb)
     ChromeDriverTest._http_server.Shutdown()
 
   @staticmethod
diff --git a/chrome/test/chromedriver/server/chromedriver_server.cc b/chrome/test/chromedriver/server/chromedriver_server.cc
index 1da71e8..c036814 100644
--- a/chrome/test/chromedriver/server/chromedriver_server.cc
+++ b/chrome/test/chromedriver/server/chromedriver_server.cc
@@ -8,8 +8,10 @@
 
 #include "base/at_exit.h"
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
@@ -19,12 +21,16 @@
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
+#include "base/threading/thread_local.h"
 #include "chrome/test/chromedriver/chrome/log.h"
 #include "chrome/test/chromedriver/chrome/version.h"
 #include "chrome/test/chromedriver/server/http_handler.h"
-#include "chrome/test/chromedriver/server/http_response.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/server/http_server.h"
 #include "net/server/http_server_request_info.h"
-#include "third_party/mongoose/mongoose.h"
+#include "net/server/http_server_response_info.h"
+#include "net/socket/tcp_listen_socket.h"
 
 #if defined(OS_POSIX)
 #include <fcntl.h>
@@ -33,105 +39,136 @@
 
 namespace {
 
-void SendHttpResponse(bool shutdown,
-                      const HttpResponseSenderFunc& send_response_func,
-                      scoped_ptr<HttpResponse> response) {
-  send_response_func.Run(response.Pass());
-  if (shutdown)
-    base::MessageLoop::current()->QuitWhenIdle();
-}
-
-void HandleHttpRequest(HttpHandler* handler,
-                       const net::HttpServerRequestInfo& request,
-                       const HttpResponseSenderFunc& send_response_func) {
-  handler->Handle(request,
-                  base::Bind(&SendHttpResponse,
-                             handler->ShouldShutdown(request),
-                             send_response_func));
-}
-
-void ReadRequestBody(const struct mg_request_info* const request_info,
-                     struct mg_connection* const connection,
-                     std::string* request_body) {
-  int content_length = 0;
-  // 64 maximum header count hard-coded in mongoose.h
-  for (int header_index = 0; header_index < 64; ++header_index) {
-    if (request_info->http_headers[header_index].name == NULL) {
-      break;
-    }
-    if (LowerCaseEqualsASCII(request_info->http_headers[header_index].name,
-                             "content-length")) {
-      base::StringToInt(
-          request_info->http_headers[header_index].value, &content_length);
-      break;
-    }
-  }
-  if (content_length > 0) {
-    request_body->resize(content_length);
-    int bytes_read = 0;
-    while (bytes_read < content_length) {
-      bytes_read += mg_read(connection,
-                            &(*request_body)[bytes_read],
-                            content_length - bytes_read);
-    }
-  }
-}
-
 typedef base::Callback<
     void(const net::HttpServerRequestInfo&, const HttpResponseSenderFunc&)>
     HttpRequestHandlerFunc;
 
-struct MongooseUserData {
-  base::SingleThreadTaskRunner* cmd_task_runner;
-  HttpRequestHandlerFunc* handler_func;
+class HttpServer : public net::HttpServer::Delegate {
+ public:
+  explicit HttpServer(const HttpRequestHandlerFunc& handle_request_func)
+      : handle_request_func_(handle_request_func),
+        weak_factory_(this) {}
+
+  virtual ~HttpServer() {}
+
+  bool Start(int port) {
+    server_ = new net::HttpServer(
+        net::TCPListenSocketFactory("0.0.0.0", port), this);
+    net::IPEndPoint address;
+    return server_->GetLocalAddress(&address) == net::OK;
+  }
+
+  // Overridden from net::HttpServer::Delegate:
+  virtual void OnHttpRequest(int connection_id,
+                             const net::HttpServerRequestInfo& info) OVERRIDE {
+    handle_request_func_.Run(
+        info,
+        base::Bind(&HttpServer::OnResponse,
+                   weak_factory_.GetWeakPtr(),
+                   connection_id));
+  }
+  virtual void OnWebSocketRequest(
+      int connection_id,
+      const net::HttpServerRequestInfo& info) OVERRIDE {}
+  virtual void OnWebSocketMessage(int connection_id,
+                                  const std::string& data) OVERRIDE {}
+  virtual void OnClose(int connection_id) OVERRIDE {}
+
+ private:
+  void OnResponse(int connection_id,
+                  scoped_ptr<net::HttpServerResponseInfo> response) {
+    // Don't support keep-alive, since there's no way to detect if the
+    // client is HTTP/1.0. In such cases, the client may hang waiting for
+    // the connection to close (e.g., python 2.7 urllib).
+    response->AddHeader("Connection", "close");
+    server_->SendResponse(connection_id, *response);
+    server_->Close(connection_id);
+  }
+
+  HttpRequestHandlerFunc handle_request_func_;
+  scoped_refptr<net::HttpServer> server_;
+  base::WeakPtrFactory<HttpServer> weak_factory_;  // Should be last.
 };
 
-void DoneProcessing(base::WaitableEvent* event,
-                    scoped_ptr<HttpResponse>* response_to_set,
-                    scoped_ptr<HttpResponse> response) {
-  *response_to_set = response.Pass();
-  event->Signal();
+void SendResponseOnCmdThread(
+    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
+    const HttpResponseSenderFunc& send_response_on_io_func,
+    scoped_ptr<net::HttpServerResponseInfo> response) {
+  io_task_runner->PostTask(
+      FROM_HERE, base::Bind(send_response_on_io_func, base::Passed(&response)));
 }
 
-void* ProcessHttpRequest(mg_event event_raised,
-                         struct mg_connection* connection,
-                         const struct mg_request_info* request_info) {
-  if (event_raised != MG_NEW_REQUEST)
-    return reinterpret_cast<void*>(false);
-  MongooseUserData* user_data =
-      reinterpret_cast<MongooseUserData*>(request_info->user_data);
+void HandleRequestOnCmdThread(
+    HttpHandler* handler,
+    const net::HttpServerRequestInfo& request,
+    const HttpResponseSenderFunc& send_response_func) {
+  handler->Handle(request, send_response_func);
+}
 
-  net::HttpServerRequestInfo request;
-  request.method = request_info->request_method;
-  request.path = request_info->uri;
-  ReadRequestBody(request_info, connection, &request.data);
+void HandleRequestOnIOThread(
+    const scoped_refptr<base::SingleThreadTaskRunner>& cmd_task_runner,
+    const HttpRequestHandlerFunc& handle_request_on_cmd_func,
+    const net::HttpServerRequestInfo& request,
+    const HttpResponseSenderFunc& send_response_func) {
+  cmd_task_runner->PostTask(
+      FROM_HERE,
+      base::Bind(handle_request_on_cmd_func,
+                 request,
+                 base::Bind(&SendResponseOnCmdThread,
+                            base::MessageLoopProxy::current(),
+                            send_response_func)));
+}
 
-  base::WaitableEvent event(false, false);
-  scoped_ptr<HttpResponse> response;
-  user_data->cmd_task_runner
+base::LazyInstance<base::ThreadLocalPointer<HttpServer> >
+    lazy_tls_server = LAZY_INSTANCE_INITIALIZER;
+
+void StopServerOnIOThread() {
+  // Note, |server| may be NULL.
+  HttpServer* server = lazy_tls_server.Pointer()->Get();
+  lazy_tls_server.Pointer()->Set(NULL);
+  delete server;
+}
+
+void StartServerOnIOThread(int port,
+                           const HttpRequestHandlerFunc& handle_request_func) {
+  scoped_ptr<HttpServer> temp_server(new HttpServer(handle_request_func));
+  if (!temp_server->Start(port)) {
+    printf("Port not available. Exiting...\n");
+    exit(1);
+  }
+  lazy_tls_server.Pointer()->Set(temp_server.release());
+}
+
+void RunServer(Log::Level log_level, int port, const std::string& url_base) {
+  base::Thread io_thread("ChromeDriver IO");
+  CHECK(io_thread.StartWithOptions(
+      base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
+
+  base::MessageLoop cmd_loop;
+  base::RunLoop cmd_run_loop;
+  Logger log(log_level);
+  HttpHandler handler(cmd_run_loop.QuitClosure(),
+                      io_thread.message_loop_proxy(),
+                      &log,
+                      url_base);
+  HttpRequestHandlerFunc handle_request_func =
+      base::Bind(&HandleRequestOnCmdThread, &handler);
+
+  io_thread.message_loop()
       ->PostTask(FROM_HERE,
-                 base::Bind(*user_data->handler_func,
-                            request,
-                            base::Bind(&DoneProcessing, &event, &response)));
-  event.Wait();
-
-  // Don't allow HTTP keep alive.
-  response->AddHeader("connection", "close");
-  std::string data;
-  response->GetData(&data);
-  mg_write(connection, data.data(), data.length());
-  return reinterpret_cast<void*>(true);
-}
-
-void MakeMongooseOptions(const std::string& port,
-                         int http_threads,
-                         std::vector<std::string>* out_options) {
-  out_options->push_back("listening_ports");
-  out_options->push_back(port);
-  out_options->push_back("enable_keep_alive");
-  out_options->push_back("no");
-  out_options->push_back("num_threads");
-  out_options->push_back(base::IntToString(http_threads));
+                 base::Bind(&StartServerOnIOThread,
+                            port,
+                            base::Bind(&HandleRequestOnIOThread,
+                                       cmd_loop.message_loop_proxy(),
+                                       handle_request_func)));
+  // Run the command loop. This loop is quit after the response for a shutdown
+  // request is posted to the IO loop. After the command loop quits, a task
+  // is posted to the IO loop to stop the server. Lastly, the IO thread is
+  // destroyed, which waits until all pending tasks have been completed.
+  // This assumes the response is sent synchronously as part of the IO task.
+  cmd_run_loop.Run();
+  io_thread.message_loop()
+      ->PostTask(FROM_HERE, base::Bind(&StopServerOnIOThread));
 }
 
 }  // namespace
@@ -143,9 +180,8 @@
   CommandLine* cmd_line = CommandLine::ForCurrentProcess();
 
   // Parse command line flags.
-  std::string port = "9515";
+  int port = 9515;
   std::string url_base;
-  int http_threads = 4;
   base::FilePath log_path;
   Log::Level log_level = Log::kError;
   if (cmd_line->HasSwitch("h") || cmd_line->HasSwitch("help")) {
@@ -157,7 +193,6 @@
         "verbose", "log verbosely",
         "silent", "log nothing",
         "url-base", "base URL path prefix for commands, e.g. wd/url",
-        "http-threads=THREAD_COUNT", "number of HTTP threads to spawn",
     };
     for (size_t i = 0; i < arraysize(kOptionAndDescriptions) - 1; i += 2) {
       options += base::StringPrintf(
@@ -167,21 +202,18 @@
     printf("Usage: %s [OPTIONS]\n\nOptions\n%s", argv[0], options.c_str());
     return 0;
   }
-  if (cmd_line->HasSwitch("port"))
-    port = cmd_line->GetSwitchValueASCII("port");
+  if (cmd_line->HasSwitch("port")) {
+    if (!base::StringToInt(cmd_line->GetSwitchValueASCII("port"), &port)) {
+      printf("Invalid port. Exiting...\n");
+      return 1;
+    }
+  }
   if (cmd_line->HasSwitch("url-base"))
     url_base = cmd_line->GetSwitchValueASCII("url-base");
   if (url_base.empty() || url_base[0] != '/')
     url_base = "/" + url_base;
   if (url_base[url_base.length() - 1] != '/')
     url_base = url_base + "/";
-  if (cmd_line->HasSwitch("http-threads")) {
-    if (!base::StringToInt(cmd_line->GetSwitchValueASCII("http-threads"),
-                           &http_threads)) {
-      printf("'http-threads' option must be an integer\n");
-      return 1;
-    }
-  }
   if (cmd_line->HasSwitch("log-path")) {
     log_level = Log::kLog;
     log_path = cmd_line->GetSwitchValuePath("log-path");
@@ -195,8 +227,20 @@
       return 1;
     }
   }
-  if (cmd_line->HasSwitch("verbose"))
+  if (cmd_line->HasSwitch("verbose")) {
     log_level = Log::kDebug;
+  } else {
+#if defined(OS_POSIX)
+    // Close stderr on exec, so that Chrome log spew doesn't confuse users.
+    fcntl(STDERR_FILENO, F_SETFD, FD_CLOEXEC);
+#endif
+  }
+  if (!cmd_line->HasSwitch("silent")) {
+    printf(
+        "Starting ChromeDriver (v%s) on port %d\n", kChromeDriverVersion, port);
+    fflush(stdout);
+  }
+
 
   logging::LoggingSettings settings;
   settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
@@ -211,51 +255,6 @@
   if (!cmd_line->HasSwitch("verbose"))
     logging::SetMinLogLevel(logging::LOG_FATAL);
 
-  base::Thread io_thread("ChromeDriver IO");
-  CHECK(io_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
-
-  scoped_ptr<Log> log(new Logger(log_level));
-  HttpHandler handler(io_thread.message_loop_proxy(), log.get(), url_base);
-  base::MessageLoop cmd_loop;
-  HttpRequestHandlerFunc handler_func =
-      base::Bind(&HandleHttpRequest, &handler);
-  MongooseUserData user_data = { cmd_loop.message_loop_proxy(), &handler_func };
-
-  std::vector<std::string> args;
-  MakeMongooseOptions(port, http_threads, &args);
-  scoped_ptr<const char*[]> options(new const char*[args.size() + 1]);
-  for (size_t i = 0; i < args.size(); ++i) {
-    options[i] = args[i].c_str();
-  }
-  options[args.size()] = NULL;
-
-  struct mg_context* ctx = mg_start(&ProcessHttpRequest,
-                                    &user_data,
-                                    options.get());
-  if (ctx == NULL) {
-    printf("Port not available. Exiting...\n");
-    return 1;
-  }
-
-  if (!cmd_line->HasSwitch("silent")) {
-    printf("Started ChromeDriver (v%s) on port %s\n",
-           kChromeDriverVersion,
-           port.c_str());
-    fflush(stdout);
-  }
-
-#if defined(OS_POSIX)
-  if (!cmd_line->HasSwitch("verbose")) {
-    // Close stderr on exec, so that Chrome log spew doesn't confuse users.
-    fcntl(STDERR_FILENO, F_SETFD, FD_CLOEXEC);
-  }
-#endif
-
-  base::RunLoop cmd_run_loop;
-  cmd_run_loop.Run();
-  // Don't run destructors for objects passed via MongooseUserData,
-  // because ProcessHttpRequest may be accessing them.
-  // TODO(kkania): Fix when switching to net::HttpServer.
-  exit(0);
+  RunServer(log_level, port, url_base);
+  return 0;
 }
diff --git a/chrome/test/chromedriver/server/http_handler.cc b/chrome/test/chromedriver/server/http_handler.cc
index 4699b93..e619ce7 100644
--- a/chrome/test/chromedriver/server/http_handler.cc
+++ b/chrome/test/chromedriver/server/http_handler.cc
@@ -24,11 +24,11 @@
 #include "chrome/test/chromedriver/chrome/status.h"
 #include "chrome/test/chromedriver/chrome/version.h"
 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
-#include "chrome/test/chromedriver/server/http_response.h"
 #include "chrome/test/chromedriver/session.h"
 #include "chrome/test/chromedriver/session_thread_map.h"
 #include "chrome/test/chromedriver/util.h"
 #include "net/server/http_server_request_info.h"
+#include "net/server/http_server_response_info.h"
 
 #if defined(OS_MACOSX)
 #include "base/mac/scoped_nsautorelease_pool.h"
@@ -64,10 +64,12 @@
       weak_ptr_factory_(this) {}
 
 HttpHandler::HttpHandler(
+    const base::Closure& quit_func,
     const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     Log* log,
     const std::string& url_base)
-    : log_(log),
+    : quit_func_(quit_func),
+      log_(log),
       url_base_(url_base),
       received_shutdown_(false),
       weak_ptr_factory_(this) {
@@ -448,14 +450,12 @@
 
   if (received_shutdown_)
     return;
-  if (ShouldShutdown(request))
-    received_shutdown_ = true;
 
   std::string path = request.path;
   if (!StartsWithASCII(path, url_base_, true)) {
-    scoped_ptr<HttpResponse> response(
-        new HttpResponse(HttpResponse::kBadRequest));
-    response->set_body("unhandled request");
+    scoped_ptr<net::HttpServerResponseInfo> response(
+        new net::HttpServerResponseInfo(net::HTTP_BAD_REQUEST));
+    response->SetBody("unhandled request", "text/plain");
     send_response_func.Run(response.Pass());
     return;
   }
@@ -463,10 +463,9 @@
   path.erase(0, url_base_.length());
 
   HandleCommand(request, path, send_response_func);
-}
 
-bool HttpHandler::ShouldShutdown(const net::HttpServerRequestInfo& request) {
-  return request.path == url_base_ + kShutdownPath;
+  if (path == kShutdownPath)
+    received_shutdown_ = true;
 }
 
 Command HttpHandler::WrapToCommand(
@@ -502,9 +501,9 @@
   CommandMap::const_iterator iter = command_map_->begin();
   while (true) {
     if (iter == command_map_->end()) {
-      scoped_ptr<HttpResponse> response(
-          new HttpResponse(HttpResponse::kNotFound));
-      response->set_body("unknown command: " + trimmed_path);
+      scoped_ptr<net::HttpServerResponseInfo> response(
+          new net::HttpServerResponseInfo(net::HTTP_NOT_FOUND));
+      response->SetBody("unknown command: " + trimmed_path, "text/plain");
       send_response_func.Run(response.Pass());
       return;
     }
@@ -519,9 +518,9 @@
     base::DictionaryValue* body_params;
     scoped_ptr<base::Value> parsed_body(base::JSONReader::Read(request.data));
     if (!parsed_body || !parsed_body->GetAsDictionary(&body_params)) {
-      scoped_ptr<HttpResponse> response(
-          new HttpResponse(HttpResponse::kBadRequest));
-      response->set_body("missing command parameters");
+      scoped_ptr<net::HttpServerResponseInfo> response(
+          new net::HttpServerResponseInfo(net::HTTP_BAD_REQUEST));
+      response->SetBody("missing command parameters", "test/plain");
       send_response_func.Run(response.Pass());
       return;
     }
@@ -543,24 +542,26 @@
     scoped_ptr<base::Value> value,
     const std::string& session_id) {
   CHECK(thread_checker_.CalledOnValidThread());
-  scoped_ptr<HttpResponse> response =
+  scoped_ptr<net::HttpServerResponseInfo> response =
       PrepareResponseHelper(trimmed_path, status, value.Pass(), session_id);
   log_->AddEntry(Log::kLog,
                  base::StringPrintf("sending response: %d %s",
-                                    response->status(),
+                                    response->status_code(),
                                     response->body().c_str()));
   send_response_func.Run(response.Pass());
+  if (trimmed_path == kShutdownPath)
+    quit_func_.Run();
 }
 
-scoped_ptr<HttpResponse> HttpHandler::PrepareResponseHelper(
+scoped_ptr<net::HttpServerResponseInfo> HttpHandler::PrepareResponseHelper(
     const std::string& trimmed_path,
     const Status& status,
     scoped_ptr<base::Value> value,
     const std::string& session_id) {
   if (status.code() == kUnknownCommand) {
-    scoped_ptr<HttpResponse> response(
-        new HttpResponse(HttpResponse::kNotImplemented));
-    response->set_body("unimplemented command: " + trimmed_path);
+    scoped_ptr<net::HttpServerResponseInfo> response(
+        new net::HttpServerResponseInfo(net::HTTP_NOT_IMPLEMENTED));
+    response->SetBody("unimplemented command: " + trimmed_path, "text/plain");
     return response.Pass();
   }
 
@@ -568,8 +569,8 @@
     // Creating a session involves a HTTP request to /session, which is
     // supposed to redirect to /session/:sessionId, which returns the
     // session info.
-    scoped_ptr<HttpResponse> response(
-        new HttpResponse(HttpResponse::kSeeOther));
+    scoped_ptr<net::HttpServerResponseInfo> response(
+        new net::HttpServerResponseInfo(net::HTTP_SEE_OTHER));
     response->AddHeader("Location", url_base_ + "session/" + session_id);
     return response.Pass();
   } else if (status.IsError()) {
@@ -595,9 +596,9 @@
   base::JSONWriter::WriteWithOptions(
       &body_params, base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION,
       &body);
-  scoped_ptr<HttpResponse> response(new HttpResponse(HttpResponse::kOk));
-  response->SetMimeType("application/json; charset=utf-8");
-  response->set_body(body);
+  scoped_ptr<net::HttpServerResponseInfo> response(
+      new net::HttpServerResponseInfo(net::HTTP_OK));
+  response->SetBody(body, "application/json; charset=utf-8");
   return response.Pass();
 }
 
diff --git a/chrome/test/chromedriver/server/http_handler.h b/chrome/test/chromedriver/server/http_handler.h
index 3ad42a1..7332554 100644
--- a/chrome/test/chromedriver/server/http_handler.h
+++ b/chrome/test/chromedriver/server/http_handler.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
@@ -29,12 +30,12 @@
 
 namespace net {
 class HttpServerRequestInfo;
+class HttpServerResponseInfo;
 }
 
 class Adb;
 class DeviceManager;
 class Log;
-class HttpResponse;
 class URLRequestContextGetter;
 
 enum HttpMethod {
@@ -54,19 +55,20 @@
   Command command;
 };
 
-typedef base::Callback<void(scoped_ptr<HttpResponse>)> HttpResponseSenderFunc;
+typedef base::Callback<void(scoped_ptr<net::HttpServerResponseInfo>)>
+    HttpResponseSenderFunc;
 
 class HttpHandler {
  public:
   HttpHandler(Log* log, const std::string& url_base);
-  HttpHandler(const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+  HttpHandler(const base::Closure& quit_func,
+              const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
               Log* log,
               const std::string& url_base);
   ~HttpHandler();
 
   void Handle(const net::HttpServerRequestInfo& request,
               const HttpResponseSenderFunc& send_response_func);
-  bool ShouldShutdown(const net::HttpServerRequestInfo& request);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(HttpHandlerTest, HandleUnknownCommand);
@@ -87,13 +89,14 @@
                        const Status& status,
                        scoped_ptr<base::Value> value,
                        const std::string& session_id);
-  scoped_ptr<HttpResponse> PrepareResponseHelper(
+  scoped_ptr<net::HttpServerResponseInfo> PrepareResponseHelper(
       const std::string& trimmed_path,
       const Status& status,
       scoped_ptr<base::Value> value,
       const std::string& session_id);
 
   base::ThreadChecker thread_checker_;
+  base::Closure quit_func_;
   Log* log_;
   std::string url_base_;
   bool received_shutdown_;
diff --git a/chrome/test/chromedriver/server/http_handler_unittest.cc b/chrome/test/chromedriver/server/http_handler_unittest.cc
index 3fc6cd8..a9f4e2d 100644
--- a/chrome/test/chromedriver/server/http_handler_unittest.cc
+++ b/chrome/test/chromedriver/server/http_handler_unittest.cc
@@ -12,8 +12,9 @@
 #include "chrome/test/chromedriver/chrome/status.h"
 #include "chrome/test/chromedriver/command.h"
 #include "chrome/test/chromedriver/server/http_handler.h"
-#include "chrome/test/chromedriver/server/http_response.h"
+#include "net/http/http_status_code.h"
 #include "net/server/http_server_request_info.h"
+#include "net/server/http_server_response_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -28,8 +29,8 @@
                "session_id");
 }
 
-void OnResponse(HttpResponse* response_to_set,
-                scoped_ptr<HttpResponse> response) {
+void OnResponse(net::HttpServerResponseInfo* response_to_set,
+                scoped_ptr<net::HttpServerResponseInfo> response) {
   *response_to_set = *response;
 }
 
@@ -42,9 +43,9 @@
   request.method = "get";
   request.path = "base/path";
   request.data = "body";
-  HttpResponse response;
+  net::HttpServerResponseInfo response;
   handler.Handle(request, base::Bind(&OnResponse, &response));
-  ASSERT_EQ(HttpResponse::kBadRequest, response.status());
+  ASSERT_EQ(net::HTTP_BAD_REQUEST, response.status_code());
 }
 
 TEST(HttpHandlerTest, HandleUnknownCommand) {
@@ -53,9 +54,9 @@
   net::HttpServerRequestInfo request;
   request.method = "get";
   request.path = "/path";
-  HttpResponse response;
+  net::HttpServerResponseInfo response;
   handler.Handle(request, base::Bind(&OnResponse, &response));
-  ASSERT_EQ(HttpResponse::kNotFound, response.status());
+  ASSERT_EQ(net::HTTP_NOT_FOUND, response.status_code());
 }
 
 TEST(HttpHandlerTest, HandleNewSession) {
@@ -68,13 +69,12 @@
   net::HttpServerRequestInfo request;
   request.method = "post";
   request.path = "/base/session";
-  HttpResponse response;
+  net::HttpServerResponseInfo response;
   handler.Handle(request, base::Bind(&OnResponse, &response));
-  ASSERT_EQ(HttpResponse::kSeeOther, response.status());
-  std::string location;
-  ASSERT_TRUE(response.GetHeader("Location", &location));
-  std::string prefix = "/base/session/";
-  ASSERT_EQ(prefix, location.substr(0, prefix.length()));
+  ASSERT_EQ(net::HTTP_SEE_OTHER, response.status_code());
+  ASSERT_NE(std::string::npos,
+            response.Serialize().find("Location:/base/session/"))
+      << response.Serialize();
 }
 
 TEST(HttpHandlerTest, HandleInvalidPost) {
@@ -86,9 +86,9 @@
   request.method = "post";
   request.path = "/path";
   request.data = "should be a dictionary";
-  HttpResponse response;
+  net::HttpServerResponseInfo response;
   handler.Handle(request, base::Bind(&OnResponse, &response));
-  ASSERT_EQ(HttpResponse::kBadRequest, response.status());
+  ASSERT_EQ(net::HTTP_BAD_REQUEST, response.status_code());
 }
 
 TEST(HttpHandlerTest, HandleUnimplementedCommand) {
@@ -100,9 +100,9 @@
   net::HttpServerRequestInfo request;
   request.method = "post";
   request.path = "/path";
-  HttpResponse response;
+  net::HttpServerResponseInfo response;
   handler.Handle(request, base::Bind(&OnResponse, &response));
-  ASSERT_EQ(HttpResponse::kNotImplemented, response.status());
+  ASSERT_EQ(net::HTTP_NOT_IMPLEMENTED, response.status_code());
 }
 
 TEST(HttpHandlerTest, HandleCommand) {
@@ -113,11 +113,9 @@
   net::HttpServerRequestInfo request;
   request.method = "post";
   request.path = "/path";
-  HttpResponse response;
+  net::HttpServerResponseInfo response;
   handler.Handle(request, base::Bind(&OnResponse, &response));
-  ASSERT_EQ(HttpResponse::kOk, response.status());
-  std::string mime;
-  ASSERT_TRUE(response.GetHeader("Content-Type", &mime));
+  ASSERT_EQ(net::HTTP_OK, response.status_code());
   base::DictionaryValue body;
   body.SetInteger("status", kOk);
   body.SetInteger("value", 1);
diff --git a/chrome/test/chromedriver/server/http_response.cc b/chrome/test/chromedriver/server/http_response.cc
deleted file mode 100644
index 7705b4e..0000000
--- a/chrome/test/chromedriver/server/http_response.cc
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright (c) 2013 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.
-
-#include "chrome/test/chromedriver/server/http_response.h"
-
-#include "base/format_macros.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-
-const int HttpResponse::kOk = 200;
-const int HttpResponse::kNoContent = 204;
-const int HttpResponse::kSeeOther = 303;
-const int HttpResponse::kNotModified = 304;
-const int HttpResponse::kBadRequest = 400;
-const int HttpResponse::kForbidden = 403;
-const int HttpResponse::kNotFound = 404;
-const int HttpResponse::kMethodNotAllowed = 405;
-const int HttpResponse::kInternalServerError = 500;
-const int HttpResponse::kNotImplemented = 501;
-
-namespace {
-
-const char* kContentLengthHeader = "content-length";
-
-}  // namespace
-
-HttpResponse::HttpResponse()
-    : status_(kOk) {
-}
-
-HttpResponse::HttpResponse(int status)
-    : status_(status) {
-}
-
-HttpResponse::~HttpResponse() {
-}
-
-void HttpResponse::AddHeader(const std::string& name,
-                             const std::string& value) {
-  std::string lower_case_name(StringToLowerASCII(name));
-  HeaderMap::iterator header = headers_.find(lower_case_name);
-  if (header == headers_.end()) {
-    headers_[lower_case_name] = value;
-  } else {
-    header->second.append("," + value);
-  }
-}
-
-bool HttpResponse::GetHeader(const std::string& name,
-                             std::string* value) const {
-  std::string lower_case_name(StringToLowerASCII(name));
-  HeaderMap::const_iterator header = headers_.find(lower_case_name);
-
-  if (header == headers_.end()) {
-    return false;
-  }
-
-  if (value) {
-    *value = header->second;
-  }
-
-  return true;
-}
-
-bool HttpResponse::RemoveHeader(const std::string& name) {
-  std::string lower_case_name(StringToLowerASCII(name));
-  HeaderMap::iterator header = headers_.find(lower_case_name);
-
-  if (header == headers_.end()) {
-    return false;
-  }
-
-  headers_.erase(header);
-  return true;
-}
-
-void HttpResponse::ClearHeaders() {
-  headers_.clear();
-}
-
-void HttpResponse::SetMimeType(const std::string& mime_type) {
-  UpdateHeader("Content-Type", mime_type);
-}
-
-std::string HttpResponse::GetReasonPhrase() const {
-  switch (status_) {
-    case kOk:
-      return "OK";
-    case kNoContent:
-      return "No Content";
-    case kSeeOther:
-      return "See Other";
-    case kNotModified:
-      return "Not Modified";
-    case kBadRequest:
-      return "Bad Request";
-    case kForbidden:
-      return "Forbidden";
-    case kNotFound:
-      return "Not Found";
-    case kMethodNotAllowed:
-      return "Method Not Allowed";
-    case kInternalServerError:
-      return "Internal Server Error";
-    case kNotImplemented:
-      return "Not Implemented";
-    default:
-      return "Unknown";
-  }
-}
-
-void HttpResponse::GetData(std::string* data) const {
-  *data += base::StringPrintf("HTTP/1.1 %d %s\r\n",
-      status_, GetReasonPhrase().c_str());
-
-  typedef HttpResponse::HeaderMap::const_iterator HeaderIter;
-  for (HeaderIter header = headers_.begin(); header != headers_.end();
-       ++header) {
-    *data += header->first + ":" + header->second + "\r\n";
-  }
-  std::string length;
-  if (!GetHeader(kContentLengthHeader, &length)) {
-    *data += base::StringPrintf(
-        "%s:%" PRIuS "\r\n",
-        kContentLengthHeader, body_.length());
-  }
-  *data += "\r\n";
-
-  if (body_.length())
-    *data += body_;
-}
-
-int HttpResponse::status() const {
-  return status_;
-}
-
-void HttpResponse::set_status(int status) {
-  status_ = status;
-}
-
-const std::string& HttpResponse::body() const {
-  return body_;
-}
-
-void HttpResponse::set_body(const std::string& body) {
-  body_ = body;
-}
-
-void HttpResponse::UpdateHeader(const std::string& name,
-                                const std::string& new_value) {
-  RemoveHeader(name);
-  AddHeader(name, new_value);
-}
diff --git a/chrome/test/chromedriver/server/http_response.h b/chrome/test/chromedriver/server/http_response.h
deleted file mode 100644
index 1465734..0000000
--- a/chrome/test/chromedriver/server/http_response.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2013 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.
-
-#ifndef CHROME_TEST_CHROMEDRIVER_SERVER_HTTP_RESPONSE_H_
-#define CHROME_TEST_CHROMEDRIVER_SERVER_HTTP_RESPONSE_H_
-
-#include <map>
-#include <string>
-
-#include "base/basictypes.h"
-
-class HttpResponse {
- public:
-  typedef std::map<std::string, std::string> HeaderMap;
-
-  // The supported HTTP response codes.
-  static const int kOk;
-  static const int kNoContent;
-  static const int kSeeOther;
-  static const int kNotModified;
-  static const int kBadRequest;
-  static const int kForbidden;
-  static const int kNotFound;
-  static const int kMethodNotAllowed;
-  static const int kInternalServerError;
-  static const int kNotImplemented;
-
-  // Creates an HTTP response with a 200 OK status.
-  HttpResponse();
-  explicit HttpResponse(int status);
-  ~HttpResponse();
-
-  // Sets a header in this response. If a header with the same |name| already
-  // exists, the |value| will be appended to the existing values. Since header
-  // names are case insensitive, the header will be stored in lowercase format.
-  void AddHeader(const std::string& name, const std::string& value);
-
-  // Retrieves the value of the specified header. If there is no such header,
-  // the output |value| will not be modified and false will be returned.
-  bool GetHeader(const std::string& name, std::string* value) const;
-
-  // Removes the header with the given |name|. Returns whether there was a
-  // matching header to remove.
-  bool RemoveHeader(const std::string& name);
-
-  // Removes all headers.
-  void ClearHeaders();
-
-  // Convenience function for setting the Content-Type header for this response.
-  void SetMimeType(const std::string& mime_type);
-
-  // Returns the status phrase recommended by RFC 2616 section 6.1.1 for this
-  // response's status code. If the status code is not recognized, the default
-  // "Unknown" status phrase will be used.
-  std::string GetReasonPhrase() const;
-
-  // Appends this response to |data|, abiding by HTTP 1.1.
-  // This will add an appropriate "Content-Length" header if not already set.
-  void GetData(std::string* data) const;
-
-  int status() const;
-  void set_status(int status);
-  const std::string& body() const;
-  void set_body(const std::string& body);
-
- private:
-  void UpdateHeader(const std::string& name, const std::string& new_value);
-
-  int status_;
-  HeaderMap headers_;
-  std::string body_;
-};
-
-#endif  // CHROME_TEST_CHROMEDRIVER_SERVER_HTTP_RESPONSE_H_
diff --git a/chrome/test/chromedriver/server/http_response_unittest.cc b/chrome/test/chromedriver/server/http_response_unittest.cc
deleted file mode 100644
index 07e0c49..0000000
--- a/chrome/test/chromedriver/server/http_response_unittest.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2013 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.
-
-#include "chrome/test/chromedriver/server/http_response.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-void ExpectHeaderValue(const HttpResponse& response, const std::string& name,
-                       const std::string& expected_value) {
-  std::string actual_value;
-  EXPECT_TRUE(response.GetHeader(name, &actual_value));
-  EXPECT_EQ(expected_value, actual_value);
-}
-
-}  // namespace
-
-TEST(HttpResponseTest, AddingHeaders) {
-  HttpResponse response;
-
-  response.AddHeader("FOO", "a");
-  ExpectHeaderValue(response, "foo", "a");
-
-  // Headers should be case insensitive.
-  response.AddHeader("fOo", "b,c");
-  response.AddHeader("FoO", "d");
-  ExpectHeaderValue(response, "foo", "a,b,c,d");
-}
-
-TEST(HttpResponseTest, RemovingHeaders) {
-  HttpResponse response;
-
-  ASSERT_FALSE(response.RemoveHeader("I-Am-Not-There"));
-
-  ASSERT_FALSE(response.GetHeader("foo", NULL));
-  response.AddHeader("foo", "bar");
-  ASSERT_TRUE(response.GetHeader("foo", NULL));
-  ASSERT_TRUE(response.RemoveHeader("foo"));
-  ASSERT_FALSE(response.GetHeader("foo", NULL));
-}
-
-TEST(HttpResponseTest, CanClearAllHeaders) {
-  HttpResponse response;
-  response.AddHeader("food", "cheese");
-  response.AddHeader("color", "red");
-
-  ExpectHeaderValue(response, "food", "cheese");
-  ExpectHeaderValue(response, "color", "red");
-
-  response.ClearHeaders();
-  EXPECT_FALSE(response.GetHeader("food", NULL));
-  EXPECT_FALSE(response.GetHeader("color", NULL));
-}
-
-TEST(HttpResponseTest, CanSetMimeType) {
-  HttpResponse response;
-
-  response.SetMimeType("application/json");
-  ExpectHeaderValue(response, "content-type", "application/json");
-
-  response.SetMimeType("text/html");
-  ExpectHeaderValue(response, "content-type", "text/html");
-}
-
-TEST(HttpResponseTest, GetData) {
-  HttpResponse response;
-  response.set_body("my body");
-  std::string data;
-  response.GetData(&data);
-  const char* expected =
-      "HTTP/1.1 200 OK\r\n"
-      "content-length:7\r\n"
-      "\r\n"
-      "my body";
-  ASSERT_STREQ(expected, data.c_str());
-}
diff --git a/chrome/test/chromedriver/test_environment.py b/chrome/test/chromedriver/test_environment.py
index ebcbd0f..2344ad3 100644
--- a/chrome/test/chromedriver/test_environment.py
+++ b/chrome/test/chromedriver/test_environment.py
@@ -94,19 +94,14 @@
     os.putenv('TEST_HTTP_PORT', str(ANDROID_TEST_HTTP_PORT))
     os.putenv('TEST_HTTPS_PORT', str(ANDROID_TEST_HTTPS_PORT))
     self._adb = android_commands.AndroidCommands()
-    forwarder.Forwarder.KillHost('Debug')
-    self._forwarder = forwarder.Forwarder(self._adb, 'Debug')
-    self._forwarder.Run(
+    forwarder.Forwarder.Map(
         [(ANDROID_TEST_HTTP_PORT, ANDROID_TEST_HTTP_PORT),
          (ANDROID_TEST_HTTPS_PORT, ANDROID_TEST_HTTPS_PORT)],
-        valgrind_tools.BaseTool())
+        self._adb)
 
   # override
   def GlobalTearDown(self):
-    if self._adb is not None:
-      forwarder.Forwarder.KillDevice(self._adb, valgrind_tools.BaseTool())
-    if self._forwarder is not None:
-      self._forwarder.Close()
+    forwarder.Forwarder.UnmapAllDevicePorts(self._adb)
 
   # override
   def GetOS(self):
diff --git a/chrome/test/ext_auto/auto_provider/manifest.json b/chrome/test/ext_auto/auto_provider/manifest.json
index 6a474ff..042bb2f 100644
--- a/chrome/test/ext_auto/auto_provider/manifest.json
+++ b/chrome/test/ext_auto/auto_provider/manifest.json
@@ -76,7 +76,6 @@
      "videoCapture",
      "wallpaperPrivate",
      "webNavigation",
-     "webSocketProxyPrivate",
      "webstorePrivate",
      "webRequest",
      "webRequestBlocking",
diff --git a/chrome/test/functional/PYAUTO_TESTS b/chrome/test/functional/PYAUTO_TESTS
index 8895d00..77c20f5 100644
--- a/chrome/test/functional/PYAUTO_TESTS
+++ b/chrome/test/functional/PYAUTO_TESTS
@@ -223,7 +223,6 @@
       'chromeos_prefs',
       'chromeos_security',
       'chromeos_time',
-      'chromeos_update',
       'chromeos_wifi_sanity',
       'doc_viewing',
       'secure_shell',
diff --git a/chrome/test/functional/chromeos_update.py b/chrome/test/functional/chromeos_update.py
deleted file mode 100755
index 9f8cb24..0000000
--- a/chrome/test/functional/chromeos_update.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2011 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.
-
-import logging
-
-import pyauto_functional
-import pyauto
-
-
-class ChromeosUpdate(pyauto.PyUITest):
-  """Tests for ChromeOS updater and channel changer."""
-
-  def testGetUpdateInfo(self):
-    """Get some status info about the updater and release track."""
-    result = self.GetUpdateInfo()
-    self.assertTrue(result)
-    logging.debug(result)
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/ispy/ispy_core/tools/reverse_port_forwarder.py b/chrome/test/functional/ispy/ispy_core/tools/reverse_port_forwarder.py
new file mode 100644
index 0000000..289c116
--- /dev/null
+++ b/chrome/test/functional/ispy/ispy_core/tools/reverse_port_forwarder.py
@@ -0,0 +1,77 @@
+# Copyright 2013 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.
+
+"""Reverse Port Forwarding class to provide webpagereplay to mobile devices."""
+
+from selenium import webdriver
+import os
+import sys
+# TODO(chris) this path will be subject to change as we put the ispy
+#  file system together.
+sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)),
+                             os.pardir, os.pardir, os.pardir, os.pardir,
+                             os.pardir, os.pardir, 'build', 'android'))
+from pylib import android_commands
+from pylib import forwarder
+from pylib import valgrind_tools
+
+
+class ReversePortForwarder(object):
+  """A class that provides reverse port forwarding functionality."""
+
+  def __init__(self, device_http_port, device_https_port,
+               host_http_port, host_https_port, device_serial):
+    """Makes an instance of ReversePortForwarder.
+
+    Args:
+      device_http_port: Http port on the device to forward data from.
+      device_https_port: Https port on the device to forward data from.
+      host_http_port: Http port on the host to forward data to.
+      host_https_port: Http port on the host to forward data to.
+      device_serial: The serial id of the device to connect with the host.
+    Returns:
+      An instance of ReversePortForwarder.
+    """
+    self._device_http = device_http_port
+    self._device_https = device_https_port
+    self._host_http = host_http_port
+    self._host_https = host_https_port
+    self._device_serial = device_serial
+
+  def __enter__(self):
+    """Starts the reverse port forwarding in the enter/exit decorator."""
+    self.Start()
+    return self
+
+  def __exit__(self, t, v, tb):
+    """Stops the reverse port forwarding in the enter/exit decorator."""
+    self.Stop()
+
+  def Start(self):
+    """Sets up reverse port forwarding with a remote webdriver."""
+    # Get an adb server running for a given device.
+    self._adb = android_commands.AndroidCommands(self._device_serial)
+    self._adb.StartAdbServer()
+    # Begin forwarding the device_ports to the host_ports.
+    self._forwarder = forwarder.Forwarder(self._cmd, 'Release')
+    self._forwarder.Run([
+        (self._device_http, self._host_http),
+        (self._device_https, self._host_https)],
+                        valgrind_tools.BaseTool())
+
+  def Stop(self):
+    """Cleans up after the start call by closing the forwarder."""
+    # shut down the forwarder.
+    self._forwarder.Close()
+
+  def GetChromeArgs(self):
+    """Makes a list of arguments to enable reverse port forwarding on chrome.
+
+    Returns:
+      A list of chrome arguments to make it work with ReversePortForwarder.
+    """
+    args = ['testing-fixed-http-port=%s' % self._device_http,
+            'testing-fixed-https-port=%s' % self._device_https,
+            'host-resolver-rules=\'MAP * 127.0.0.1,EXCEPT, localhost\'']
+    return args
diff --git a/chrome/test/functional/perf.py b/chrome/test/functional/perf.py
index 61e9fe6..a65a792 100755
--- a/chrome/test/functional/perf.py
+++ b/chrome/test/functional/perf.py
@@ -158,13 +158,26 @@
     except OSError, err:
       if err.errno == errno.ESRCH:
         return False
+
+    try:
+      with open('/proc/%s/status' % pid) as proc_file:
+        if 'zombie' in proc_file.read():
+          return False
+    except IOError:
+      return False
     return True
 
+  def _GetAllDescendentProcesses(self, pid):
+    pstree_out = subprocess.check_output(['pstree', '-p', '%s' % pid])
+    children = re.findall('\((\d+)\)', pstree_out)
+    return [int(pid) for pid in children]
+
   def _WaitForChromeExit(self, browser_info, timeout):
-    child_processes = browser_info['child_processes']
+    pid = browser_info['browser_pid']
+    chrome_pids = self._GetAllDescendentProcesses(pid)
     initial_time = time.time()
     while time.time() - initial_time < timeout:
-      if any([self._IsPIDRunning(c['pid']) for c in child_processes]):
+      if any([self._IsPIDRunning(pid) for pid in chrome_pids]):
         time.sleep(1)
       else:
         logging.info('_WaitForChromeExit() took: %s seconds',
@@ -177,8 +190,14 @@
     if self._IsPGOMode():
       browser_info = self.GetBrowserInfo()
       pid = browser_info['browser_pid']
+      # session_manager kills chrome without waiting for it to cleanly exit.
+      # Until that behavior is changed, we stop it and wait for Chrome to exit
+      # cleanly before restarting it. See:
+      # crbug.com/264717
+      subprocess.call(['sudo', 'pkill', '-STOP', 'session_manager'])
       os.kill(pid, signal.SIGINT)
       self._WaitForChromeExit(browser_info, 120)
+      subprocess.call(['sudo', 'pkill', '-CONT', 'session_manager'])
 
     pyauto.PyUITest.tearDown(self)
 
diff --git a/chrome/test/gpu/test_support_gpu.gypi b/chrome/test/gpu/test_support_gpu.gypi
index 4be0e52..083c0e3 100644
--- a/chrome/test/gpu/test_support_gpu.gypi
+++ b/chrome/test/gpu/test_support_gpu.gypi
@@ -60,7 +60,6 @@
         '<(SHARED_INTERMEDIATE_DIR)/content/content_resources.rc',
         '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.rc',
         '<(SHARED_INTERMEDIATE_DIR)/webkit/blink_resources.rc',
-        '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.rc',
       ],
       'conditions': [
         ['win_use_allocator_shim==1', {
diff --git a/chrome/test/gpu/webgl_infobar_browsertest.cc b/chrome/test/gpu/webgl_infobar_browsertest.cc
index c22f59b..35071ae 100644
--- a/chrome/test/gpu/webgl_infobar_browsertest.cc
+++ b/chrome/test/gpu/webgl_infobar_browsertest.cc
@@ -17,6 +17,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/test_launcher_utils.h"
+#include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/gpu_data_manager.h"
 #include "content/public/browser/notification_service.h"
@@ -28,10 +29,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_implementation.h"
 
-#if defined(OS_WIN) && defined(USE_ASH)
-#include "base/win/windows_version.h"
-#endif
-
 namespace {
 
 void SimulateGPUCrash(Browser* browser) {
@@ -72,7 +69,7 @@
 IN_PROC_BROWSER_TEST_F(WebGLInfobarTest, ContextLossRaisesInfobar) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
@@ -103,7 +100,7 @@
 IN_PROC_BROWSER_TEST_F(WebGLInfobarTest, ContextLossInfobarReload) {
 #if defined(OS_WIN) && defined(USE_ASH)
   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN8)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
     return;
 #endif
 
diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py
index 4084f36..7d81e0a 100755
--- a/chrome/test/pyautolib/pyauto.py
+++ b/chrome/test/pyautolib/pyauto.py
@@ -5086,27 +5086,6 @@
     }
     self._GetResultFromJSONRequest(cmd_dict, windex=None)
 
-  def GetUpdateInfo(self):
-    """Gets the status of the ChromeOS updater.
-
-    Returns:
-      a dictionary.
-      Samples:
-      { u'status': u'idle',
-        u'release_track': u'beta-channel'}
-
-      { u'status': u'downloading',
-        u'release_track': u'beta-channel',
-        u'download_progress': 0.1203236708350371,   # 0.0 ~ 1.0
-        u'new_size': 152033593,                     # size of payload, in bytes
-        u'last_checked_time': 1302055709}           # seconds since UNIX epoch
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    cmd_dict = { 'command': 'GetUpdateInfo' }
-    return self._GetResultFromJSONRequest(cmd_dict, windex=None)
-
   def UpdateCheck(self):
     """Checks for a ChromeOS update. Blocks until finished updating.
 
@@ -5116,23 +5095,6 @@
     cmd_dict = { 'command': 'UpdateCheck' }
     self._GetResultFromJSONRequest(cmd_dict, windex=None)
 
-  def SetReleaseTrack(self, track):
-    """Sets the release track (channel) of the ChromeOS updater.
-
-    Valid values for the track parameter are:
-      'stable-channel', 'beta-channel', 'dev-channel'
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    assert track in ('stable-channel', 'beta-channel', 'dev-channel'), \
-        'Attempt to set release track to unknown release track "%s".' % track
-    cmd_dict = {
-        'command': 'SetReleaseTrack',
-        'track': track,
-    }
-    self._GetResultFromJSONRequest(cmd_dict, windex=None)
-
   def GetVolumeInfo(self):
     """Gets the volume and whether the device is muted.
 
diff --git a/chrome/test/webdriver/test/infobar_browser_action_extension/bg.js b/chrome/test/webdriver/test/infobar_browser_action_extension/bg.js
index 9a2fa62..5d29b54 100644
--- a/chrome/test/webdriver/test/infobar_browser_action_extension/bg.js
+++ b/chrome/test/webdriver/test/infobar_browser_action_extension/bg.js
@@ -11,7 +11,7 @@
 }
 
 chrome.tabs.getSelected(null, function(tab) {
-  chrome.experimental.infobars.show(
+  chrome.infobars.show(
       {'tabId': tab.id, 'path': 'view_checks.html'}, function() {
     g_infobarReady = true;
     if (g_callback)
diff --git a/chrome/test/webdriver/test/infobar_browser_action_extension/manifest.json b/chrome/test/webdriver/test/infobar_browser_action_extension/manifest.json
index cfee923..db1f570 100644
--- a/chrome/test/webdriver/test/infobar_browser_action_extension/manifest.json
+++ b/chrome/test/webdriver/test/infobar_browser_action_extension/manifest.json
@@ -10,6 +10,6 @@
   },
   "permissions": [
     "tabs",
-    "experimental"
+    "infobars"
   ]
 }
diff --git a/chrome/test/webdriver/webdriver_session.cc b/chrome/test/webdriver/webdriver_session.cc
index 2675259..51f901e 100644
--- a/chrome/test/webdriver/webdriver_session.cc
+++ b/chrome/test/webdriver/webdriver_session.cc
@@ -308,7 +308,7 @@
 }
 
 Error* Session::SendKeys(const string16& keys) {
-  Error* error;
+  Error* error = NULL;
   RunSessionTask(base::Bind(
       &Session::SendKeysOnSessionThread,
       base::Unretained(this),
diff --git a/chrome/tools/build/generate_policy_source.py b/chrome/tools/build/generate_policy_source.py
index efe0d2f..4f5ee46 100755
--- a/chrome/tools/build/generate_policy_source.py
+++ b/chrome/tools/build/generate_policy_source.py
@@ -71,6 +71,7 @@
     self.desc = '\n'.join(
         map(str.strip, self._RemovePlaceholders(policy['desc']).splitlines()))
     self.caption = self._RemovePlaceholders(policy['caption'])
+    self.items = policy.get('items')
 
   PH_PATTERN = re.compile('<ph[^>]*>([^<]*|[^<]*<ex>([^<]*)</ex>[^<]*)</ph>')
 
@@ -369,6 +370,10 @@
 
 def _WritePolicyProto(f, policy, fields):
   _OutputComment(f, policy.caption + '\n\n' + policy.desc)
+  if policy.items is not None:
+    _OutputComment(f, '\nValid values:')
+    for item in policy.items:
+      _OutputComment(f, '  %s: %s' % (str(item['value']), item['caption']))
   f.write('message %sProto {\n' % policy.name)
   f.write('  optional PolicyOptions policy_options = 1;\n')
   f.write('  optional %s %s = 2;\n' % (policy.protobuf_type, policy.name))
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 66303fa..7b1ecca 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -37,13 +37,13 @@
 #include "base/win/iat_patch_function.h"
 #include "base/win/scoped_handle.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/utility/itunes_pref_parser_win.h"
+#include "chrome/utility/media_galleries/itunes_pref_parser_win.h"
 #include "printing/emf_win.h"
 #include "ui/gfx/gdi_util.h"
 #endif  // defined(OS_WIN)
 
 #if defined(OS_WIN) || defined(OS_MACOSX)
-#include "chrome/utility/itunes_library_parser.h"
+#include "chrome/utility/media_galleries/itunes_library_parser.h"
 #include "chrome/utility/media_galleries/picasa_album_table_reader.h"
 #include "chrome/utility/media_galleries/picasa_albums_indexer.h"
 #endif  // defined(OS_WIN) || defined(OS_MACOSX)
diff --git a/chrome/utility/local_discovery/local_domain_resolver_unittest.cc b/chrome/utility/local_discovery/local_domain_resolver_unittest.cc
index c1fd51c..1dc9fc3 100644
--- a/chrome/utility/local_discovery/local_domain_resolver_unittest.cc
+++ b/chrome/utility/local_discovery/local_domain_resolver_unittest.cc
@@ -14,48 +14,48 @@
 
 namespace {
 
-const char kSamplePacketA[] = {
+const uint8 kSamplePacketA[] = {
   // Header
-  '\x00', '\x00',               // ID is zeroed out
-  '\x81', '\x80',               // Standard query response, RA, no error
-  '\x00', '\x00',               // No questions (for simplicity)
-  '\x00', '\x01',               // 1 RR (answers)
-  '\x00', '\x00',               // 0 authority RRs
-  '\x00', '\x00',               // 0 additional RRs
+  0x00, 0x00,               // ID is zeroed out
+  0x81, 0x80,               // Standard query response, RA, no error
+  0x00, 0x00,               // No questions (for simplicity)
+  0x00, 0x01,               // 1 RR (answers)
+  0x00, 0x00,               // 0 authority RRs
+  0x00, 0x00,               // 0 additional RRs
 
-  '\x07', 'm', 'y', 'h', 'e', 'l', 'l', 'o',
-  '\x05', 'l', 'o', 'c', 'a', 'l',
-  '\x00',
-  '\x00', '\x01',        // TYPE is A.
-  '\x00', '\x01',        // CLASS is IN.
-  '\x00', '\x00',        // TTL (4 bytes) is 16 seconds.
-  '\x00', '\x10',
-  '\x00', '\x04',        // RDLENGTH is 4 bytes.
-  '\x01', '\x02',
-  '\x03', '\x04',
+  0x07, 'm', 'y', 'h', 'e', 'l', 'l', 'o',
+  0x05, 'l', 'o', 'c', 'a', 'l',
+  0x00,
+  0x00, 0x01,        // TYPE is A.
+  0x00, 0x01,        // CLASS is IN.
+  0x00, 0x00,        // TTL (4 bytes) is 16 seconds.
+  0x00, 0x10,
+  0x00, 0x04,        // RDLENGTH is 4 bytes.
+  0x01, 0x02,
+  0x03, 0x04,
 };
 
-const char kSamplePacketAAAA[] = {
+const uint8 kSamplePacketAAAA[] = {
   // Header
-  '\x00', '\x00',               // ID is zeroed out
-  '\x81', '\x80',               // Standard query response, RA, no error
-  '\x00', '\x00',               // No questions (for simplicity)
-  '\x00', '\x01',               // 1 RR (answers)
-  '\x00', '\x00',               // 0 authority RRs
-  '\x00', '\x00',               // 0 additional RRs
+  0x00, 0x00,               // ID is zeroed out
+  0x81, 0x80,               // Standard query response, RA, no error
+  0x00, 0x00,               // No questions (for simplicity)
+  0x00, 0x01,               // 1 RR (answers)
+  0x00, 0x00,               // 0 authority RRs
+  0x00, 0x00,               // 0 additional RRs
 
-  '\x07', 'm', 'y', 'h', 'e', 'l', 'l', 'o',
-  '\x05', 'l', 'o', 'c', 'a', 'l',
-  '\x00',
-  '\x00', '\x1C',        // TYPE is AAAA.
-  '\x00', '\x01',        // CLASS is IN.
-  '\x00', '\x00',        // TTL (4 bytes) is 16 seconds.
-  '\x00', '\x10',
-  '\x00', '\x10',        // RDLENGTH is 4 bytes.
-  '\x00', '\x0A', '\x00', '\x00',
-  '\x00', '\x00', '\x00', '\x00',
-  '\x00', '\x01', '\x00', '\x02',
-  '\x00', '\x03', '\x00', '\x04',
+  0x07, 'm', 'y', 'h', 'e', 'l', 'l', 'o',
+  0x05, 'l', 'o', 'c', 'a', 'l',
+  0x00,
+  0x00, 0x1C,        // TYPE is AAAA.
+  0x00, 0x01,        // CLASS is IN.
+  0x00, 0x00,        // TTL (4 bytes) is 16 seconds.
+  0x00, 0x10,
+  0x00, 0x10,        // RDLENGTH is 4 bytes.
+  0x00, 0x0A, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00,
+  0x00, 0x01, 0x00, 0x02,
+  0x00, 0x03, 0x00, 0x04,
 };
 
 class LocalDomainResolverTest : public testing::Test {
diff --git a/chrome/utility/local_discovery/service_discovery_client_unittest.cc b/chrome/utility/local_discovery/service_discovery_client_unittest.cc
index b79d86e..2f7a15d 100644
--- a/chrome/utility/local_discovery/service_discovery_client_unittest.cc
+++ b/chrome/utility/local_discovery/service_discovery_client_unittest.cc
@@ -26,113 +26,113 @@
 
 namespace {
 
-const char kSamplePacketPTR[] = {
+const uint8 kSamplePacketPTR[] = {
   // Header
-  '\x00', '\x00',               // ID is zeroed out
-  '\x81', '\x80',               // Standard query response, RA, no error
-  '\x00', '\x00',               // No questions (for simplicity)
-  '\x00', '\x01',               // 1 RR (answers)
-  '\x00', '\x00',               // 0 authority RRs
-  '\x00', '\x00',               // 0 additional RRs
+  0x00, 0x00,               // ID is zeroed out
+  0x81, 0x80,               // Standard query response, RA, no error
+  0x00, 0x00,               // No questions (for simplicity)
+  0x00, 0x01,               // 1 RR (answers)
+  0x00, 0x00,               // 0 authority RRs
+  0x00, 0x00,               // 0 additional RRs
 
-  '\x07', '_', 'p', 'r', 'i', 'v', 'e', 't',
-  '\x04', '_', 't', 'c', 'p',
-  '\x05', 'l', 'o', 'c', 'a', 'l',
-  '\x00',
-  '\x00', '\x0c',        // TYPE is PTR.
-  '\x00', '\x01',        // CLASS is IN.
-  '\x00', '\x00',        // TTL (4 bytes) is 1 second.
-  '\x00', '\x01',
-  '\x00', '\x08',        // RDLENGTH is 8 bytes.
-  '\x05', 'h', 'e', 'l', 'l', 'o',
-  '\xc0', '\x0c'
+  0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+  0x04, '_', 't', 'c', 'p',
+  0x05, 'l', 'o', 'c', 'a', 'l',
+  0x00,
+  0x00, 0x0c,        // TYPE is PTR.
+  0x00, 0x01,        // CLASS is IN.
+  0x00, 0x00,        // TTL (4 bytes) is 1 second.
+  0x00, 0x01,
+  0x00, 0x08,        // RDLENGTH is 8 bytes.
+  0x05, 'h', 'e', 'l', 'l', 'o',
+  0xc0, 0x0c
 };
 
-const char kSamplePacketSRV[] = {
+const uint8 kSamplePacketSRV[] = {
   // Header
-  '\x00', '\x00',               // ID is zeroed out
-  '\x81', '\x80',               // Standard query response, RA, no error
-  '\x00', '\x00',               // No questions (for simplicity)
-  '\x00', '\x01',               // 1 RR (answers)
-  '\x00', '\x00',               // 0 authority RRs
-  '\x00', '\x00',               // 0 additional RRs
+  0x00, 0x00,               // ID is zeroed out
+  0x81, 0x80,               // Standard query response, RA, no error
+  0x00, 0x00,               // No questions (for simplicity)
+  0x00, 0x01,               // 1 RR (answers)
+  0x00, 0x00,               // 0 authority RRs
+  0x00, 0x00,               // 0 additional RRs
 
-  '\x05', 'h', 'e', 'l', 'l', 'o',
-  '\x07', '_', 'p', 'r', 'i', 'v', 'e', 't',
-  '\x04', '_', 't', 'c', 'p',
-  '\x05', 'l', 'o', 'c', 'a', 'l',
-  '\x00',
-  '\x00', '\x21',        // TYPE is SRV.
-  '\x00', '\x01',        // CLASS is IN.
-  '\x00', '\x00',        // TTL (4 bytes) is 1 second.
-  '\x00', '\x01',
-  '\x00', '\x15',        // RDLENGTH is 21 bytes.
-  '\x00', '\x00',
-  '\x00', '\x00',
-  '\x22', '\xb8',  // port 8888
-  '\x07', 'm', 'y', 'h', 'e', 'l', 'l', 'o',
-  '\x05', 'l', 'o', 'c', 'a', 'l',
-  '\x00',
+  0x05, 'h', 'e', 'l', 'l', 'o',
+  0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+  0x04, '_', 't', 'c', 'p',
+  0x05, 'l', 'o', 'c', 'a', 'l',
+  0x00,
+  0x00, 0x21,        // TYPE is SRV.
+  0x00, 0x01,        // CLASS is IN.
+  0x00, 0x00,        // TTL (4 bytes) is 1 second.
+  0x00, 0x01,
+  0x00, 0x15,        // RDLENGTH is 21 bytes.
+  0x00, 0x00,
+  0x00, 0x00,
+  0x22, 0xb8,  // port 8888
+  0x07, 'm', 'y', 'h', 'e', 'l', 'l', 'o',
+  0x05, 'l', 'o', 'c', 'a', 'l',
+  0x00,
 };
 
-const char kSamplePacketTXT[] = {
+const uint8 kSamplePacketTXT[] = {
   // Header
-  '\x00', '\x00',               // ID is zeroed out
-  '\x81', '\x80',               // Standard query response, RA, no error
-  '\x00', '\x00',               // No questions (for simplicity)
-  '\x00', '\x01',               // 1 RR (answers)
-  '\x00', '\x00',               // 0 authority RRs
-  '\x00', '\x00',               // 0 additional RRs
+  0x00, 0x00,               // ID is zeroed out
+  0x81, 0x80,               // Standard query response, RA, no error
+  0x00, 0x00,               // No questions (for simplicity)
+  0x00, 0x01,               // 1 RR (answers)
+  0x00, 0x00,               // 0 authority RRs
+  0x00, 0x00,               // 0 additional RRs
 
-  '\x05', 'h', 'e', 'l', 'l', 'o',
-  '\x07', '_', 'p', 'r', 'i', 'v', 'e', 't',
-  '\x04', '_', 't', 'c', 'p',
-  '\x05', 'l', 'o', 'c', 'a', 'l',
-  '\x00',
-  '\x00', '\x10',        // TYPE is PTR.
-  '\x00', '\x01',        // CLASS is IN.
-  '\x00', '\x00',        // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
-  '\x00', '\x01',
-  '\x00', '\x06',        // RDLENGTH is 21 bytes.
-  '\x05', 'h', 'e', 'l', 'l', 'o'
+  0x05, 'h', 'e', 'l', 'l', 'o',
+  0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+  0x04, '_', 't', 'c', 'p',
+  0x05, 'l', 'o', 'c', 'a', 'l',
+  0x00,
+  0x00, 0x10,        // TYPE is PTR.
+  0x00, 0x01,        // CLASS is IN.
+  0x00, 0x00,        // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
+  0x00, 0x01,
+  0x00, 0x06,        // RDLENGTH is 21 bytes.
+  0x05, 'h', 'e', 'l', 'l', 'o'
 };
 
-const char kSamplePacketSRVA[] = {
+const uint8 kSamplePacketSRVA[] = {
   // Header
-  '\x00', '\x00',               // ID is zeroed out
-  '\x81', '\x80',               // Standard query response, RA, no error
-  '\x00', '\x00',               // No questions (for simplicity)
-  '\x00', '\x02',               // 2 RR (answers)
-  '\x00', '\x00',               // 0 authority RRs
-  '\x00', '\x00',               // 0 additional RRs
+  0x00, 0x00,               // ID is zeroed out
+  0x81, 0x80,               // Standard query response, RA, no error
+  0x00, 0x00,               // No questions (for simplicity)
+  0x00, 0x02,               // 2 RR (answers)
+  0x00, 0x00,               // 0 authority RRs
+  0x00, 0x00,               // 0 additional RRs
 
-  '\x05', 'h', 'e', 'l', 'l', 'o',
-  '\x07', '_', 'p', 'r', 'i', 'v', 'e', 't',
-  '\x04', '_', 't', 'c', 'p',
-  '\x05', 'l', 'o', 'c', 'a', 'l',
-  '\x00',
-  '\x00', '\x21',        // TYPE is SRV.
-  '\x00', '\x01',        // CLASS is IN.
-  '\x00', '\x00',        // TTL (4 bytes) is 16 seconds.
-  '\x00', '\x10',
-  '\x00', '\x15',        // RDLENGTH is 21 bytes.
-  '\x00', '\x00',
-  '\x00', '\x00',
-  '\x22', '\xb8',  // port 8888
-  '\x07', 'm', 'y', 'h', 'e', 'l', 'l', 'o',
-  '\x05', 'l', 'o', 'c', 'a', 'l',
-  '\x00',
+  0x05, 'h', 'e', 'l', 'l', 'o',
+  0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+  0x04, '_', 't', 'c', 'p',
+  0x05, 'l', 'o', 'c', 'a', 'l',
+  0x00,
+  0x00, 0x21,        // TYPE is SRV.
+  0x00, 0x01,        // CLASS is IN.
+  0x00, 0x00,        // TTL (4 bytes) is 16 seconds.
+  0x00, 0x10,
+  0x00, 0x15,        // RDLENGTH is 21 bytes.
+  0x00, 0x00,
+  0x00, 0x00,
+  0x22, 0xb8,  // port 8888
+  0x07, 'm', 'y', 'h', 'e', 'l', 'l', 'o',
+  0x05, 'l', 'o', 'c', 'a', 'l',
+  0x00,
 
-  '\x07', 'm', 'y', 'h', 'e', 'l', 'l', 'o',
-  '\x05', 'l', 'o', 'c', 'a', 'l',
-  '\x00',
-  '\x00', '\x01',        // TYPE is A.
-  '\x00', '\x01',        // CLASS is IN.
-  '\x00', '\x00',        // TTL (4 bytes) is 16 seconds.
-  '\x00', '\x10',
-  '\x00', '\x04',        // RDLENGTH is 4 bytes.
-  '\x01', '\x02',
-  '\x03', '\x04',
+  0x07, 'm', 'y', 'h', 'e', 'l', 'l', 'o',
+  0x05, 'l', 'o', 'c', 'a', 'l',
+  0x00,
+  0x00, 0x01,        // TYPE is A.
+  0x00, 0x01,        // CLASS is IN.
+  0x00, 0x00,        // TTL (4 bytes) is 16 seconds.
+  0x00, 0x10,
+  0x00, 0x04,        // RDLENGTH is 4 bytes.
+  0x01, 0x02,
+  0x03, 0x04,
 };
 
 class MockServiceWatcherClient {
diff --git a/chrome/utility/media_galleries/OWNERS b/chrome/utility/media_galleries/OWNERS
index f441493..3b15a92 100644
--- a/chrome/utility/media_galleries/OWNERS
+++ b/chrome/utility/media_galleries/OWNERS
@@ -1,4 +1,4 @@
-estade@chromium.org
 gbillock@chromium.org
 thestig@chromium.org
+tommycli@chromium.org
 vandebo@chromium.org
diff --git a/chrome/utility/itunes_library_parser.cc b/chrome/utility/media_galleries/itunes_library_parser.cc
similarity index 97%
rename from chrome/utility/itunes_library_parser.cc
rename to chrome/utility/media_galleries/itunes_library_parser.cc
index 89aa1a9..5f14a0e 100644
--- a/chrome/utility/itunes_library_parser.cc
+++ b/chrome/utility/media_galleries/itunes_library_parser.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/utility/itunes_library_parser.h"
+#include "chrome/utility/media_galleries/itunes_library_parser.h"
 
 #include <string>
 
@@ -11,7 +11,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/common/itunes_xml_utils.h"
+#include "chrome/common/media_galleries/itunes_xml_utils.h"
 #include "third_party/libxml/chromium/libxml_utils.h"
 #include "url/gurl.h"
 #include "url/url_canon.h"
diff --git a/chrome/utility/itunes_library_parser.h b/chrome/utility/media_galleries/itunes_library_parser.h
similarity index 75%
rename from chrome/utility/itunes_library_parser.h
rename to chrome/utility/media_galleries/itunes_library_parser.h
index 377612f..60558e4 100644
--- a/chrome/utility/itunes_library_parser.h
+++ b/chrome/utility/media_galleries/itunes_library_parser.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_UTILITY_ITUNES_LIBRARY_PARSER_H_
-#define CHROME_UTILITY_ITUNES_LIBRARY_PARSER_H_
+#ifndef CHROME_UTILITY_MEDIA_GALLERIES_ITUNES_LIBRARY_PARSER_H_
+#define CHROME_UTILITY_MEDIA_GALLERIES_ITUNES_LIBRARY_PARSER_H_
 
 #include <string>
 
 #include "base/platform_file.h"
-#include "chrome/common/itunes_library.h"
+#include "chrome/common/media_galleries/itunes_library.h"
 
 namespace itunes {
 
@@ -34,4 +34,4 @@
 
 }  // namespace itunes
 
-#endif  // CHROME_UTILITY_ITUNES_LIBRARY_PARSER_H_
+#endif  // CHROME_UTILITY_MEDIA_GALLERIES_ITUNES_LIBRARY_PARSER_H_
diff --git a/chrome/utility/itunes_library_parser_unittest.cc b/chrome/utility/media_galleries/itunes_library_parser_unittest.cc
similarity index 98%
rename from chrome/utility/itunes_library_parser_unittest.cc
rename to chrome/utility/media_galleries/itunes_library_parser_unittest.cc
index 19114c0..ccdeb94 100644
--- a/chrome/utility/itunes_library_parser_unittest.cc
+++ b/chrome/utility/media_galleries/itunes_library_parser_unittest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "base/logging.h"
-#include "chrome/common/itunes_library.h"
-#include "chrome/utility/itunes_library_parser.h"
+#include "chrome/common/media_galleries/itunes_library.h"
+#include "chrome/utility/media_galleries/itunes_library_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #define SIMPLE_HEADER()         \
diff --git a/chrome/utility/itunes_pref_parser_win.cc b/chrome/utility/media_galleries/itunes_pref_parser_win.cc
similarity index 91%
rename from chrome/utility/itunes_pref_parser_win.cc
rename to chrome/utility/media_galleries/itunes_pref_parser_win.cc
index b4f0a2a..a36170d 100644
--- a/chrome/utility/itunes_pref_parser_win.cc
+++ b/chrome/utility/media_galleries/itunes_pref_parser_win.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/utility/itunes_pref_parser_win.h"
+#include "chrome/utility/media_galleries/itunes_pref_parser_win.h"
 
 #include "base/base64.h"
 #include "base/strings/string_util.h"
-#include "chrome/common/itunes_xml_utils.h"
+#include "chrome/common/media_galleries/itunes_xml_utils.h"
 #include "third_party/libxml/chromium/libxml_utils.h"
 
 namespace itunes {
diff --git a/chrome/utility/itunes_pref_parser_win.h b/chrome/utility/media_galleries/itunes_pref_parser_win.h
similarity index 79%
rename from chrome/utility/itunes_pref_parser_win.h
rename to chrome/utility/media_galleries/itunes_pref_parser_win.h
index fc2371f..c52ec5f 100644
--- a/chrome/utility/itunes_pref_parser_win.h
+++ b/chrome/utility/media_galleries/itunes_pref_parser_win.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_UTILITY_ITUNES_PREF_PARSER_WIN_H_
-#define CHROME_UTILITY_ITUNES_PREF_PARSER_WIN_H_
+#ifndef CHROME_UTILITY_MEDIA_GALLERIES_ITUNES_PREF_PARSER_WIN_H_
+#define CHROME_UTILITY_MEDIA_GALLERIES_ITUNES_PREF_PARSER_WIN_H_
 
 #include <string>
 
@@ -30,4 +30,4 @@
 
 }  // namespace itunes
 
-#endif  // CHROME_UTILITY_ITUNES_PREF_PARSER_WIN_H_
+#endif  // CHROME_UTILITY_MEDIA_GALLERIES_ITUNES_PREF_PARSER_WIN_H_
diff --git a/chrome/utility/media_galleries/picasa_album_table_reader_unittest.cc b/chrome/utility/media_galleries/picasa_album_table_reader_unittest.cc
index 3c5991c..fe9b828 100644
--- a/chrome/utility/media_galleries/picasa_album_table_reader_unittest.cc
+++ b/chrome/utility/media_galleries/picasa_album_table_reader_unittest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "chrome/common/media_galleries/pmp_constants.h"
+#include "chrome/common/media_galleries/pmp_test_helper.h"
 #include "chrome/utility/media_galleries/picasa_album_table_reader.h"
-#include "chrome/utility/media_galleries/pmp_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace picasa {
diff --git a/chrome/utility/media_galleries/pmp_column_reader_unittest.cc b/chrome/utility/media_galleries/pmp_column_reader_unittest.cc
index a9cfa5b..a1f44b7 100644
--- a/chrome/utility/media_galleries/pmp_column_reader_unittest.cc
+++ b/chrome/utility/media_galleries/pmp_column_reader_unittest.cc
@@ -5,15 +5,47 @@
 #include <algorithm>
 #include <vector>
 
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/platform_file.h"
 #include "chrome/common/media_galleries/pmp_constants.h"
+#include "chrome/common/media_galleries/pmp_test_helper.h"
 #include "chrome/utility/media_galleries/pmp_column_reader.h"
-#include "chrome/utility/media_galleries/pmp_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace picasa {
+
 namespace {
 
-using picasa::PmpColumnReader;
-using picasa::PmpTestHelper;
+bool InitColumnReaderFromBytes(
+    PmpColumnReader* const reader,
+    const std::vector<char>& data,
+    const PmpFieldType expected_type) {
+  base::ScopedTempDir temp_dir;
+  if (!temp_dir.CreateUniqueTempDir())
+    return false;
+
+  base::FilePath temp_path;
+  if (!file_util::CreateTemporaryFileInDir(temp_dir.path(), &temp_path))
+    return false;
+
+  // Explicit conversion from signed to unsigned.
+  size_t bytes_written = file_util::WriteFile(temp_path, &data[0], data.size());
+  if (bytes_written != data.size())
+    return false;
+
+  int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ;
+  base::PlatformFile platform_file =
+      base::CreatePlatformFile(temp_path, flags, NULL, NULL);
+  if (platform_file == base::kInvalidPlatformFileValue)
+    return false;
+
+  bool read_success = reader->ReadFile(platform_file, expected_type);
+
+  base::ClosePlatformFile(platform_file);
+
+  return read_success;
+}
 
 // Overridden version of Read method to make test code templatable.
 bool DoRead(const PmpColumnReader* reader, uint32 row, std::string* target) {
@@ -38,16 +70,15 @@
 
 // TestValid
 template<class T>
-void TestValid(const picasa::PmpFieldType field_type,
+void TestValid(const PmpFieldType field_type,
                const std::vector<T>& elems) {
   PmpTestHelper test_helper("test");
   ASSERT_TRUE(test_helper.Init());
 
   PmpColumnReader reader;
-  std::vector<uint8> data =
+  std::vector<char> data =
       PmpTestHelper::MakeHeaderAndBody(field_type, elems.size(), elems);
-  ASSERT_TRUE(test_helper.InitColumnReaderFromBytes(
-      &reader, data, field_type));
+  ASSERT_TRUE(InitColumnReaderFromBytes(&reader, data, field_type));
   EXPECT_EQ(elems.size(), reader.rows_read());
 
   for (uint32 i = 0; i < elems.size() && i < reader.rows_read(); i++) {
@@ -58,44 +89,44 @@
 }
 
 template<class T>
-void TestMalformed(const picasa::PmpFieldType field_type,
+void TestMalformed(const PmpFieldType field_type,
                    const std::vector<T>& elems) {
   PmpTestHelper test_helper("test");
   ASSERT_TRUE(test_helper.Init());
 
   PmpColumnReader reader_too_few_declared_rows;
-  std::vector<uint8> data_too_few_declared_rows =
+  std::vector<char> data_too_few_declared_rows =
       PmpTestHelper::MakeHeaderAndBody(field_type, elems.size()-1, elems);
-  EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
-      &reader_too_few_declared_rows,
-      data_too_few_declared_rows,
-      field_type));
+  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_too_few_declared_rows,
+                                         data_too_few_declared_rows,
+                                         field_type));
 
   PmpColumnReader reader_too_many_declared_rows;
-  std::vector<uint8> data_too_many_declared_rows =
+  std::vector<char> data_too_many_declared_rows =
       PmpTestHelper::MakeHeaderAndBody(field_type, elems.size()+1, elems);
-  EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
-      &reader_too_many_declared_rows,
-      data_too_many_declared_rows,
-      field_type));
+  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_too_many_declared_rows,
+                                         data_too_many_declared_rows,
+                                         field_type));
 
   PmpColumnReader reader_truncated;
-  std::vector<uint8> data_truncated =
+  std::vector<char> data_truncated =
       PmpTestHelper::MakeHeaderAndBody(field_type, elems.size(), elems);
   data_truncated.resize(data_truncated.size()-10);
-  EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
-      &reader_truncated, data_truncated, field_type));
+  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_truncated,
+                                         data_truncated,
+                                         field_type));
 
   PmpColumnReader reader_padded;
-  std::vector<uint8> data_padded =
+  std::vector<char> data_padded =
       PmpTestHelper::MakeHeaderAndBody(field_type, elems.size(), elems);
   data_padded.resize(data_padded.size()+10);
-  EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
-      &reader_padded, data_padded, field_type));
+  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_padded,
+                                         data_padded,
+                                         field_type));
 }
 
 template<class T>
-void TestPrimitive(const picasa::PmpFieldType field_type) {
+void TestPrimitive(const PmpFieldType field_type) {
   // Make an ascending vector of the primitive.
   uint32 n = 100;
   std::vector<T> data(n, 0);
@@ -113,58 +144,53 @@
   ASSERT_TRUE(test_helper.Init());
 
   PmpColumnReader reader_good_header;
-  std::vector<uint8> good_header =
-      PmpTestHelper::MakeHeader(picasa::PMP_TYPE_STRING, 0);
-  EXPECT_TRUE(test_helper.InitColumnReaderFromBytes(
-      &reader_good_header,
-      good_header,
-      picasa::PMP_TYPE_STRING));
+  std::vector<char> good_header =
+      PmpTestHelper::MakeHeader(PMP_TYPE_STRING, 0);
+  EXPECT_TRUE(InitColumnReaderFromBytes(&reader_good_header,
+                                        good_header,
+                                        PMP_TYPE_STRING));
   EXPECT_EQ(0U, reader_good_header.rows_read()) <<
       "Read non-zero rows from header-only data.";
 
   PmpColumnReader reader_bad_magic_bytes;
-  std::vector<uint8> bad_magic_bytes =
-      PmpTestHelper::MakeHeader(picasa::PMP_TYPE_STRING, 0);
-  bad_magic_bytes[0] = 0xff;
-  EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
-      &reader_bad_magic_bytes,
-      bad_magic_bytes,
-      picasa::PMP_TYPE_STRING));
+  std::vector<char> bad_magic_bytes =
+      PmpTestHelper::MakeHeader(PMP_TYPE_STRING, 0);
+  bad_magic_bytes[0] = (char)0xff;
+  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_bad_magic_bytes,
+                                         bad_magic_bytes,
+                                         PMP_TYPE_STRING));
 
   PmpColumnReader reader_inconsistent_types;
-  std::vector<uint8> inconsistent_type =
-      PmpTestHelper::MakeHeader(picasa::PMP_TYPE_STRING, 0);
-  inconsistent_type[picasa::kPmpFieldType1Offset] = 0xff;
-  EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
-      &reader_inconsistent_types,
-      inconsistent_type,
-      picasa::PMP_TYPE_STRING));
+  std::vector<char> inconsistent_type =
+      PmpTestHelper::MakeHeader(PMP_TYPE_STRING, 0);
+  inconsistent_type[kPmpFieldType1Offset] = (char)0xff;
+  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_inconsistent_types,
+                                         inconsistent_type,
+                                         PMP_TYPE_STRING));
 
   PmpColumnReader reader_invalid_type;
-  std::vector<uint8> invalid_type =
-      PmpTestHelper::MakeHeader(picasa::PMP_TYPE_STRING, 0);
-  invalid_type[picasa::kPmpFieldType1Offset] = 0xff;
-  invalid_type[picasa::kPmpFieldType2Offset] = 0xff;
-  EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
-      &reader_invalid_type,
-      invalid_type,
-      picasa::PMP_TYPE_STRING));
+  std::vector<char> invalid_type =
+      PmpTestHelper::MakeHeader(PMP_TYPE_STRING, 0);
+  invalid_type[kPmpFieldType1Offset] = (char)0xff;
+  invalid_type[kPmpFieldType2Offset] = (char)0xff;
+  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_invalid_type,
+                                         invalid_type,
+                                         PMP_TYPE_STRING));
 
   PmpColumnReader reader_incomplete_header;
-  std::vector<uint8> incomplete_header =
-      PmpTestHelper::MakeHeader(picasa::PMP_TYPE_STRING, 0);
+  std::vector<char> incomplete_header =
+      PmpTestHelper::MakeHeader(PMP_TYPE_STRING, 0);
   incomplete_header.resize(10);
-  EXPECT_FALSE(test_helper.InitColumnReaderFromBytes(
-      &reader_incomplete_header,
-      incomplete_header,
-      picasa::PMP_TYPE_STRING));
+  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_incomplete_header,
+                                         incomplete_header,
+                                         PMP_TYPE_STRING));
 }
 
 TEST(PmpColumnReaderTest, StringParsing) {
   std::vector<std::string> empty_strings(100, "");
 
   // Test empty strings read okay.
-  TestValid(picasa::PMP_TYPE_STRING, empty_strings);
+  TestValid(PMP_TYPE_STRING, empty_strings);
 
   std::vector<std::string> mixed_strings;
   mixed_strings.push_back("");
@@ -176,17 +202,19 @@
   mixed_strings.push_back("");
 
   // Test that a mixed set of strings read correctly.
-  TestValid(picasa::PMP_TYPE_STRING, mixed_strings);
+  TestValid(PMP_TYPE_STRING, mixed_strings);
 
   // Test with the data messed up in a variety of ways.
-  TestMalformed(picasa::PMP_TYPE_STRING, mixed_strings);
+  TestMalformed(PMP_TYPE_STRING, mixed_strings);
 }
 
 TEST(PmpColumnReaderTest, PrimitiveParsing) {
-  TestPrimitive<uint32>(picasa::PMP_TYPE_UINT32);
-  TestPrimitive<double>(picasa::PMP_TYPE_DOUBLE64);
-  TestPrimitive<uint8>(picasa::PMP_TYPE_UINT8);
-  TestPrimitive<uint64>(picasa::PMP_TYPE_UINT64);
+  TestPrimitive<uint32>(PMP_TYPE_UINT32);
+  TestPrimitive<double>(PMP_TYPE_DOUBLE64);
+  TestPrimitive<uint8>(PMP_TYPE_UINT8);
+  TestPrimitive<uint64>(PMP_TYPE_UINT64);
 }
 
 }  // namespace
+
+}  // namespace picasa