Index: chrome/browser/extensions/api/developer_private/developer_private_api.cc |
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 9470e7821be80f10b1f9df841071588ddfea849e..b618c1d3f99aa513280dcdab105e3abd6542e017 100644 |
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc |
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc |
@@ -14,6 +14,7 @@ |
#include "base/i18n/file_util_icu.h" |
#include "base/lazy_instance.h" |
#include "base/strings/string_number_conversions.h" |
+#include "base/strings/stringprintf.h" |
#include "base/strings/utf_string_conversions.h" |
#include "base/values.h" |
#include "chrome/browser/devtools/devtools_window.h" |
@@ -32,8 +33,9 @@ |
#include "chrome/browser/platform_util.h" |
#include "chrome/browser/profiles/profile.h" |
#include "chrome/browser/sync_file_system/syncable_file_system_util.h" |
+#include "chrome/browser/ui/browser_finder.h" |
#include "chrome/browser/ui/chrome_select_file_policy.h" |
-#include "chrome/browser/ui/webui/extensions/extension_error_ui_util.h" |
+#include "chrome/browser/ui/tabs/tab_strip_model.h" |
#include "chrome/browser/ui/webui/extensions/extension_icon_source.h" |
#include "chrome/common/extensions/api/developer_private.h" |
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h" |
@@ -53,6 +55,7 @@ |
#include "extensions/browser/extension_prefs.h" |
#include "extensions/browser/extension_registry.h" |
#include "extensions/browser/extension_system.h" |
+#include "extensions/browser/file_highlighter.h" |
#include "extensions/browser/management_policy.h" |
#include "extensions/browser/notification_types.h" |
#include "extensions/browser/view_type_utils.h" |
@@ -100,8 +103,13 @@ const char kCouldNotShowSelectFileDialogError[] = |
"Could not show a file chooser."; |
const char kFileSelectionCanceled[] = |
"File selection was canceled."; |
+const char kInvalidPathError[] = "Invalid path."; |
+const char kManifestKeyIsRequiredError[] = |
+ "The 'manifestKey' argument is required for manifest files."; |
+const char kNoSuchRendererError[] = "Could not find the renderer."; |
const char kUnpackedAppsFolder[] = "apps_target"; |
+const char kManifestFile[] = "manifest.json"; |
ExtensionService* GetExtensionService(content::BrowserContext* context) { |
return ExtensionSystem::Get(context)->extension_service(); |
@@ -170,6 +178,12 @@ void BroadcastItemStateChanged(content::BrowserContext* browser_context, |
EventRouter::Get(browser_context)->BroadcastEvent(event.Pass()); |
} |
+std::string ReadFileToString(const base::FilePath& path) { |
+ std::string data; |
+ ignore_result(base::ReadFileToString(path, &data)); |
+ return data; |
+} |
+ |
} // namespace |
namespace AllowFileAccess = api::developer_private::AllowFileAccess; |
@@ -1332,46 +1346,124 @@ DeveloperPrivateRequestFileSourceFunction:: |
DeveloperPrivateRequestFileSourceFunction:: |
~DeveloperPrivateRequestFileSourceFunction() {} |
-bool DeveloperPrivateRequestFileSourceFunction::RunAsync() { |
- scoped_ptr<developer::RequestFileSource::Params> params( |
- developer::RequestFileSource::Params::Create(*args_)); |
- EXTENSION_FUNCTION_VALIDATE(params); |
+ExtensionFunction::ResponseAction |
+DeveloperPrivateRequestFileSourceFunction::Run() { |
+ params_ = developer::RequestFileSource::Params::Create(*args_); |
+ EXTENSION_FUNCTION_VALIDATE(params_); |
- base::DictionaryValue* dict = nullptr; |
- // TODO(devlin): Use generated |params|. |
- EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0u, &dict)); |
+ const developer::RequestFileSourceProperties& properties = |
+ params_->properties; |
+ const Extension* extension = GetExtensionById(properties.extension_id); |
+ if (!extension) |
+ return RespondNow(Error(kNoSuchExtensionError)); |
- AddRef(); // Balanced in LaunchCallback(). |
- error_ui_util::HandleRequestFileSource( |
- dict, |
- GetProfile(), |
- base::Bind(&DeveloperPrivateRequestFileSourceFunction::LaunchCallback, |
- base::Unretained(this))); |
- return true; |
+ // Under no circumstances should we ever need to reference a file outside of |
+ // the extension's directory. If it tries to, abort. |
+ base::FilePath path_suffix = |
+ base::FilePath::FromUTF8Unsafe(properties.path_suffix); |
+ if (path_suffix.empty() || path_suffix.ReferencesParent()) |
+ return RespondNow(Error(kInvalidPathError)); |
+ |
+ if (properties.path_suffix == kManifestFile && !properties.manifest_key) |
+ return RespondNow(Error(kManifestKeyIsRequiredError)); |
+ |
+ base::PostTaskAndReplyWithResult( |
+ content::BrowserThread::GetBlockingPool(), |
+ FROM_HERE, |
+ base::Bind(&ReadFileToString, extension->path().Append(path_suffix)), |
+ base::Bind(&DeveloperPrivateRequestFileSourceFunction::Finish, this)); |
+ |
+ return RespondLater(); |
} |
-void DeveloperPrivateRequestFileSourceFunction::LaunchCallback( |
- const base::DictionaryValue& results) { |
- SetResult(results.DeepCopy()); |
- SendResponse(true); |
- Release(); // Balanced in RunAsync(). |
+void DeveloperPrivateRequestFileSourceFunction::Finish( |
+ const std::string& file_contents) { |
+ const developer::RequestFileSourceProperties& properties = |
+ params_->properties; |
+ const Extension* extension = GetExtensionById(properties.extension_id); |
+ if (!extension) { |
+ Respond(Error(kNoSuchExtensionError)); |
+ return; |
+ } |
+ |
+ developer::RequestFileSourceResponse response; |
+ base::FilePath path_suffix = |
+ base::FilePath::FromUTF8Unsafe(properties.path_suffix); |
+ base::FilePath path = extension->path().Append(path_suffix); |
+ response.title = base::StringPrintf("%s: %s", |
+ extension->name().c_str(), |
+ path.BaseName().AsUTF8Unsafe().c_str()); |
+ response.message = properties.message; |
+ |
+ scoped_ptr<FileHighlighter> highlighter; |
+ if (properties.path_suffix == kManifestFile) { |
+ highlighter.reset(new ManifestHighlighter( |
+ file_contents, |
+ *properties.manifest_key, |
+ properties.manifest_specific ? |
+ *properties.manifest_specific : std::string())); |
+ } else { |
+ highlighter.reset(new SourceHighlighter( |
+ file_contents, |
+ properties.line_number ? *properties.line_number : 0)); |
+ } |
+ |
+ response.before_highlight = highlighter->GetBeforeFeature(); |
+ response.highlight = highlighter->GetFeature(); |
+ response.after_highlight = highlighter->GetAfterFeature(); |
+ |
+ Respond(OneArgument(response.ToValue().release())); |
} |
DeveloperPrivateOpenDevToolsFunction::DeveloperPrivateOpenDevToolsFunction() {} |
DeveloperPrivateOpenDevToolsFunction::~DeveloperPrivateOpenDevToolsFunction() {} |
-bool DeveloperPrivateOpenDevToolsFunction::RunAsync() { |
+ExtensionFunction::ResponseAction |
+DeveloperPrivateOpenDevToolsFunction::Run() { |
scoped_ptr<developer::OpenDevTools::Params> params( |
developer::OpenDevTools::Params::Create(*args_)); |
EXTENSION_FUNCTION_VALIDATE(params); |
+ const developer::OpenDevToolsProperties& properties = params->properties; |
+ |
+ content::RenderViewHost* rvh = |
+ content::RenderViewHost::FromID(properties.render_process_id, |
+ properties.render_view_id); |
+ content::WebContents* web_contents = |
+ rvh ? content::WebContents::FromRenderViewHost(rvh) : nullptr; |
+ // It's possible that the render view was closed since we last updated the |
+ // links. Handle this gracefully. |
+ if (!web_contents) |
+ return RespondNow(Error(kNoSuchRendererError)); |
+ |
+ // If we include a url, we should inspect it specifically (and not just the |
+ // render view). |
+ if (properties.url) { |
+ // Line/column numbers are reported in display-friendly 1-based numbers, |
+ // but are inspected in zero-based numbers. |
+ // Default to the first line/column. |
+ DevToolsWindow::OpenDevToolsWindow( |
+ web_contents, |
+ DevToolsToggleAction::Reveal( |
+ base::UTF8ToUTF16(*properties.url), |
+ properties.line_number ? *properties.line_number - 1 : 0, |
+ properties.column_number ? *properties.column_number - 1 : 0)); |
+ } else { |
+ DevToolsWindow::OpenDevToolsWindow(web_contents); |
+ } |
- base::DictionaryValue* dict = nullptr; |
- // TODO(devlin): Use generated |params|. |
- EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0u, &dict)); |
+ // Once we open the inspector, we focus on the appropriate tab... |
+ Browser* browser = chrome::FindBrowserWithWebContents(web_contents); |
- error_ui_util::HandleOpenDevTools(dict); |
+ // ... but some pages (popups and apps) don't have tabs, and some (background |
+ // pages) don't have an associated browser. For these, the inspector opens in |
+ // a new window, and our work is done. |
+ if (!browser || !browser->is_type_tabbed()) |
+ return RespondNow(NoArguments()); |
- return true; |
+ TabStripModel* tab_strip = browser->tab_strip_model(); |
+ tab_strip->ActivateTabAt(tab_strip->GetIndexOfWebContents(web_contents), |
+ false); // Not through direct user gesture. |
+ return RespondNow(NoArguments()); |
} |
} // namespace api |