Remove inappropriate chrome.{extension,app} bindings for platform apps.

Also:

-Begin process of moving from chrome.extension.lastError to chrome.runtime.lastError.

-Add chrome.runtime.getManifest and chrome.runtime.getURL



BUG=131717
TEST=In platform apps, chrome.extension and chrome.app should not contain any
extension or legacy hosted app stuff on them.


Review URL: https://chromiumcodereview.appspot.com/10666027

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@145238 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/runtime_apitest.cc b/chrome/browser/extensions/runtime_apitest.cc
new file mode 100644
index 0000000..d8b449d
--- /dev/null
+++ b/chrome/browser/extensions/runtime_apitest.cc
@@ -0,0 +1,9 @@
+// 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"
+
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeRuntime) {
+  ASSERT_TRUE(RunExtensionTest("runtime")) << message_;
+}
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi
index 1171d28..e26075d 100644
--- a/chrome/chrome_renderer.gypi
+++ b/chrome/chrome_renderer.gypi
@@ -109,6 +109,8 @@
         'renderer/extensions/page_actions_custom_bindings.h',
         'renderer/extensions/page_capture_custom_bindings.cc',
         'renderer/extensions/page_capture_custom_bindings.h',
+        'renderer/extensions/runtime_custom_bindings.cc',
+        'renderer/extensions/runtime_custom_bindings.h',
         'renderer/extensions/send_request_natives.cc',
         'renderer/extensions/send_request_natives.h',
         'renderer/extensions/set_icon_natives.cc',
@@ -160,6 +162,7 @@
         'renderer/resources/extensions/greasemonkey_api.js',
         'renderer/resources/extensions/input.ime_custom_bindings.js',
         'renderer/resources/extensions/json_schema.js',
+        'renderer/resources/extensions/last_error.js',
         'renderer/resources/extensions/miscellaneous_bindings.js',
         'renderer/resources/extensions/omnibox_custom_bindings.js',
         'renderer/resources/extensions/page_action_custom_bindings.js',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index b41ecfa..fc9b162 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -2831,6 +2831,7 @@
         'browser/extensions/platform_app_browsertest_util.h',
         'browser/extensions/plugin_apitest.cc',
         'browser/extensions/process_management_browsertest.cc',
+        'browser/extensions/runtime_apitest.cc',
         'browser/extensions/sandboxed_pages_apitest.cc',
         'browser/extensions/shadow_dom_apitest.cc',
         'browser/extensions/settings/settings_apitest.cc',
diff --git a/chrome/common/extensions/api/extension_api.cc b/chrome/common/extensions/api/extension_api.cc
index e6298fab..83e3960 100644
--- a/chrome/common/extensions/api/extension_api.cc
+++ b/chrome/common/extensions/api/extension_api.cc
@@ -570,6 +570,34 @@
   return result;
 }
 
+namespace {
+
+bool IsFeatureAllowedForExtension(const std::string& feature,
+                                  const extensions::Extension& extension) {
+  if (extension.is_platform_app() &&
+      (feature == "app" || feature == "extension"))
+    return false;
+  return true;
+}
+
+// Removes APIs from |apis| that should not be allowed for |extension|.
+// TODO(kalman/asargent) - Make it possible to specify these rules
+// declaratively.
+void RemoveDisallowedAPIs(const Extension& extension,
+                          std::set<std::string>* apis) {
+  CHECK(apis);
+  std::set<std::string>::iterator i = apis->begin();
+  while (i != apis->end()) {
+    if (!IsFeatureAllowedForExtension(*i, extension)) {
+      apis->erase(i++);
+    } else {
+      ++i;
+    }
+  }
+}
+
+}  // namespace
+
 scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext(
     Feature::Context context, const Extension* extension, const GURL& url) {
   // We're forced to load all schemas now because we need to know the metadata
@@ -601,6 +629,7 @@
         // Availability is determined by the permissions of the extension.
         GetAllowedAPIs(extension, &temp_result);
         ResolveDependencies(&temp_result);
+        RemoveDisallowedAPIs(*extension, &temp_result);
       }
       break;
 
diff --git a/chrome/common/extensions/api/runtime.json b/chrome/common/extensions/api/runtime.json
index 1bae4fac..8428fee 100644
--- a/chrome/common/extensions/api/runtime.json
+++ b/chrome/common/extensions/api/runtime.json
@@ -6,6 +6,49 @@
   {
     "namespace": "runtime",
     "documentation_permissions_required": ["runtime"],
+    "properties": {
+      "lastError": {
+        "type": "object",
+        "optional": true,
+        "description": "This will be defined during an API method callback if there was an error",
+        "properties": {
+          "message": {
+            "optional": true,
+            "type": "string",
+            "description": "Details about the error which occurred."
+          }
+        }
+      }
+    },
+    "types": [
+      {
+        "id": "ManifestDetails",
+        "description": "Details from the manifest for the current extension/app.",
+        "type": "object",
+        "properties": {
+          "name": {
+            "type": "string",
+            "description": "The name of the extension/app"
+          },
+          "version": {
+            "type":"string",
+            "description": "The version of the extension/app"
+          },
+          "manifest_version": {
+            "optional": true,
+            "type": "integer"
+          },
+          "permissions": {
+            "optional": true,
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          }
+        },
+        "additionalProperties": { "type": "any" }
+      }
+    ],
     "functions": [
       {
         "name": "getBackgroundPage",
@@ -29,6 +72,33 @@
             ]
           }
         ]
+      },
+      {
+        "name": "getManifest",
+        "description": "Returns details about the app or extension from the manifest",
+        "type": "function",
+        "parameters": [],
+        "returns": {
+          "$ref": "ManifestDetails",
+          "description": "The manifest details."
+        }
+      },
+      {
+        "name": "getURL",
+        "type": "function",
+        "unprivileged": true,
+        "description": "Converts a relative path within an app/extension install directory to a fully-qualified URL.",
+        "parameters": [
+          {
+            "type": "string",
+            "name": "path",
+            "description": "A path to a resource within an app/extension expressed relative to its install directory."
+          }
+        ],
+        "returns": {
+          "type": "string",
+          "description": "The fully-qualified URL to the resource."
+        }
       }
     ],
     "events": [
diff --git a/chrome/common/extensions/docs/extensions/runtime.html b/chrome/common/extensions/docs/extensions/runtime.html
index 8e07141..381ab2e 100644
--- a/chrome/common/extensions/docs/extensions/runtime.html
+++ b/chrome/common/extensions/docs/extensions/runtime.html
@@ -200,6 +200,10 @@
             <ol>
               <li>
                 <a href="#method-getBackgroundPage">getBackgroundPage</a>
+              </li><li>
+                <a href="#method-getManifest">getManifest</a>
+              </li><li>
+                <a href="#method-getURL">getURL</a>
               </li>
             </ol>
           </li>
@@ -213,6 +217,16 @@
               </li>
             </ol>
           </li>
+                  <li>
+            <a href="#types">Types</a>
+            <ol>
+              <li>
+                <a href="#type-runtime.ManifestDetails">ManifestDetails</a>
+                <ol>
+                </ol>
+              </li>
+            </ol>
+          </li>
                 </ol>
               </li>
           </ol>
@@ -326,6 +340,115 @@
                 </div>
                 <!-- MIN_VERSION -->
               </div> <!-- /description -->
+            </div><div class="apiItem">
+              <a name="method-getManifest"></a> <!-- method-anchor -->
+              <h4>getManifest</h4>
+              <div class="summary"><span>runtime.ManifestDetails</span>
+                  <!-- Note: intentionally longer 80 columns -->
+                  <span>chrome.runtime.getManifest</span>()</div>
+              <div class="description">
+                <p>Returns details about the app or extension from the manifest</p>
+                <!-- PARAMETERS -->
+                <dl>
+                </dl>
+                <!-- RETURNS -->
+                <h4>Returns</h4>
+                <dl>
+                  <div>
+                    <div>
+          <dt>
+              <em>
+                <!-- TYPE -->
+                <div style="display:inline">
+                  (
+                    <span class="optional">optional</span>
+                    <span id="typeTemplate">
+                      <span>
+                        <a href="runtime.html#type-runtime.ManifestDetails">runtime.ManifestDetails</a>
+                      </span>
+                    </span>
+                  )
+                </div>
+              </em>
+          </dt>
+          <dd>The manifest details.</dd>
+          <!-- OBJECT PROPERTIES -->
+          <!-- OBJECT METHODS -->
+          <!-- OBJECT EVENT FIELDS -->
+          <!-- FUNCTION PARAMETERS -->
+        </div>
+                  </div>
+                </dl>
+                <!-- CALLBACK -->
+                <!-- MIN_VERSION -->
+              </div> <!-- /description -->
+            </div><div class="apiItem">
+              <a name="method-getURL"></a> <!-- method-anchor -->
+              <h4>getURL</h4>
+              <div class="summary"><span>string</span>
+                  <!-- Note: intentionally longer 80 columns -->
+                  <span>chrome.runtime.getURL</span>(<span class="null"><span>string</span>
+                      <var><span>path</span></var></span>)</div>
+              <div class="description">
+                <p>Converts a relative path within an app/extension install directory to a fully-qualified URL.</p>
+                <!-- PARAMETERS -->
+                <h4>Parameters</h4>
+                <dl>
+                  <div>
+                    <div>
+          <dt>
+            <var>path</var>
+              <em>
+                <!-- TYPE -->
+                <div style="display:inline">
+                  (
+                    <span id="typeTemplate">
+                      <span>
+                        <span>string</span>
+                      </span>
+                    </span>
+                  )
+                </div>
+              </em>
+          </dt>
+          <dd>A path to a resource within an app/extension expressed relative to its install directory.</dd>
+          <!-- OBJECT PROPERTIES -->
+          <!-- OBJECT METHODS -->
+          <!-- OBJECT EVENT FIELDS -->
+          <!-- FUNCTION PARAMETERS -->
+        </div>
+                  </div>
+                </dl>
+                <!-- RETURNS -->
+                <h4>Returns</h4>
+                <dl>
+                  <div>
+                    <div>
+          <dt>
+              <em>
+                <!-- TYPE -->
+                <div style="display:inline">
+                  (
+                    <span id="typeTemplate">
+                      <span>
+                        <span>string</span>
+                      </span>
+                    </span>
+                  )
+                </div>
+              </em>
+          </dt>
+          <dd>The fully-qualified URL to the resource.</dd>
+          <!-- OBJECT PROPERTIES -->
+          <!-- OBJECT METHODS -->
+          <!-- OBJECT EVENT FIELDS -->
+          <!-- FUNCTION PARAMETERS -->
+        </div>
+                  </div>
+                </dl>
+                <!-- CALLBACK -->
+                <!-- MIN_VERSION -->
+              </div> <!-- /description -->
             </div>  <!-- /apiItem -->
           </div>  <!-- /apiGroup -->
           <!-- EVENTS -->
@@ -368,7 +491,147 @@
             </div> <!-- /apiItem -->
           </div> <!-- /apiGroup -->
           <!-- TYPES -->
-           <!-- /apiGroup -->
+          <div class="apiGroup">
+            <a name="types"></a>
+            <h3 id="types">Types</h3>
+            <!-- iterates over all types -->
+            <div class="apiItem">
+              <a name="type-runtime.ManifestDetails"></a>
+              <h4>runtime.ManifestDetails</h4>
+              <div>
+          <dt>
+              <em>
+                <!-- TYPE -->
+                <div style="display:inline">
+                  (
+                    <span id="typeTemplate">
+                      <span>
+                        <span>object</span>
+                      </span>
+                    </span>
+                  )
+                </div>
+              </em>
+          </dt>
+          <dd>Details from the manifest for the current extension/app.</dd>
+          <!-- OBJECT PROPERTIES -->
+          <dd>
+            <dl>
+              <div>
+                <div>
+          <dt>
+            <var>name</var>
+              <em>
+                <!-- TYPE -->
+                <div style="display:inline">
+                  (
+                    <span id="typeTemplate">
+                      <span>
+                        <span>string</span>
+                      </span>
+                    </span>
+                  )
+                </div>
+              </em>
+          </dt>
+          <dd>The name of the extension/app</dd>
+          <!-- OBJECT PROPERTIES -->
+          <!-- OBJECT METHODS -->
+          <!-- OBJECT EVENT FIELDS -->
+          <!-- FUNCTION PARAMETERS -->
+        </div>
+              </div><div>
+                <div>
+          <dt>
+            <var>version</var>
+              <em>
+                <!-- TYPE -->
+                <div style="display:inline">
+                  (
+                    <span id="typeTemplate">
+                      <span>
+                        <span>string</span>
+                      </span>
+                    </span>
+                  )
+                </div>
+              </em>
+          </dt>
+          <dd class="todo">
+            Undocumented.
+          </dd>
+          <!-- OBJECT PROPERTIES -->
+          <!-- OBJECT METHODS -->
+          <!-- OBJECT EVENT FIELDS -->
+          <!-- FUNCTION PARAMETERS -->
+        </div>
+              </div><div>
+                <div>
+          <dt>
+            <var>manifest_version</var>
+              <em>
+                <!-- TYPE -->
+                <div style="display:inline">
+                  (
+                    <span class="optional">optional</span>
+                    <span id="typeTemplate">
+                      <span>
+                        <span>integer</span>
+                      </span>
+                    </span>
+                  )
+                </div>
+              </em>
+          </dt>
+          <dd class="todo">
+            Undocumented.
+          </dd>
+          <!-- OBJECT PROPERTIES -->
+          <!-- OBJECT METHODS -->
+          <!-- OBJECT EVENT FIELDS -->
+          <!-- FUNCTION PARAMETERS -->
+        </div>
+              </div><div>
+                <div>
+          <dt>
+            <var>permissions</var>
+              <em>
+                <!-- TYPE -->
+                <div style="display:inline">
+                  (
+                    <span class="optional">optional</span>
+                    <span id="typeTemplate">
+                      <span>
+                        <span>
+                          array of <span><span>
+                      <span>
+                        <span>any</span>
+                      </span>
+                    </span></span>
+                        </span>
+                      </span>
+                    </span>
+                  )
+                </div>
+              </em>
+          </dt>
+          <dd class="todo">
+            Undocumented.
+          </dd>
+          <!-- OBJECT PROPERTIES -->
+          <!-- OBJECT METHODS -->
+          <!-- OBJECT EVENT FIELDS -->
+          <!-- FUNCTION PARAMETERS -->
+        </div>
+              </div>
+            </dl>
+          </dd>
+          <!-- OBJECT METHODS -->
+          <!-- OBJECT EVENT FIELDS -->
+          <!-- FUNCTION PARAMETERS -->
+        </div>
+            </div> <!-- /apiItem -->
+          </div> <!-- /apiGroup -->
         </div> <!-- /apiPage -->
       </div> <!-- /gc-pagecontent -->
     </div> <!-- /g-section -->
diff --git a/chrome/common/extensions/docs/samples.json b/chrome/common/extensions/docs/samples.json
index 8c622d7..c7d1bcc 100644
--- a/chrome/common/extensions/docs/samples.json
+++ b/chrome/common/extensions/docs/samples.json
@@ -308,6 +308,8 @@
     "chrome.permissions.request": "permissions.html#method-request",
     "chrome.proxy.onProxyError": "proxy.html#event-onProxyError",
     "chrome.runtime.getBackgroundPage": "runtime.html#method-getBackgroundPage",
+    "chrome.runtime.getManifest": "runtime.html#method-getManifest",
+    "chrome.runtime.getURL": "runtime.html#method-getURL",
     "chrome.runtime.onInstalled": "runtime.html#event-onInstalled",
     "chrome.runtime.onSuspend": "runtime.html#event-onSuspend",
     "chrome.scriptBadge.getPopup": "scriptBadge.html#method-getPopup",
diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc
index a7da8601..56d5c89 100644
--- a/chrome/renderer/extensions/extension_dispatcher.cc
+++ b/chrome/renderer/extensions/extension_dispatcher.cc
@@ -39,6 +39,7 @@
 #include "chrome/renderer/extensions/miscellaneous_bindings.h"
 #include "chrome/renderer/extensions/page_actions_custom_bindings.h"
 #include "chrome/renderer/extensions/page_capture_custom_bindings.h"
+#include "chrome/renderer/extensions/runtime_custom_bindings.h"
 #include "chrome/renderer/extensions/send_request_natives.h"
 #include "chrome/renderer/extensions/set_icon_natives.h"
 #include "chrome/renderer/extensions/tab_finder.h"
@@ -94,6 +95,7 @@
 using extensions::PageActionsCustomBindings;
 using extensions::PageCaptureCustomBindings;
 using extensions::PermissionSet;
+using extensions::RuntimeCustomBindings;
 using extensions::SendRequestNatives;
 using extensions::SetIconNatives;
 using extensions::TTSCustomBindings;
@@ -536,6 +538,8 @@
           new PageActionsCustomBindings(this)));
   module_system->RegisterNativeHandler("page_capture",
       scoped_ptr<NativeHandler>(new PageCaptureCustomBindings()));
+  module_system->RegisterNativeHandler("runtime",
+      scoped_ptr<NativeHandler>(new RuntimeCustomBindings(context)));
   module_system->RegisterNativeHandler("tabs",
       scoped_ptr<NativeHandler>(new TabsCustomBindings()));
   module_system->RegisterNativeHandler("tts",
@@ -556,6 +560,7 @@
   source_map_.RegisterSource("apitest", IDR_EXTENSION_APITEST_JS);
 
   // Libraries.
+  source_map_.RegisterSource("lastError", IDR_LAST_ERROR_JS);
   source_map_.RegisterSource("schemaUtils", IDR_SCHEMA_UTILS_JS);
   source_map_.RegisterSource("sendRequest", IDR_SEND_REQUEST_JS);
   source_map_.RegisterSource("setIcon", IDR_SET_ICON_JS);
@@ -707,7 +712,9 @@
     case Feature::BLESSED_EXTENSION_CONTEXT:
     case Feature::UNBLESSED_EXTENSION_CONTEXT:
     case Feature::CONTENT_SCRIPT_CONTEXT: {
-      module_system->Require("miscellaneous_bindings");
+      CHECK(extension);
+      if (!extension->is_platform_app())
+        module_system->Require("miscellaneous_bindings");
       module_system->Require("schema_generated_bindings");
       module_system->Require("apitest");
 
@@ -725,8 +732,7 @@
     }
   }
 
-  // Inject custom JS into the platform app context to block certain features
-  // of the document and window.
+  // Inject custom JS into the platform app context.
   if (IsWithinPlatformApp(frame))
     module_system->Require("platformApp");
 
diff --git a/chrome/renderer/extensions/runtime_custom_bindings.cc b/chrome/renderer/extensions/runtime_custom_bindings.cc
new file mode 100644
index 0000000..abc15a1
--- /dev/null
+++ b/chrome/renderer/extensions/runtime_custom_bindings.cc
@@ -0,0 +1,37 @@
+// 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/renderer/extensions/runtime_custom_bindings.h"
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/manifest.h"
+#include "chrome/renderer/extensions/extension_dispatcher.h"
+#include "chrome/renderer/extensions/chrome_v8_context.h"
+#include "content/public/renderer/v8_value_converter.h"
+
+using content::V8ValueConverter;
+
+namespace extensions {
+
+RuntimeCustomBindings::RuntimeCustomBindings(ChromeV8Context* context)
+    : ChromeV8Extension(NULL), context_(context) {
+  RouteFunction("GetManifest",
+      base::Bind(&RuntimeCustomBindings::GetManifest, base::Unretained(this)));
+}
+
+RuntimeCustomBindings::~RuntimeCustomBindings() {}
+
+v8::Handle<v8::Value> RuntimeCustomBindings::GetManifest(
+    const v8::Arguments& args) {
+  CHECK(context_->extension());
+
+  scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+  return converter->ToV8Value(context_->extension()->manifest()->value(),
+                              context_->v8_context());
+}
+
+}  // extensions
diff --git a/chrome/renderer/extensions/runtime_custom_bindings.h b/chrome/renderer/extensions/runtime_custom_bindings.h
new file mode 100644
index 0000000..56bf98e
--- /dev/null
+++ b/chrome/renderer/extensions/runtime_custom_bindings.h
@@ -0,0 +1,33 @@
+// 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_RENDERER_EXTENSIONS_RUNTIME_CUSTOM_BINDINGS_H_
+#define CHROME_RENDERER_EXTENSIONS_RUNTIME_CUSTOM_BINDINGS_H_
+#pragma once
+
+#include "base/compiler_specific.h"
+#include "chrome/renderer/extensions/chrome_v8_extension.h"
+#include "v8/include/v8.h"
+
+class ExtensionDispatcher;
+class ChromeV8Context;
+
+namespace extensions {
+
+// The native component of custom bindings for the chrome.runtime API.
+class RuntimeCustomBindings : public ChromeV8Extension {
+ public:
+  explicit RuntimeCustomBindings(ChromeV8Context* context);
+
+  virtual ~RuntimeCustomBindings();
+
+ private:
+  v8::Handle<v8::Value> GetManifest(const v8::Arguments& args);
+
+  ChromeV8Context* context_;
+};
+
+}  // extensions
+
+#endif  // CHROME_RENDERER_EXTENSIONS_RUNTIME_CUSTOM_BINDINGS_H_
diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd
index eb63855..3d90611 100644
--- a/chrome/renderer/renderer_resources.grd
+++ b/chrome/renderer/renderer_resources.grd
@@ -28,6 +28,7 @@
       <include name="IDR_SCHEMA_GENERATED_BINDINGS_JS" file="resources\extensions\schema_generated_bindings.js" type="BINDATA" />
 
       <!-- Libraries. -->
+      <include name="IDR_LAST_ERROR_JS" file="resources\extensions\last_error.js" type="BINDATA" />
       <include name="IDR_SCHEMA_UTILS_JS" file="resources\extensions\schema_utils.js" type="BINDATA" />
       <include name="IDR_SEND_REQUEST_JS" file="resources\extensions\send_request.js" type="BINDATA" />
       <include name="IDR_SET_ICON_JS" file="resources\extensions\set_icon.js" type="BINDATA" />
diff --git a/chrome/renderer/resources/extensions/apitest.js b/chrome/renderer/resources/extensions/apitest.js
index 2f36dee..a29f0979 100644
--- a/chrome/renderer/resources/extensions/apitest.js
+++ b/chrome/renderer/resources/extensions/apitest.js
@@ -183,9 +183,9 @@
   };
 
   chrome.test.assertNoLastError = function() {
-    if (chrome.extension.lastError != undefined) {
+    if (chrome.runtime.lastError != undefined) {
       chrome.test.fail("lastError.message == " +
-                       chrome.extension.lastError.message);
+                       chrome.runtime.lastError.message);
     }
   };
 
@@ -212,9 +212,9 @@
         chrome.test.assertNoLastError();
       } else {
         chrome.test.assertEq(typeof(expectedError), 'string');
-        chrome.test.assertTrue(chrome.extension.lastError != undefined,
+        chrome.test.assertTrue(chrome.runtime.lastError != undefined,
             "No lastError, but expected " + expectedError);
-        chrome.test.assertEq(expectedError, chrome.extension.lastError.message);
+        chrome.test.assertEq(expectedError, chrome.runtime.lastError.message);
       }
 
       if (func) {
diff --git a/chrome/renderer/resources/extensions/context_menus_custom_bindings.js b/chrome/renderer/resources/extensions/context_menus_custom_bindings.js
index c4e48bf..9e4016a 100644
--- a/chrome/renderer/resources/extensions/context_menus_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/context_menus_custom_bindings.js
@@ -56,7 +56,7 @@
   });
 
   apiFunctions.setCustomCallback('create', function(name, request, response) {
-    if (chrome.extension.lastError) {
+    if (chrome.runtime.lastError) {
       return;
     }
 
@@ -72,7 +72,7 @@
   });
 
   apiFunctions.setCustomCallback('remove', function(name, request, response) {
-    if (chrome.extension.lastError) {
+    if (chrome.runtime.lastError) {
       return;
     }
     var id = request.args[0];
@@ -80,7 +80,7 @@
   });
 
   apiFunctions.setCustomCallback('update', function(name, request, response) {
-    if (chrome.extension.lastError) {
+    if (chrome.runtime.lastError) {
       return;
     }
     var id = request.args[0];
@@ -91,7 +91,7 @@
 
   apiFunctions.setCustomCallback('removeAll',
                                  function(name, request, response) {
-    if (chrome.extension.lastError) {
+    if (chrome.runtime.lastError) {
       return;
     }
     chromeHidden.contextMenus.generatedIdHandlers = {};
diff --git a/chrome/renderer/resources/extensions/file_system_custom_bindings.js b/chrome/renderer/resources/extensions/file_system_custom_bindings.js
index 548bedf..4248787 100644
--- a/chrome/renderer/resources/extensions/file_system_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_system_custom_bindings.js
@@ -7,6 +7,7 @@
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var fileSystemNatives = requireNative('file_system_natives');
 var GetIsolatedFileSystem = fileSystemNatives.GetIsolatedFileSystem;
+var lastError = require('lastError');
 
 chromeHidden.registerCustomHook('fileSystem', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
@@ -35,13 +36,11 @@
           fs.root.getFile(baseName, {}, function(fileEntry) {
             callback(fileEntry);
           }, function(fileError) {
-            chrome.extension.lastError = {"message":
-                'Error getting fileEntry, code: ' + fileError.code};
+            lastError.set('Error getting fileEntry, code: ' + fileError.code);
             callback();
           });
         } catch (e) {
-          chrome.extension.lastError = {"message":
-              'Error in event handler for onLaunched: ' + e.stack};
+          lastError.set('Error in event handler for onLaunched: ' + e.stack);
           callback();
         }
       }
diff --git a/chrome/renderer/resources/extensions/last_error.js b/chrome/renderer/resources/extensions/last_error.js
new file mode 100644
index 0000000..66f69f51
--- /dev/null
+++ b/chrome/renderer/resources/extensions/last_error.js
@@ -0,0 +1,19 @@
+// 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.
+
+function set(message) {
+  var errorObject = { "message": message };
+  if (chrome.extension)
+    chrome.extension.lastError = errorObject;
+  chrome.runtime.lastError = errorObject;
+};
+
+function clear() {
+  if (chrome.extension)
+    delete chrome.extension.lastError;
+  delete chrome.runtime.lastError;
+};
+
+exports.clear = clear;
+exports.set = set;
diff --git a/chrome/renderer/resources/extensions/miscellaneous_bindings.js b/chrome/renderer/resources/extensions/miscellaneous_bindings.js
index 1c07de7..7a806055b 100644
--- a/chrome/renderer/resources/extensions/miscellaneous_bindings.js
+++ b/chrome/renderer/resources/extensions/miscellaneous_bindings.js
@@ -9,6 +9,7 @@
 
   require('json_schema');
   require('event_bindings');
+  var lastError = requireNative('lastError');
   var miscNatives = requireNative('miscellaneous_bindings');
   var CloseChannel = miscNatives.CloseChannel;
   var PortAddRef = miscNatives.PortAddRef;
@@ -116,7 +117,7 @@
     if (sourceExtensionId != targetExtensionId)
       errorMsg += " for extension " + targetExtensionId;
     errorMsg += ").";
-    chrome.extension.lastError = {"message": errorMsg};
+    lastError.set(errorMsg);
     console.error("Could not send response: " + errorMsg);
   }
 
@@ -228,14 +229,14 @@
       if (connectionInvalid) {
         var errorMsg =
             "Could not establish connection. Receiving end does not exist.";
-        chrome.extension.lastError = {"message": errorMsg};
+        lastError.set(errorMsg);
         console.error("Port error: " + errorMsg);
       }
       try {
         port.onDisconnect.dispatch(port);
       } finally {
         port.destroy_();
-        delete chrome.extension.lastError;
+        lastError.clear();
       }
     }
   };
@@ -272,7 +273,7 @@
     port.onDisconnect.addListener(function() {
       // For onDisconnects, we only notify the callback if there was an error
       try {
-        if (chrome.extension.lastError)
+        if (chrome.runtime.lastError)
           responseCallback();
       } finally {
         port = null;
diff --git a/chrome/renderer/resources/extensions/runtime_custom_bindings.js b/chrome/renderer/resources/extensions/runtime_custom_bindings.js
index 8fb713fe..3c4c28e 100644
--- a/chrome/renderer/resources/extensions/runtime_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/runtime_custom_bindings.js
@@ -4,12 +4,12 @@
 
 // Custom bindings for the runtime API.
 
+var runtimeNatives = requireNative('runtime');
 var extensionNatives = requireNative('extension');
 var GetExtensionViews = extensionNatives.GetExtensionViews;
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var sendRequest = require('sendRequest').sendRequest;
 
-chromeHidden.registerCustomHook('runtime', function(bindingsAPI) {
+chromeHidden.registerCustomHook('runtime', function(bindingsAPI, extensionId) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setCustomCallback('getBackgroundPage',
@@ -20,4 +20,16 @@
     }
     request.callback = null;
   });
+
+  apiFunctions.setHandleRequest('getManifest', function() {
+    return runtimeNatives.GetManifest();
+  });
+
+  apiFunctions.setHandleRequest('getURL', function(path) {
+    path = String(path);
+    if (!path.length || path[0] != '/')
+      path = '/' + path;
+    return 'chrome-extension://' + extensionId + path;
+  });
+
 });
diff --git a/chrome/renderer/resources/extensions/send_request.js b/chrome/renderer/resources/extensions/send_request.js
index abb582a..2ea6545d 100644
--- a/chrome/renderer/resources/extensions/send_request.js
+++ b/chrome/renderer/resources/extensions/send_request.js
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
+var lastError = require('lastError');
 var natives = requireNative('sendRequest');
 var validate = require('schemaUtils').validate;
 
@@ -13,15 +14,13 @@
   try {
     var request = requests[requestId];
     if (success) {
-      delete chrome.extension.lastError;
+      lastError.clear();
     } else {
       if (!error) {
         error = "Unknown error.";
       }
       console.error("Error during " + name + ": " + error);
-      chrome.extension.lastError = {
-        "message": error
-      };
+      lastError.set(error);
     }
 
     if (request.customCallback) {
@@ -60,7 +59,7 @@
     }
   } finally {
     delete requests[requestId];
-    delete chrome.extension.lastError;
+    lastError.clear();
   }
 
   return undefined;
diff --git a/chrome/test/data/extensions/api_test/runtime/manifest.json b/chrome/test/data/extensions/api_test/runtime/manifest.json
new file mode 100644
index 0000000..086f0890
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/runtime/manifest.json
@@ -0,0 +1,8 @@
+{
+  "name": "chrome.runtime API Test",
+  "version": "1",
+  "manifest_version": 2,
+  "background": {
+    "scripts": ["test.js"]
+  }
+}
diff --git a/chrome/test/data/extensions/api_test/runtime/test.js b/chrome/test/data/extensions/api_test/runtime/test.js
new file mode 100644
index 0000000..74de48c
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/runtime/test.js
@@ -0,0 +1,38 @@
+// 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.
+
+var assertEq = chrome.test.assertEq;
+var fail = chrome.test.fail;
+var succeed = chrome.test.succeed;
+
+chrome.test.runTests([
+
+  function testGetURL() {
+    if (!chrome.runtime || !chrome.runtime.getURL) {
+      fail("chrome.runtime.getURL not defined");
+      return;
+    }
+    var url = chrome.runtime.getURL("_generated_background_page.html");
+    assertEq(url, window.location.href);
+    succeed();
+  },
+
+  function  testGetManifest() {
+    if (!chrome.runtime || !chrome.runtime.getManifest) {
+      fail("chrome.runtime.getManifest not defined");
+      return;
+    }
+    var manifest = chrome.runtime.getManifest();
+    if (!manifest || !manifest.background || !manifest.background.scripts) {
+      fail();
+      return;
+    }
+    assertEq(manifest.name, "chrome.runtime API Test");
+    assertEq(manifest.version, "1");
+    assertEq(manifest.manifest_version, 2);
+    assertEq(manifest.background.scripts, ["test.js"]);
+    succeed();
+  }
+
+]);
diff --git a/chrome/test/data/extensions/platform_apps/restrictions/main.js b/chrome/test/data/extensions/platform_apps/restrictions/main.js
index fb695c7..78d5873 100644
--- a/chrome/test/data/extensions/platform_apps/restrictions/main.js
+++ b/chrome/test/data/extensions/platform_apps/restrictions/main.js
@@ -150,5 +150,19 @@
     var iframe = document.createElement('iframe');
     iframe.src = 'sandboxed_iframe.html';
     document.body.appendChild(iframe);
+  },
+
+  function testLegacyApis() {
+    if (chrome.app) {
+      assertEq("undefined", typeof(chrome.app.getIsInstalled));
+      assertEq("undefined", typeof(chrome.app.install));
+      assertEq("undefined", typeof(chrome.app.isInstalled));
+      assertEq("undefined", typeof(chrome.app.getDetails));
+      assertEq("undefined", typeof(chrome.app.getDetailsForFrame));
+      assertEq("undefined", typeof(chrome.app.runningState));
+    }
+    assertEq("undefined", typeof(chrome.appNotifications));
+    assertEq("undefined", typeof(chrome.extension));
+    succeed();
   }
 ]);
diff --git a/chrome/test/data/extensions/platform_apps/restrictions/manifest.json b/chrome/test/data/extensions/platform_apps/restrictions/manifest.json
index 814775f2..65e676a 100644
--- a/chrome/test/data/extensions/platform_apps/restrictions/manifest.json
+++ b/chrome/test/data/extensions/platform_apps/restrictions/manifest.json
@@ -1,5 +1,5 @@
 {
-  "name": "Platform App Test: various document and window restrictions",
+  "name": "Platform App Test: various document, window, and API restrictions",
   "version": "1",
   "manifest_version": 2,
   "permissions": [