diff options
-rw-r--r-- | chrome/browser/extensions/chrome_app_api_browsertest.cc | 174 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_browsertest.cc | 22 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_browsertest.h | 21 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_browsertests_misc.cc | 74 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_service.cc | 7 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_service.h | 2 | ||||
-rw-r--r-- | chrome/browser/renderer_host/browser_render_process_host.cc | 1 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 4 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 | ||||
-rw-r--r-- | chrome/renderer/extensions/chrome_app_bindings.cc | 122 | ||||
-rw-r--r-- | chrome/test/data/extensions/get_app_details_for_frame.html | 27 |
12 files changed, 352 insertions, 104 deletions
diff --git a/chrome/browser/extensions/chrome_app_api_browsertest.cc b/chrome/browser/extensions/chrome_app_api_browsertest.cc new file mode 100644 index 0000000..d34de44 --- /dev/null +++ b/chrome/browser/extensions/chrome_app_api_browsertest.cc @@ -0,0 +1,174 @@ +// 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 <string> + +#include "base/command_line.h" +#include "base/json/json_reader.h" +#include "base/scoped_ptr.h" +#include "base/string_number_conversions.h" +#include "base/values.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/test/ui_test_utils.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "googleurl/src/gurl.h" +#include "net/base/mock_host_resolver.h" + +class ChromeAppAPITest : public ExtensionBrowserTest { + private: + virtual void SetUpCommandLine(CommandLine* command_line) { + ExtensionBrowserTest::SetUpCommandLine(command_line); + command_line->AppendSwitchASCII(switches::kAppsCheckoutURL, + "http://checkout.com:"); + } +}; + +IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, IsInstalled) { + std::string app_host("app.com"); + std::string nonapp_host("nonapp.com"); + + host_resolver()->AddRule(app_host, "127.0.0.1"); + host_resolver()->AddRule(nonapp_host, "127.0.0.1"); + ASSERT_TRUE(test_server()->Start()); + + GURL test_file_url(test_server()->GetURL("extensions/test_file.html")); + GURL::Replacements replace_host; + + replace_host.SetHostStr(app_host); + GURL app_url(test_file_url.ReplaceComponents(replace_host)); + + replace_host.SetHostStr(nonapp_host); + GURL non_app_url(test_file_url.ReplaceComponents(replace_host)); + + + // Load an app which includes app.com in its extent. + const Extension* extension = LoadExtension( + test_data_dir_.AppendASCII("app_dot_com_app")); + ASSERT_TRUE(extension); + + + // Test that a non-app page has chrome.app.isInstalled = false. + ui_test_utils::NavigateToURL(browser(), non_app_url); + std::wstring get_app_is_installed = + L"window.domAutomationController.send(" + L" JSON.stringify(window.chrome.app.isInstalled));"; + std::string result; + ASSERT_TRUE( + ui_test_utils::ExecuteJavaScriptAndExtractString( + browser()->GetSelectedTabContents()->render_view_host(), + L"", get_app_is_installed, &result)); + EXPECT_EQ("false", result); + + // Test that a non-app page returns null for chrome.app.getDetails(). + std::wstring get_app_details = + L"window.domAutomationController.send(" + L" JSON.stringify(window.chrome.app.getDetails()));"; + ASSERT_TRUE( + ui_test_utils::ExecuteJavaScriptAndExtractString( + browser()->GetSelectedTabContents()->render_view_host(), + L"", get_app_details, &result)); + EXPECT_EQ("null", result); + + // Check that an app page has chrome.app.isInstalled = true. + ui_test_utils::NavigateToURL(browser(), app_url); + ASSERT_TRUE( + ui_test_utils::ExecuteJavaScriptAndExtractString( + browser()->GetSelectedTabContents()->render_view_host(), + L"", get_app_is_installed, &result)); + EXPECT_EQ("true", result); + + // Check that an app page returns the correct result for + // chrome.app.getDetails(). + ui_test_utils::NavigateToURL(browser(), app_url); + ASSERT_TRUE( + ui_test_utils::ExecuteJavaScriptAndExtractString( + browser()->GetSelectedTabContents()->render_view_host(), + L"", get_app_details, &result)); + scoped_ptr<DictionaryValue> app_details( + static_cast<DictionaryValue*>( + base::JSONReader::Read(result, false /* allow trailing comma */))); + // extension->manifest_value() does not contain the id. + app_details->Remove("id", NULL); + EXPECT_TRUE(app_details.get()); + EXPECT_TRUE(app_details->Equals(extension->manifest_value())); + + + // Test that trying to set window.chrome.app.isInstalled throws + // an exception. + ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString( + browser()->GetSelectedTabContents()->render_view_host(), + L"", + L"window.domAutomationController.send(" + L" function() {" + L" try {" + L" window.chrome.app.isInstalled = false;" + L" return 'BAD: Should have thrown by now...';" + L" } catch (e) {" + L" return 'GOOD: Saw expected error.';" + L" }" + L" }()" + L");", + &result)); + EXPECT_EQ("GOOD: Saw expected error.", result); +} + +IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, GetDetailsForFrame) { + std::string app_host("app.com"); + std::string nonapp_host("nonapp.com"); + std::string checkout_host("checkout.com"); + + host_resolver()->AddRule(app_host, "127.0.0.1"); + host_resolver()->AddRule(nonapp_host, "127.0.0.1"); + host_resolver()->AddRule(checkout_host, "127.0.0.1"); + ASSERT_TRUE(test_server()->Start()); + + GURL test_file_url(test_server()->GetURL( + "files/extensions/get_app_details_for_frame.html")); + GURL::Replacements replace_host; + + replace_host.SetHostStr(checkout_host); + GURL checkout_url(test_file_url.ReplaceComponents(replace_host)); + + replace_host.SetHostStr(app_host); + GURL app_url(test_file_url.ReplaceComponents(replace_host)); + + // Load an app which includes app.com in its extent. + const Extension* extension = LoadExtension( + test_data_dir_.AppendASCII("app_dot_com_app")); + ASSERT_TRUE(extension); + + // Test that normal pages (even apps) cannot use getDetailsForFrame(). + ui_test_utils::NavigateToURL(browser(), app_url); + std::wstring test_unsuccessful_access = + L"window.domAutomationController.send(window.testUnsuccessfulAccess())"; + bool result = false; + ASSERT_TRUE( + ui_test_utils::ExecuteJavaScriptAndExtractBool( + browser()->GetSelectedTabContents()->render_view_host(), + L"", test_unsuccessful_access, &result)); + EXPECT_TRUE(result); + + // Test that checkout can use getDetailsForFrame() and that it works + // correctly. + ui_test_utils::NavigateToURL(browser(), checkout_url); + std::wstring get_details_for_frame = + L"window.domAutomationController.send(" + L" JSON.stringify(chrome.app.getDetailsForFrame(frames[0])))"; + std::string json; + ASSERT_TRUE( + ui_test_utils::ExecuteJavaScriptAndExtractString( + browser()->GetSelectedTabContents()->render_view_host(), + L"", get_details_for_frame, &json)); + + scoped_ptr<DictionaryValue> app_details( + static_cast<DictionaryValue*>( + base::JSONReader::Read(json, false /* allow trailing comma */))); + // extension->manifest_value() does not contain the id. + app_details->Remove("id", NULL); + EXPECT_TRUE(app_details.get()); + EXPECT_TRUE(app_details->Equals(extension->manifest_value())); +} diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index a93ffe2..3f22a38 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc @@ -59,9 +59,8 @@ void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) { #endif } -bool ExtensionBrowserTest::LoadExtensionImpl(const FilePath& path, - bool incognito_enabled, - bool fileaccess_enabled) { +const Extension* ExtensionBrowserTest::LoadExtensionImpl( + const FilePath& path, bool incognito_enabled, bool fileaccess_enabled) { ExtensionService* service = browser()->profile()->GetExtensionService(); { NotificationRegistrar registrar; @@ -84,7 +83,7 @@ bool ExtensionBrowserTest::LoadExtensionImpl(const FilePath& path, } } if (!extension) - return false; + return NULL; // The call to OnExtensionInstalled ensures the other extension prefs // are set up with the defaults. @@ -93,22 +92,27 @@ bool ExtensionBrowserTest::LoadExtensionImpl(const FilePath& path, service->SetIsIncognitoEnabled(extension->id(), incognito_enabled); service->SetAllowFileAccess(extension, fileaccess_enabled); - return WaitForExtensionHostsToLoad(); + if (!WaitForExtensionHostsToLoad()) + return NULL; + + return extension; } -bool ExtensionBrowserTest::LoadExtension(const FilePath& path) { +const Extension* ExtensionBrowserTest::LoadExtension(const FilePath& path) { return LoadExtensionImpl(path, false, true); } -bool ExtensionBrowserTest::LoadExtensionIncognito(const FilePath& path) { +const Extension* ExtensionBrowserTest::LoadExtensionIncognito( + const FilePath& path) { return LoadExtensionImpl(path, true, true); } -bool ExtensionBrowserTest::LoadExtensionNoFileAccess(const FilePath& path) { +const Extension* ExtensionBrowserTest::LoadExtensionNoFileAccess( + const FilePath& path) { return LoadExtensionImpl(path, false, false); } -bool ExtensionBrowserTest::LoadExtensionIncognitoNoFileAccess( +const Extension* ExtensionBrowserTest::LoadExtensionIncognitoNoFileAccess( const FilePath& path) { return LoadExtensionImpl(path, true, false); } diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h index 2fda271..72ffa0c 100644 --- a/chrome/browser/extensions/extension_browsertest.h +++ b/chrome/browser/extensions/extension_browsertest.h @@ -16,6 +16,8 @@ #include "content/common/notification_observer.h" #include "content/common/notification_type.h" +class Extension; + // Base class for extension browser tests. Provides utilities for loading, // unloading, and installing extensions. class ExtensionBrowserTest @@ -24,20 +26,20 @@ class ExtensionBrowserTest ExtensionBrowserTest(); virtual void SetUpCommandLine(CommandLine* command_line); - bool LoadExtension(const FilePath& path); + const Extension* LoadExtension(const FilePath& path); // Same as above, but enables the extension in incognito mode first. - bool LoadExtensionIncognito(const FilePath& path); - - // Loads extension and imitates that it is a component extension. - bool LoadExtensionAsComponent(const FilePath& path); + const Extension* LoadExtensionIncognito(const FilePath& path); // By default, unpacked extensions have file access: this loads them with // that permission removed. - bool LoadExtensionNoFileAccess(const FilePath& path); + const Extension* LoadExtensionNoFileAccess(const FilePath& path); // Same as above, but enables the extension in incognito mode first. - bool LoadExtensionIncognitoNoFileAccess(const FilePath& path); + const Extension* LoadExtensionIncognitoNoFileAccess(const FilePath& path); + + // Loads extension and imitates that it is a component extension. + bool LoadExtensionAsComponent(const FilePath& path); // Pack the extension in |dir_path| into a crx file and return its path. // Return an empty FilePath if there were errors. @@ -142,8 +144,9 @@ class ExtensionBrowserTest InstallUIType ui_type, int expected_change, Profile* profile); - bool LoadExtensionImpl(const FilePath& path, bool incognito_enabled, - bool fileaccess_enabled); + const Extension* LoadExtensionImpl(const FilePath& path, + bool incognito_enabled, + bool fileaccess_enabled); bool WaitForExtensionHostsToLoad(); diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc index 16f0e80..5800fa3 100644 --- a/chrome/browser/extensions/extension_browsertests_misc.cc +++ b/chrome/browser/extensions/extension_browsertests_misc.cc @@ -26,7 +26,6 @@ #include "content/browser/renderer_host/render_view_host.h" #include "content/browser/site_instance.h" #include "content/browser/tab_contents/tab_contents.h" -#include "net/base/mock_host_resolver.h" #include "net/base/net_util.h" #include "net/test/test_server.h" @@ -831,71 +830,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, DISABLED_OptionsPage) { tab_strip->GetTabContentsAt(1)->tab_contents()->GetURL()); } -// Test window.chrome.app.isInstalled . -IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, PropertyAppIsInstalled) { - std::string app_host("app.com"); - std::string nonapp_host("nonapp.com"); - - host_resolver()->AddRule(app_host, "127.0.0.1"); - host_resolver()->AddRule(nonapp_host, "127.0.0.1"); - ASSERT_TRUE(test_server()->Start()); - - GURL test_file_url(test_server()->GetURL("extensions/test_file.html")); - GURL::Replacements replace_host; - - replace_host.SetHostStr(app_host); - GURL app_url(test_file_url.ReplaceComponents(replace_host)); - - replace_host.SetHostStr(nonapp_host); - GURL non_app_url(test_file_url.ReplaceComponents(replace_host)); - - - // Load an app which includes app.com in its extent. - ASSERT_TRUE(LoadExtension( - test_data_dir_.AppendASCII("app_dot_com_app"))); - - - // Test that a non-app page has chrome.app.isInstalled = false. - ui_test_utils::NavigateToURL(browser(), non_app_url); - std::wstring get_app_is_installed = - L"window.domAutomationController.send(" - L" JSON.stringify(window.chrome.app.isInstalled));"; - std::string result; - ASSERT_TRUE( - ui_test_utils::ExecuteJavaScriptAndExtractString( - browser()->GetSelectedTabContents()->render_view_host(), - L"", - get_app_is_installed.c_str(), - &result)); - EXPECT_EQ("false", result); - - - // Check that an app page has chrome.app.isInstalled = true. - ui_test_utils::NavigateToURL(browser(), app_url); - ASSERT_TRUE( - ui_test_utils::ExecuteJavaScriptAndExtractString( - browser()->GetSelectedTabContents()->render_view_host(), - L"", - get_app_is_installed.c_str(), - &result)); - EXPECT_EQ("true", result); - - - // Test that trying to set window.chrome.app.isInstalled throws - // an exception. - ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString( - browser()->GetSelectedTabContents()->render_view_host(), - L"", - L"window.domAutomationController.send(" - L" function() {" - L" try {" - L" window.chrome.app.isInstalled = false;" - L" return 'BAD: Should have thrown by now...';" - L" } catch (e) {" - L" return 'GOOD: Saw expected error.';" - L" }" - L" }()" - L");", - &result)); - EXPECT_EQ("GOOD: Saw expected error.", result); -} +//============================================================================== +// STOP! Please do not add any more random-ass tests here. Create new files for +// your tests grouped by functionality. Also, you should strongly consider using +// ExtensionAPITest if possible. +//============================================================================== diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index a97bd0d..78c3ff0 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -810,13 +810,13 @@ void ExtensionService::LoadComponentExtensions() { } } -void ExtensionService::LoadComponentExtension( +const Extension* ExtensionService::LoadComponentExtension( const ComponentExtensionInfo &info) { JSONStringValueSerializer serializer(info.manifest); scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL)); if (!manifest.get()) { DLOG(ERROR) << "Failed to parse manifest for extension"; - return; + return NULL; } int flags = Extension::REQUIRE_KEY; @@ -831,9 +831,10 @@ void ExtensionService::LoadComponentExtension( &error)); if (!extension.get()) { NOTREACHED() << error; - return; + return NULL; } AddExtension(extension); + return extension; } void ExtensionService::LoadAllExtensions() { diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index fb897aa..ee486f7 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h @@ -258,7 +258,7 @@ class ExtensionService void LoadComponentExtensions(); // Loads particular component extension. - void LoadComponentExtension(const ComponentExtensionInfo& info); + const Extension* LoadComponentExtension(const ComponentExtensionInfo& info); // Loads all known extensions (used by startup and testing code). void LoadAllExtensions(); diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index 8c2664e..cef75c1 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -684,6 +684,7 @@ void BrowserRenderProcessHost::PropagateBrowserCommandLineToRenderer( switches::kAllowHTTPBackgroundPage, switches::kAllowScriptingGallery, switches::kAlwaysAuthorizePlugins, + switches::kAppsCheckoutURL, switches::kAppsGalleryURL, // We propagate the Chrome Frame command line here as well in case the // renderer is not run in the sandbox. diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 981d3a8..284b916 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2241,6 +2241,7 @@ 'browser/extensions/browser_action_test_util_gtk.cc', 'browser/extensions/browser_action_test_util_mac.mm', 'browser/extensions/browser_action_test_util_views.cc', + 'browser/extensions/chrome_app_api_browsertest.cc', 'browser/extensions/content_script_apitest.cc', 'browser/extensions/convert_web_app_browsertest.cc', 'browser/extensions/cross_origin_xhr_apitest.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 44aa7cd..efbb36b 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -47,6 +47,10 @@ const char kApp[] = "app"; // according to its configuration. const char kAppId[] = "app-id"; +// Override the apps checkout URL, which is used to determine when to expose +// some private APIs. +const char kAppsCheckoutURL[] = "apps-checkout-url"; + // Specifying this flag allows the webstorePrivate APIs to return browser (aka // sync) login tokens to be used for auto-login in the Web Store (normally they // do not). diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 640e764..64bc63c 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -32,6 +32,7 @@ extern const char kAlwaysAuthorizePlugins[]; extern const char kAlwaysEnableDevTools[]; extern const char kApp[]; extern const char kAppId[]; +extern const char kAppsCheckoutURL[]; extern const char kAppsGalleryReturnTokens[]; extern const char kAppsGalleryURL[]; extern const char kAppsGalleryUpdateURL[]; diff --git a/chrome/renderer/extensions/chrome_app_bindings.cc b/chrome/renderer/extensions/chrome_app_bindings.cc index ef072346..e71209d 100644 --- a/chrome/renderer/extensions/chrome_app_bindings.cc +++ b/chrome/renderer/extensions/chrome_app_bindings.cc @@ -4,8 +4,13 @@ #include "chrome/renderer/extensions/chrome_app_bindings.h" +#include "base/command_line.h" +#include "base/json/json_writer.h" #include "base/string16.h" +#include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_set.h" #include "chrome/renderer/extensions/bindings_utils.h" #include "chrome/renderer/extensions/extension_dispatcher.h" @@ -15,25 +20,66 @@ using WebKit::WebFrame; +namespace { + +bool IsCheckoutURL(const std::string& url_spec) { + std::string checkout_url_prefix = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kAppsCheckoutURL); + if (checkout_url_prefix.empty()) + checkout_url_prefix = "https://checkout.google.com/"; + + return StartsWithASCII(url_spec, checkout_url_prefix, false); +} + +bool CheckAccessToAppDetails() { + WebFrame* frame = WebFrame::frameForCurrentContext(); + if (!frame) { + LOG(ERROR) << "Could not get frame for current context."; + return false; + } + + if (!IsCheckoutURL(frame->url().spec())) { + std::string error("Access denied for URL: "); + error += frame->url().spec(); + v8::ThrowException(v8::String::New(error.c_str())); + return false; + } + + return true; +} + +} + namespace extensions_v8 { static const char* const kAppExtensionName = "v8/ChromeApp"; class ChromeAppExtensionWrapper : public v8::Extension { public: - explicit ChromeAppExtensionWrapper(ExtensionDispatcher* extension_dispatcher) - : v8::Extension(kAppExtensionName, - "var chrome;" - "if (!chrome)" - " chrome = {};" - "if (!chrome.app) {" - " chrome.app = new function() {" - " native function GetIsInstalled();" - " native function Install();" - " this.__defineGetter__('isInstalled', GetIsInstalled);" - " this.install = Install;" - " };" - "}") { + explicit ChromeAppExtensionWrapper(ExtensionDispatcher* extension_dispatcher) : + v8::Extension(kAppExtensionName, + "var chrome;" + "if (!chrome)" + " chrome = {};" + "if (!chrome.app) {" + " chrome.app = new function() {" + " native function GetIsInstalled();" + " native function Install();" + " native function GetDetails();" + " native function GetDetailsForFrame();" + " this.__defineGetter__('isInstalled', GetIsInstalled);" + " this.install = Install;" + " this.getDetails = function() {" + " var json = GetDetails();" + " return json == null ? null : JSON.parse(json);" + " };" + " this.getDetailsForFrame = function(frame) {" + " var json = GetDetailsForFrame(frame);" + " return json == null ? null : JSON.parse(json);" + " };" + " };" + "}") { extension_dispatcher_ = extension_dispatcher; } @@ -47,6 +93,10 @@ class ChromeAppExtensionWrapper : public v8::Extension { return v8::FunctionTemplate::New(GetIsInstalled); } else if (name->Equals(v8::String::New("Install"))) { return v8::FunctionTemplate::New(Install); + } else if (name->Equals(v8::String::New("GetDetails"))) { + return v8::FunctionTemplate::New(GetDetails); + } else if (name->Equals(v8::String::New("GetDetailsForFrame"))) { + return v8::FunctionTemplate::New(GetDetailsForFrame); } else { return v8::Handle<v8::FunctionTemplate>(); } @@ -80,6 +130,52 @@ class ChromeAppExtensionWrapper : public v8::Extension { return v8::Undefined(); } + static v8::Handle<v8::Value> GetDetails(const v8::Arguments& args) { + return GetDetailsForFrameImpl(WebFrame::frameForCurrentContext()); + } + + static v8::Handle<v8::Value> GetDetailsForFrame( + const v8::Arguments& args) { + if (!CheckAccessToAppDetails()) + return v8::Undefined(); + + if (args.Length() < 0) + return v8::ThrowException(v8::String::New("Not enough arguments.")); + + if (!args[0]->IsObject()) { + return v8::ThrowException( + v8::String::New("Argument 0 must be an object.")); + } + + v8::Local<v8::Context> context = + v8::Local<v8::Object>::Cast(args[0])->CreationContext(); + CHECK(!context.IsEmpty()); + + WebFrame* target_frame = WebFrame::frameForContext(context); + if (!target_frame) { + return v8::ThrowException( + v8::String::New("Could not find frame for specified object.")); + } + + return GetDetailsForFrameImpl(target_frame); + } + + static v8::Handle<v8::Value> GetDetailsForFrameImpl(const WebFrame* frame) { + const ::Extension* extension = + extension_dispatcher_->extensions()->GetByURL(frame->url()); + if (!extension) + return v8::Null(); + + std::string manifest_json; + const bool kPrettyPrint = false; + scoped_ptr<DictionaryValue> manifest_copy( + extension->manifest_value()->DeepCopy()); + manifest_copy->SetString("id", extension->id()); + base::JSONWriter::Write(manifest_copy.get(), kPrettyPrint, &manifest_json); + + return v8::String::New(manifest_json.c_str(), manifest_json.size()); + } + static ExtensionDispatcher* extension_dispatcher_; }; diff --git a/chrome/test/data/extensions/get_app_details_for_frame.html b/chrome/test/data/extensions/get_app_details_for_frame.html new file mode 100644 index 0000000..81b4f2a --- /dev/null +++ b/chrome/test/data/extensions/get_app_details_for_frame.html @@ -0,0 +1,27 @@ +<script> +function testUnsuccessfulAccess() { + try { + chrome.app.getDetailsForFrame(frames[0]); + } catch (e) { + if (e.indexOf("Access denied") == 0) + return true; + else + throw e; + } + return false; +} + +function getFrameURL(host) { + var result = "http://" + host; + if (location.port) { + result += ":"; + result += location.port; + } + result += location.pathname; + return result; +} + +var iframe = document.createElement("iframe"); +iframe.src = getFrameURL("app.com"); +document.documentElement.appendChild(iframe); +</script> |