summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormihaip@chromium.org <mihaip@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-03 00:43:33 +0000
committermihaip@chromium.org <mihaip@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-03 00:43:33 +0000
commit8df666b5d0c0ff82ebcfeba28da4bcb1d364002e (patch)
tree4af2e4069efb2e39e54260668257a396a95a8903
parent93321cb3d5ee92edef64ba8b93f8cc2869dd0344 (diff)
downloadchromium_src-8df666b5d0c0ff82ebcfeba28da4bcb1d364002e.zip
chromium_src-8df666b5d0c0ff82ebcfeba28da4bcb1d364002e.tar.gz
chromium_src-8df666b5d0c0ff82ebcfeba28da4bcb1d364002e.tar.bz2
Shim that simulates a <browser> tag via Mutation Observers.
The actual tag is implemented via the browser plugin. The internals of this are hidden via Shadow DOM. Also fixes a bug with Shadow DOM not being enabled for all extensions views. BUG=133379 Review URL: https://chromiumcodereview.appspot.com/10598006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@145229 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/platform_app_browsertest.cc4
-rw-r--r--chrome/browser/extensions/shadow_dom_apitest.cc15
-rw-r--r--chrome/chrome_renderer.gypi1
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/extensions/api/_permission_features.json4
-rw-r--r--chrome/common/extensions/permissions/api_permission.cc1
-rw-r--r--chrome/common/extensions/permissions/api_permission.h1
-rw-r--r--chrome/common/extensions/permissions/permission_set_unittest.cc1
-rw-r--r--chrome/renderer/chrome_render_view_observer.cc42
-rw-r--r--chrome/renderer/chrome_render_view_observer.h7
-rw-r--r--chrome/renderer/extensions/extension_dispatcher.cc13
-rw-r--r--chrome/renderer/renderer_resources.grd9
-rw-r--r--chrome/renderer/resources/extensions/browser_tag.js90
-rw-r--r--chrome/renderer/resources/extensions/platform_app.css4
-rw-r--r--chrome/test/data/extensions/api_test/shadow_dom/background.js16
-rw-r--r--chrome/test/data/extensions/api_test/shadow_dom/empty.html8
-rw-r--r--chrome/test/data/extensions/api_test/shadow_dom/manifest.json9
-rw-r--r--chrome/test/data/extensions/platform_apps/browser_tag/main.html12
-rw-r--r--chrome/test/data/extensions/platform_apps/browser_tag/main.js41
-rw-r--r--chrome/test/data/extensions/platform_apps/browser_tag/manifest.json15
-rw-r--r--chrome/test/data/extensions/platform_apps/browser_tag/test.js7
21 files changed, 278 insertions, 23 deletions
diff --git a/chrome/browser/extensions/platform_app_browsertest.cc b/chrome/browser/extensions/platform_app_browsertest.cc
index 49f83f1..4aee2bf 100644
--- a/chrome/browser/extensions/platform_app_browsertest.cc
+++ b/chrome/browser/extensions/platform_app_browsertest.cc
@@ -278,3 +278,7 @@ IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, OpenLink) {
observer.Wait();
ASSERT_EQ(2, browser()->tab_count());
}
+
+IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, BrowserTag) {
+ ASSERT_TRUE(RunPlatformAppTest("platform_apps/browser_tag")) << message_;
+}
diff --git a/chrome/browser/extensions/shadow_dom_apitest.cc b/chrome/browser/extensions/shadow_dom_apitest.cc
new file mode 100644
index 0000000..f53d5d4
--- /dev/null
+++ b/chrome/browser/extensions/shadow_dom_apitest.cc
@@ -0,0 +1,15 @@
+// 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 "base/command_line.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/common/chrome_switches.h"
+
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ShadowDom) {
+ // Shadow DOM is exposed to extensions with the experimental permission only.
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalExtensionApis);
+
+ ASSERT_TRUE(RunExtensionTest("shadow_dom")) << message_;
+}
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi
index 1ca5ff2..1171d28 100644
--- a/chrome/chrome_renderer.gypi
+++ b/chrome/chrome_renderer.gypi
@@ -148,6 +148,7 @@
'renderer/resources/extensions/apitest.js',
'renderer/resources/extensions/app_custom_bindings.js',
'renderer/resources/extensions/browser_action_custom_bindings.js',
+ 'renderer/resources/extensions/browser_tag.js',
'renderer/resources/extensions/context_menus_custom_bindings.js',
'renderer/resources/extensions/declarative_webrequest_custom_bindings.js',
'renderer/resources/extensions/devtools_custom_bindings.js',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 167ad85..b41ecfa 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -2832,6 +2832,7 @@
'browser/extensions/plugin_apitest.cc',
'browser/extensions/process_management_browsertest.cc',
'browser/extensions/sandboxed_pages_apitest.cc',
+ 'browser/extensions/shadow_dom_apitest.cc',
'browser/extensions/settings/settings_apitest.cc',
'browser/extensions/stubs_apitest.cc',
'browser/extensions/subscribe_page_action_browsertest.cc',
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 8888066..34aefe3 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -35,6 +35,10 @@
"channel": "stable",
"extension_types": ["extension", "packaged_app"]
},
+ "browserTag": {
+ "channel": "dev",
+ "extension_types": ["extension", "platform_app"]
+ },
"browsingData": {
"channel": "stable",
"extension_types": ["extension", "packaged_app"]
diff --git a/chrome/common/extensions/permissions/api_permission.cc b/chrome/common/extensions/permissions/api_permission.cc
index 753b356..8d1b8ee 100644
--- a/chrome/common/extensions/permissions/api_permission.cc
+++ b/chrome/common/extensions/permissions/api_permission.cc
@@ -79,6 +79,7 @@ void APIPermission::RegisterAllPermissions(
{ kBookmark, "bookmarks", kFlagNone,
IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS,
PermissionMessage::kBookmarks },
+ { kBrowserTag, "browserTag", kFlagCannotBeOptional },
{ kBrowsingData, "browsingData" },
{ kContentSettings, "contentSettings", kFlagNone,
IDS_EXTENSION_PROMPT_WARNING_CONTENT_SETTINGS,
diff --git a/chrome/common/extensions/permissions/api_permission.h b/chrome/common/extensions/permissions/api_permission.h
index 917833a..c3132b7 100644
--- a/chrome/common/extensions/permissions/api_permission.h
+++ b/chrome/common/extensions/permissions/api_permission.h
@@ -31,6 +31,7 @@ class APIPermission {
kAudioCapture,
kBackground,
kBookmark,
+ kBrowserTag,
kBrowsingData,
kChromeAuthPrivate,
kChromeosInfoPrivate,
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index a4ec251..2f0a1c7 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -578,6 +578,7 @@ TEST(PermissionsTest, PermissionMessages) {
skip.insert(APIPermission::kAlarms);
skip.insert(APIPermission::kAppNotifications);
skip.insert(APIPermission::kAppWindow);
+ skip.insert(APIPermission::kBrowserTag);
skip.insert(APIPermission::kBrowsingData);
skip.insert(APIPermission::kContextMenus);
skip.insert(APIPermission::kDeclarative);
diff --git a/chrome/renderer/chrome_render_view_observer.cc b/chrome/renderer/chrome_render_view_observer.cc
index 3958597..5093e43 100644
--- a/chrome/renderer/chrome_render_view_observer.cc
+++ b/chrome/renderer/chrome_render_view_observer.cc
@@ -70,6 +70,7 @@ using WebKit::WebURL;
using WebKit::WebURLRequest;
using WebKit::WebView;
using WebKit::WebVector;
+using extensions::APIPermission;
using webkit_glue::ImageResourceFetcher;
// Delay in milliseconds that we'll wait before capturing the page contents
@@ -482,31 +483,40 @@ bool ChromeRenderViewObserver::allowWriteToClipboard(WebFrame* frame,
return allowed;
}
-bool ChromeRenderViewObserver::IsExperimentalWebFeatureAllowed(
- const WebDocument& document) {
- // Experimental Web API is enabled when
- // - The specific API is allowed from command line flag, or
- // - If the document is running extensions or apps which
- // has the "experimental" permission, or
- // - The document is running Web UI.
- WebSecurityOrigin origin = document.securityOrigin();
- if (EqualsASCII(origin.protocol(), chrome::kChromeUIScheme))
- return true;
+bool ChromeRenderViewObserver::HasExtensionPermission(
+ const WebSecurityOrigin& origin, APIPermission::ID permission) const {
+ if (!EqualsASCII(origin.protocol(), chrome::kExtensionScheme))
+ return false;
+
+ const std::string extension_id = origin.host().utf8().data();
+ if (!extension_dispatcher_->IsExtensionActive(extension_id))
+ return false;
+
const extensions::Extension* extension =
- extension_dispatcher_->extensions()->GetExtensionOrAppByURL(
- ExtensionURLInfo(origin, document.url()));
+ extension_dispatcher_->extensions()->GetByID(extension_id);
if (!extension)
return false;
- return (extension_dispatcher_->IsExtensionActive(extension->id()) &&
- extension->HasAPIPermission(
- extensions::APIPermission::kExperimental));
+
+ return extension->HasAPIPermission(permission);
}
bool ChromeRenderViewObserver::allowWebComponents(const WebDocument& document,
bool defaultValue) {
if (defaultValue)
return true;
- return IsExperimentalWebFeatureAllowed(document);
+
+ WebSecurityOrigin origin = document.securityOrigin();
+ if (EqualsASCII(origin.protocol(), chrome::kChromeUIScheme))
+ return true;
+
+ // The <browser> tag is implemented via Shadow DOM.
+ if (HasExtensionPermission(origin, APIPermission::kBrowserTag))
+ return true;
+
+ if (HasExtensionPermission(origin, APIPermission::kExperimental))
+ return true;
+
+ return false;
}
static void SendInsecureContentSignal(int signal) {
diff --git a/chrome/renderer/chrome_render_view_observer.h b/chrome/renderer/chrome_render_view_observer.h
index 57a28a8..ac48adf 100644
--- a/chrome/renderer/chrome_render_view_observer.h
+++ b/chrome/renderer/chrome_render_view_observer.h
@@ -13,6 +13,7 @@
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "base/timer.h"
+#include "chrome/common/extensions/permissions/api_permission.h"
#include "content/public/renderer/render_view_observer.h"
#include "googleurl/src/gurl.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPermissionClient.h"
@@ -183,8 +184,10 @@ class ChromeRenderViewObserver : public content::RenderViewObserver,
// Determines if a host is in the strict security host set.
bool IsStrictSecurityHost(const std::string& host);
- // Determines if the document has a permission to use experimental Web API
- bool IsExperimentalWebFeatureAllowed(const WebKit::WebDocument& document);
+ // Checks if |origin| correponds to an installed extension that has been
+ // granted the |permission|.
+ bool HasExtensionPermission(const WebKit::WebSecurityOrigin& origin,
+ extensions::APIPermission::ID permission) const;
// Save the JavaScript to preload if a ViewMsg_WebUIJavaScript is received.
scoped_ptr<WebUIJavaScript> webui_javascript_;
diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc
index 7ef396f..a7da860 100644
--- a/chrome/renderer/extensions/extension_dispatcher.cc
+++ b/chrome/renderer/extensions/extension_dispatcher.cc
@@ -75,6 +75,7 @@ using WebKit::WebVector;
using WebKit::WebView;
using content::RenderThread;
using content::RenderView;
+using extensions::APIPermission;
using extensions::ApiDefinitionsNatives;
using extensions::AppWindowCustomBindings;
using extensions::ContextMenusCustomBindings;
@@ -597,7 +598,6 @@ void ExtensionDispatcher::PopulateSourceMap() {
source_map_.RegisterSource("pageAction", IDR_PAGE_ACTION_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("pageCapture",
IDR_PAGE_CAPTURE_CUSTOM_BINDINGS_JS);
- source_map_.RegisterSource("platformApp", IDR_PLATFORM_APP_JS);
source_map_.RegisterSource("runtime", IDR_RUNTIME_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("storage", IDR_STORAGE_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("tabs", IDR_TABS_CUSTOM_BINDINGS_JS);
@@ -608,6 +608,10 @@ void ExtensionDispatcher::PopulateSourceMap() {
source_map_.RegisterSource("webRequestInternal",
IDR_WEB_REQUEST_INTERNAL_CUSTOM_BINDINGS_JS);
source_map_.RegisterSource("webstore", IDR_WEBSTORE_CUSTOM_BINDINGS_JS);
+
+ // Platform app sources that are not API-specific..
+ source_map_.RegisterSource("browserTag", IDR_BROWSER_TAG_JS);
+ source_map_.RegisterSource("platformApp", IDR_PLATFORM_APP_JS);
}
void ExtensionDispatcher::PopulateLazyBindingsMap() {
@@ -726,6 +730,11 @@ void ExtensionDispatcher::DidCreateScriptContext(
if (IsWithinPlatformApp(frame))
module_system->Require("platformApp");
+ if (context_type == Feature::BLESSED_EXTENSION_CONTEXT &&
+ extension && extension->HasAPIPermission(APIPermission::kBrowserTag)) {
+ module_system->Require("browserTag");
+ }
+
context->set_module_system(module_system.Pass());
int manifest_version = 1;
@@ -792,7 +801,7 @@ void ExtensionDispatcher::InitOriginPermissions(const Extension* extension) {
// TODO(jstritar): We should try to remove this special case. Also, these
// whitelist entries need to be updated when the kManagement permission
// changes.
- if (extension->HasAPIPermission(extensions::APIPermission::kManagement)) {
+ if (extension->HasAPIPermission(APIPermission::kManagement)) {
WebSecurityPolicy::addOriginAccessWhitelistEntry(
extension->url(),
WebString::fromUTF8(chrome::kChromeUIScheme),
diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd
index 85675bc..eb63855 100644
--- a/chrome/renderer/renderer_resources.grd
+++ b/chrome/renderer/renderer_resources.grd
@@ -24,8 +24,6 @@ without changes to the corresponding grd file. fb9 -->
<include name="IDR_JSON_SCHEMA_JS" file="resources\extensions\json_schema.js" type="BINDATA" />
<include name="IDR_MISCELLANEOUS_BINDINGS_JS" file="resources\extensions\miscellaneous_bindings.js" type="BINDATA" />
<include name="IDR_NET_ERROR_HTML" file="resources\neterror.html" flattenhtml="true" type="BINDATA" />
- <include name="IDR_PLATFORM_APP_CSS" file="resources\extensions\platform_app.css" type="BINDATA" />
- <include name="IDR_PLATFORM_APP_JS" file="resources\extensions\platform_app.js" type="BINDATA" />
<include name="IDR_SAD_PLUGIN" file="resources\sadplugin.png" type="BINDATA" />
<include name="IDR_SCHEMA_GENERATED_BINDINGS_JS" file="resources\extensions\schema_generated_bindings.js" type="BINDATA" />
@@ -35,11 +33,12 @@ without changes to the corresponding grd file. fb9 -->
<include name="IDR_SET_ICON_JS" file="resources\extensions\set_icon.js" type="BINDATA" />
<include name="IDR_UTILS_JS" file="resources\extensions\utils.js" type="BINDATA" />
- <!-- Custom bindings for extension APIs. -->
<if expr="pp_ifdef('enable_extensions')">
+ <!-- Custom bindings for extension APIs. -->
<include name="IDR_APP_CUSTOM_BINDINGS_JS" file="resources\extensions\app_custom_bindings.js" type="BINDATA" />
<include name="IDR_APP_WINDOW_CUSTOM_BINDINGS_JS" file="resources\extensions\app_window_custom_bindings.js" type="BINDATA" />
<include name="IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS" file="resources\extensions\browser_action_custom_bindings.js" type="BINDATA" />
+ <include name="IDR_BROWSER_TAG_JS" file="resources\extensions\browser_tag.js" type="BINDATA" />
<include name="IDR_CONTENT_SETTINGS_CUSTOM_BINDINGS_JS" file="resources\extensions\content_settings_custom_bindings.js" type="BINDATA" />
<include name="IDR_CONTEXT_MENUS_CUSTOM_BINDINGS_JS" file="resources\extensions\context_menus_custom_bindings.js" type="BINDATA" />
<include name="IDR_DECLARATIVE_WEBREQUEST_CUSTOM_BINDINGS_JS" file="resources\extensions\declarative_webrequest_custom_bindings.js" type="BINDATA" />
@@ -68,6 +67,10 @@ without changes to the corresponding grd file. fb9 -->
<include name="IDR_WEB_REQUEST_CUSTOM_BINDINGS_JS" file="resources\extensions\web_request_custom_bindings.js" type="BINDATA" />
<include name="IDR_WEB_REQUEST_INTERNAL_CUSTOM_BINDINGS_JS" file="resources\extensions\web_request_internal_custom_bindings.js" type="BINDATA" />
<include name="IDR_WEBSTORE_CUSTOM_BINDINGS_JS" file="resources\extensions\webstore_custom_bindings.js" type="BINDATA" />
+
+ <!-- Platform app support. -->
+ <include name="IDR_PLATFORM_APP_CSS" file="resources\extensions\platform_app.css" type="BINDATA" />
+ <include name="IDR_PLATFORM_APP_JS" file="resources\extensions\platform_app.js" type="BINDATA" />
</if>
</includes>
</release>
diff --git a/chrome/renderer/resources/extensions/browser_tag.js b/chrome/renderer/resources/extensions/browser_tag.js
new file mode 100644
index 0000000..796eb34
--- /dev/null
+++ b/chrome/renderer/resources/extensions/browser_tag.js
@@ -0,0 +1,90 @@
+// 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.
+
+// Shim that simulates a <browser> tag via Mutation Observers.
+//
+// The actual tag is implemented via the browser plugin. The internals of this
+// are hidden via Shadow DOM.
+
+var BROWSER_TAG_ATTRIBUTES = ['src', 'width', 'height'];
+
+// Handle <browser> tags already in the document.
+window.addEventListener('DOMContentLoaded', function() {
+ var browserNodes = document.body.querySelectorAll('browser');
+ for (var i = 0, browserNode; browserNode = browserNodes[i]; i++) {
+ new BrowserTag(browserNode);
+ }
+});
+
+// Handle <browser> tags added later.
+var observer = new WebKitMutationObserver(function(mutations) {
+ mutations.forEach(function(mutation) {
+ for (var i = 0, addedNode; addedNode = mutation.addedNodes[i]; i++) {
+ if (addedNode.tagName == 'BROWSER') {
+ new BrowserTag(addedNode);
+ }
+ }
+ });
+});
+observer.observe(document, {subtree: true, childList: true});
+
+/**
+ * @constructor
+ */
+function BrowserTag(node) {
+ this.node_ = node;
+ var shadowRoot = new WebKitShadowRoot(node);
+
+ this.objectNode_ = document.createElement('object');
+ this.objectNode_.type = 'application/browser-plugin';
+ BROWSER_TAG_ATTRIBUTES.forEach(this.copyAttribute_, this);
+ shadowRoot.appendChild(this.objectNode_);
+
+ // Map attribute modifications on the <browser> tag to changes on the
+ // underlying <object> node.
+ var handleMutation = this.handleMutation_.bind(this);
+ var observer = new WebKitMutationObserver(function(mutations) {
+ mutations.forEach(handleMutation);
+ });
+ observer.observe(
+ this.node_,
+ {attributes: true, attributeFilter: BROWSER_TAG_ATTRIBUTES});
+
+ // Expose getters and setters for the attributes.
+ BROWSER_TAG_ATTRIBUTES.forEach(function(attributeName) {
+ Object.defineProperty(this.node_, attributeName, {
+ get: function() {
+ var value = node.getAttribute(attributeName);
+ var numericValue = parseInt(value, 10);
+ return isNaN(numericValue) ? value : numericValue;
+ },
+ set: function(value) {
+ node.setAttribute(attributeName, value);
+ },
+ enumerable: true
+ });
+ }, this);
+};
+
+/**
+ * @private
+ */
+BrowserTag.prototype.handleMutation_ = function(mutation) {
+ switch (mutation.attributeName) {
+ case 'src':
+ this.objectNode_.postMessage(this.node_.getAttribute('src'));
+ break;
+ default:
+ this.copyAttribute_(mutation.attributeName);
+ break;
+ }
+};
+
+/**
+ * @private
+ */
+BrowserTag.prototype.copyAttribute_ = function(attributeName) {
+ this.objectNode_.setAttribute(
+ attributeName, this.node_.getAttribute(attributeName));
+};
diff --git a/chrome/renderer/resources/extensions/platform_app.css b/chrome/renderer/resources/extensions/platform_app.css
index 7976a85..6f26923 100644
--- a/chrome/renderer/resources/extensions/platform_app.css
+++ b/chrome/renderer/resources/extensions/platform_app.css
@@ -12,3 +12,7 @@ body {
-webkit-user-select: none;
cursor: default;
}
+
+browser {
+ display: inline-block;
+}
diff --git a/chrome/test/data/extensions/api_test/shadow_dom/background.js b/chrome/test/data/extensions/api_test/shadow_dom/background.js
new file mode 100644
index 0000000..e2e4428
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/shadow_dom/background.js
@@ -0,0 +1,16 @@
+// 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.
+
+chrome.test.runTests([
+ function currentWindow() {
+ chrome.test.assertTrue('WebKitShadowRoot' in window);
+ chrome.test.succeed();
+ },
+
+ function newWindow() {
+ var w = window.open('empty.html');
+ chrome.test.assertTrue('WebKitShadowRoot' in w);
+ chrome.test.succeed();
+ }
+]);
diff --git a/chrome/test/data/extensions/api_test/shadow_dom/empty.html b/chrome/test/data/extensions/api_test/shadow_dom/empty.html
new file mode 100644
index 0000000..1f1a5c18
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/shadow_dom/empty.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Just an empty window</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/chrome/test/data/extensions/api_test/shadow_dom/manifest.json b/chrome/test/data/extensions/api_test/shadow_dom/manifest.json
new file mode 100644
index 0000000..e77681b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/shadow_dom/manifest.json
@@ -0,0 +1,9 @@
+{
+ "name": "Shadow DOM API Test",
+ "version": "0.1",
+ "manifest_version": 2,
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": [ "experimental" ]
+}
diff --git a/chrome/test/data/extensions/platform_apps/browser_tag/main.html b/chrome/test/data/extensions/platform_apps/browser_tag/main.html
new file mode 100644
index 0000000..3b6711ec
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/browser_tag/main.html
@@ -0,0 +1,12 @@
+<!--
+ * 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.
+-->
+<html>
+<body>
+<browser src="data:text/html,hello world" width="300" height="200"></browser>
+
+<script src="main.js"></script>
+</body>
+</html>
diff --git a/chrome/test/data/extensions/platform_apps/browser_tag/main.js b/chrome/test/data/extensions/platform_apps/browser_tag/main.js
new file mode 100644
index 0000000..98a8642
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/browser_tag/main.js
@@ -0,0 +1,41 @@
+// 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.
+
+onload = function() {
+ chrome.test.runTests([
+ function browserTag() {
+ var browserTag = document.querySelector('browser');
+ // Since we can't currently inspect the page loaded inside the <browser>,
+ // the only way we can check that the shim is working is by changing the
+ // size and seeing if the shim updates the size of the DOM.
+ chrome.test.assertEq(300, browserTag.offsetWidth);
+ chrome.test.assertEq(200, browserTag.offsetHeight);
+
+ browserTag.setAttribute('width', 310);
+ browserTag.setAttribute('height', 210);
+
+ // Timeout is necessary to give the mutation observers a chance to fire.
+ setTimeout(function() {
+ chrome.test.assertEq(310, browserTag.offsetWidth);
+ chrome.test.assertEq(210, browserTag.offsetHeight);
+
+ // Should also be able to query/update the dimensions via getterts/
+ // setters.
+ chrome.test.assertEq(310, browserTag.width);
+ chrome.test.assertEq(210, browserTag.height);
+
+ browserTag.width = 320;
+ browserTag.height = 220;
+
+ // Setters also end up operating via mutation observers.
+ setTimeout(function() {
+ chrome.test.assertEq(320, browserTag.offsetWidth);
+ chrome.test.assertEq(220, browserTag.offsetHeight);
+
+ chrome.test.succeed();
+ }, 0);
+ }, 0);
+ }
+ ]);
+};
diff --git a/chrome/test/data/extensions/platform_apps/browser_tag/manifest.json b/chrome/test/data/extensions/platform_apps/browser_tag/manifest.json
new file mode 100644
index 0000000..660c0c9
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/browser_tag/manifest.json
@@ -0,0 +1,15 @@
+{
+ "name": "Platform App Test: <browser>",
+ "version": "1",
+ "manifest_version": 2,
+ "permissions": [
+ "experimental",
+ "appWindow",
+ "browserTag"
+ ],
+ "app": {
+ "background": {
+ "scripts": ["test.js"]
+ }
+ }
+}
diff --git a/chrome/test/data/extensions/platform_apps/browser_tag/test.js b/chrome/test/data/extensions/platform_apps/browser_tag/test.js
new file mode 100644
index 0000000..ce719f7
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/browser_tag/test.js
@@ -0,0 +1,7 @@
+// 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.
+
+chrome.experimental.app.onLaunched.addListener(function() {
+ chrome.appWindow.create('main.html', {}, function () {});
+});