15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/extensions/extension_error_ui_util.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/files/file_path.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/files/file_util.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/location.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/strings/string16.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/sequenced_worker_pool.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/devtools/devtools_window.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/browser/extension_error.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "extensions/browser/extension_registry.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/browser/file_highlighter.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "extensions/common/constants.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "extensions/common/extension.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace extensions {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace error_ui_util {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Keys for objects passed to and from extension error UI.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kPathSuffixKey[] = "pathSuffix";
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kTitleKey[] = "title";
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string ReadFileToString(const base::FilePath& path) {
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string data;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::ReadFileToString(path, &data);
43a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  return data;
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GetManifestFileCallback(base::DictionaryValue* results,
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const std::string& key,
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const std::string& specific,
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const RequestFileSourceCallback& response,
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const std::string& contents) {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ManifestHighlighter highlighter(contents, key, specific);
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  highlighter.SetHighlightedRegions(results);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response.Run(*results);
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GetSourceFileCallback(base::DictionaryValue* results,
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           int line_number,
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           const RequestFileSourceCallback& response,
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           const std::string& contents) {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SourceHighlighter highlighter(contents, line_number);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  highlighter.SetHighlightedRegions(results);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response.Run(*results);
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HandleRequestFileSource(const base::DictionaryValue* args,
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             Profile* profile,
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const RequestFileSourceCallback& response) {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Three required arguments: extension_id, path_suffix, and error_message.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string extension_id;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::FilePath::StringType path_suffix_string;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::string16 error_message;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!args->GetString(kPathSuffixKey, &path_suffix_string) ||
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !args->GetString(ExtensionError::kExtensionIdKey, &extension_id) ||
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !args->GetString(ExtensionError::kMessageKey, &error_message)) {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ExtensionRegistry::Get(profile)->GetExtensionById(
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          extension_id, ExtensionRegistry::EVERYTHING);
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension) {
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    NOTREACHED();
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Under no circumstances should we ever need to reference a file outside of
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the extension's directory. If it tries to, abort.
9390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::FilePath path_suffix(path_suffix_string);
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (path_suffix.ReferencesParent())
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath path = extension->path().Append(path_suffix);
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Setting the title and the error message is the same for all file types.
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<base::DictionaryValue> results(new base::DictionaryValue);
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  results->SetString(kTitleKey,
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     base::UTF8ToUTF16(extension->name()) +
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         base::ASCIIToUTF16(": ") +
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         path.BaseName().LossyDisplayName());
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  results->SetString(ExtensionError::kMessageKey, error_message);
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::Callback<void(const std::string&)> reply;
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (path_suffix_string == kManifestFilename) {
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string manifest_key;
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!args->GetString(ManifestError::kManifestKeyKey, &manifest_key)) {
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTREACHED();
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // A "specific" location is optional.
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string specific;
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    args->GetString(ManifestError::kManifestSpecificKey, &specific);
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    reply = base::Bind(&GetManifestFileCallback,
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       base::Owned(results.release()),
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       manifest_key,
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       specific,
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       response);
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int line_number = 0;
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    args->GetInteger(RuntimeError::kLineNumberKey, &line_number);
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    reply = base::Bind(&GetSourceFileCallback,
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       base::Owned(results.release()),
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       line_number,
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       response);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::PostTaskAndReplyWithResult(content::BrowserThread::GetBlockingPool(),
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   FROM_HERE,
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   base::Bind(&ReadFileToString, path),
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   reply);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HandleOpenDevTools(const base::DictionaryValue* args) {
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int render_process_id = 0;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int render_view_id = 0;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The render view and render process ids are required.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!args->GetInteger(RuntimeError::kRenderProcessIdKey,
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        &render_process_id) ||
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !args->GetInteger(RuntimeError::kRenderViewIdKey, &render_view_id)) {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::RenderViewHost* rvh =
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::RenderViewHost::FromID(render_process_id, render_view_id);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // It's possible that the render view was closed since we last updated the
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // links. Handle this gracefully.
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!rvh)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::WebContents* web_contents =
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::WebContents::FromRenderViewHost(rvh);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!web_contents)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we include a url, we should inspect it specifically (and not just the
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // render view).
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::string16 url;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (args->GetString(RuntimeError::kUrlKey, &url)) {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Line and column numbers are optional; default to the first line.
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int line_number = 1;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int column_number = 1;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    args->GetInteger(RuntimeError::kLineNumberKey, &line_number);
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    args->GetInteger(RuntimeError::kColumnNumberKey, &column_number);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Line/column numbers are reported in display-friendly 1-based numbers,
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // but are inspected in zero-based numbers.
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DevToolsWindow::OpenDevToolsWindow(
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        web_contents,
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DevToolsToggleAction::Reveal(url, line_number - 1, column_number - 1));
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DevToolsWindow::OpenDevToolsWindow(web_contents);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Once we open the inspector, we focus on the appropriate tab...
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ... but some pages (popups and apps) don't have tabs, and some (background
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // pages) don't have an associated browser. For these, the inspector opens in
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // a new window, and our work is done.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!browser || !browser->is_type_tabbed())
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabStripModel* tab_strip = browser->tab_strip_model();
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab_strip->ActivateTabAt(tab_strip->GetIndexOfWebContents(web_contents),
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           false);  // Not through direct user gesture.
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace error_ui_util
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace extensions
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)