Commit 6e6ebd18 authored by edwardjung's avatar edwardjung Committed by Commit bot

Implement chrome://network-errors for direct access to network error interstitials

+ chrome://network-errors/ lists all the network errors.
+ chrome://network-error/<err_code>  shows a specific network error.

BUG=

Review URL: https://codereview.chromium.org/1421743002

Cr-Commit-Position: refs/heads/master@{#362968}
parent 7eb1fccb
...@@ -627,6 +627,8 @@ const char* const kChromeHostURLs[] = { ...@@ -627,6 +627,8 @@ const char* const kChromeHostURLs[] = {
content::kChromeUIHistogramHost, content::kChromeUIHistogramHost,
content::kChromeUIIndexedDBInternalsHost, content::kChromeUIIndexedDBInternalsHost,
content::kChromeUIMediaInternalsHost, content::kChromeUIMediaInternalsHost,
content::kChromeUINetworkErrorHost,
content::kChromeUINetworkErrorsListingHost,
content::kChromeUINetworkViewCacheHost, content::kChromeUINetworkViewCacheHost,
content::kChromeUIServiceWorkerInternalsHost, content::kChromeUIServiceWorkerInternalsHost,
content::kChromeUITracingHost, content::kChromeUITracingHost,
......
// Copyright 2015 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 "content/browser/net/network_errors_listing_ui.h"
#include "base/bind.h"
#include "base/json/json_writer.h"
#include "base/values.h"
#include "content/grit/content_resources.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "net/base/net_errors.h"
#include "net/log/net_log_util.h"
static const char kDataFile[] = "network-error-data.json";
static const char kErrorCodeField[] = "errorCode";
static const char kErrorCodesDataName[] = "errorCodes";
static const char kErrorIdField[] = "errorId";
static const char kNetworkErrorKey[] = "netError";
namespace content {
namespace {
scoped_ptr<base::ListValue> GetNetworkErrorData() {
scoped_ptr<base::DictionaryValue> error_codes = net::GetNetConstants();
const base::DictionaryValue* net_error_codes_dict = nullptr;
for (base::DictionaryValue::Iterator itr(*error_codes); !itr.IsAtEnd();
itr.Advance()) {
if (itr.key() == kNetworkErrorKey) {
itr.value().GetAsDictionary(&net_error_codes_dict);
break;
}
}
scoped_ptr<base::ListValue> error_list(new base::ListValue());
for (base::DictionaryValue::Iterator itr(*net_error_codes_dict);
!itr.IsAtEnd(); itr.Advance()) {
int error_code;
itr.value().GetAsInteger(&error_code);
// Exclude the aborted and pending codes as these don't return a page.
if (error_code != net::Error::ERR_IO_PENDING &&
error_code != net::Error::ERR_ABORTED) {
base::DictionaryValue* error = new base::DictionaryValue();
error->SetInteger(kErrorIdField, error_code);
error->SetString(kErrorCodeField, itr.key());
error_list->Append(error);
}
}
return error_list;
}
bool HandleRequestCallback(BrowserContext* current_context,
const std::string& path,
const WebUIDataSource::GotDataCallback& callback) {
if (path != kDataFile)
return false;
base::DictionaryValue data;
data.Set(kErrorCodesDataName, GetNetworkErrorData().release());
std::string json_string;
base::JSONWriter::Write(data, &json_string);
callback.Run(base::RefCountedString::TakeString(&json_string));
return true;
}
} // namespace
NetworkErrorsListingUI::NetworkErrorsListingUI(WebUI* web_ui)
: WebUIController(web_ui) {
// Set up the chrome://network-errors source.
WebUIDataSource* html_source =
WebUIDataSource::Create(kChromeUINetworkErrorsListingHost);
// Add required resources.
html_source->SetJsonPath("strings.js");
html_source->AddResourcePath("network_errors_listing.css",
IDR_NETWORK_ERROR_LISTING_CSS);
html_source->AddResourcePath("network_errors_listing.js",
IDR_NETWORK_ERROR_LISTING_JS);
html_source->SetDefaultResource(IDR_NETWORK_ERROR_LISTING_HTML);
html_source->SetRequestFilter(
base::Bind(&HandleRequestCallback,
web_ui->GetWebContents()->GetBrowserContext()));
BrowserContext* browser_context =
web_ui->GetWebContents()->GetBrowserContext();
WebUIDataSource::Add(browser_context, html_source);
}
} // namespace content
// Copyright 2015 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 CONTENT_BROWSER_NET_NETWORK_ERRORS_LISTING_UI_H_
#define CONTENT_BROWSER_NET_NETWORK_ERRORS_LISTING_UI_H_
#include "base/macros.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_data_source.h"
namespace content {
class NetworkErrorsListingUI : public WebUIController {
public:
explicit NetworkErrorsListingUI(WebUI* web_ui);
~NetworkErrorsListingUI() override {};
private:
DISALLOW_COPY_AND_ASSIGN(NetworkErrorsListingUI);
};
} // namespace content
#endif // CONTENT_BROWSER_NET_NETWORK_ERRORS_LISTING_UI_H_
/* Copyright 2015 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. */
body {
margin: 10px 10px 30px;
}
ul {
line-height: 1.7em;
padding-left: 15px;
}
a {
word-break: break-word;
}
\ No newline at end of file
<!doctype html>
<html>
<!--
Copyright 2015 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.
-->
<head>
<meta charset="utf-8">
<title>Network errors</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="chrome://resources/css/roboto.css">
<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
<link rel="stylesheet" href="network_errors_listing.css">
<script src="chrome://resources/js/cr.js"></script>
<script src="chrome://resources/js/load_time_data.js"></script>
<script src="chrome://resources/js/util.js"></script>
<script src="strings.js"></script>
<script src="network_errors_listing.js"></script>
</head>
<body>
<h1>Network errors</h1>
<div id="pages" class="list"></div>
<script src="chrome://resources/js/i18n_template.js"></script>
</body>
</html>
\ No newline at end of file
// Copyright 2015 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.
cr.define('errorCodes', function() {
'use strict';
/**
* Generate the page content.
* @param {Array.<Object>} errorCodes Error codes array consisting of a
* numerical error ID and error code string.
*/
function listErrorCodes(errorCodes) {
var errorPageUrl = 'chrome://network-error/';
var errorCodesList = document.createElement('ul');
for (var i = 0; i < errorCodes.length; i++) {
var listEl = document.createElement('li');
var errorCodeLinkEl = document.createElement('a');
errorCodeLinkEl.href = errorPageUrl + errorCodes[i].errorId;
errorCodeLinkEl.textContent = errorCodes[i].errorCode + ' (' +
errorCodes[i].errorId + ')';
listEl.appendChild(errorCodeLinkEl);
errorCodesList.appendChild(listEl);
}
$('pages').appendChild(errorCodesList);
}
function initialize() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'network-error-data.json');
xhr.addEventListener('load', function(e) {
if (xhr.status === 200) {
try {
var data = JSON.parse(xhr.responseText);
listErrorCodes(data['errorCodes']);
} catch (e) {
$('pages').innerText = 'Could not parse the error codes data. ' +
'Try reloading the page.';
}
}
});
xhr.send();
}
return {
initialize: initialize
};
});
document.addEventListener('DOMContentLoaded', errorCodes.initialize);
\ No newline at end of file
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "content/browser/gpu/gpu_internals_ui.h" #include "content/browser/gpu/gpu_internals_ui.h"
#include "content/browser/indexed_db/indexed_db_internals_ui.h" #include "content/browser/indexed_db/indexed_db_internals_ui.h"
#include "content/browser/media/media_internals_ui.h" #include "content/browser/media/media_internals_ui.h"
#include "content/browser/net/network_errors_listing_ui.h"
#include "content/browser/service_worker/service_worker_internals_ui.h" #include "content/browser/service_worker/service_worker_internals_ui.h"
#include "content/browser/tracing/tracing_ui.h" #include "content/browser/tracing/tracing_ui.h"
#include "content/public/browser/storage_partition.h" #include "content/public/browser/storage_partition.h"
...@@ -36,7 +37,8 @@ WebUI::TypeID ContentWebUIControllerFactory::GetWebUIType( ...@@ -36,7 +37,8 @@ WebUI::TypeID ContentWebUIControllerFactory::GetWebUIType(
url.host() == kChromeUIMediaInternalsHost || url.host() == kChromeUIMediaInternalsHost ||
url.host() == kChromeUIServiceWorkerInternalsHost || url.host() == kChromeUIServiceWorkerInternalsHost ||
url.host() == kChromeUIAccessibilityHost || url.host() == kChromeUIAccessibilityHost ||
url.host() == kChromeUIAppCacheInternalsHost) { url.host() == kChromeUIAppCacheInternalsHost ||
url.host() == kChromeUINetworkErrorsListingHost) {
return const_cast<ContentWebUIControllerFactory*>(this); return const_cast<ContentWebUIControllerFactory*>(this);
} }
return WebUI::kNoWebUI; return WebUI::kNoWebUI;
...@@ -69,6 +71,8 @@ WebUIController* ContentWebUIControllerFactory::CreateWebUIControllerForURL( ...@@ -69,6 +71,8 @@ WebUIController* ContentWebUIControllerFactory::CreateWebUIControllerForURL(
return new AccessibilityUI(web_ui); return new AccessibilityUI(web_ui);
if (url.host() == kChromeUIServiceWorkerInternalsHost) if (url.host() == kChromeUIServiceWorkerInternalsHost)
return new ServiceWorkerInternalsUI(web_ui); return new ServiceWorkerInternalsUI(web_ui);
if (url.host() == kChromeUINetworkErrorsListingHost)
return new NetworkErrorsListingUI(web_ui);
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
if (url.host() == kChromeUITracingHost) if (url.host() == kChromeUITracingHost)
return new TracingUI(web_ui); return new TracingUI(web_ui);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/profiler/scoped_tracker.h" #include "base/profiler/scoped_tracker.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
...@@ -38,10 +39,13 @@ ...@@ -38,10 +39,13 @@
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
#include "net/http/http_response_headers.h" #include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h" #include "net/http/http_status_code.h"
#include "net/log/net_log_util.h"
#include "net/url_request/url_request.h" #include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_job.h" #include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_job_factory.h" #include "net/url_request/url_request_job_factory.h"
#include "url/url_util.h" #include "url/url_util.h"
namespace content { namespace content {
...@@ -52,7 +56,7 @@ const char kChromeURLContentSecurityPolicyHeaderBase[] = ...@@ -52,7 +56,7 @@ const char kChromeURLContentSecurityPolicyHeaderBase[] =
"Content-Security-Policy: script-src chrome://resources 'self'"; "Content-Security-Policy: script-src chrome://resources 'self'";
const char kChromeURLXFrameOptionsHeader[] = "X-Frame-Options: DENY"; const char kChromeURLXFrameOptionsHeader[] = "X-Frame-Options: DENY";
static const char kNetworkErrorKey[] = "netError";
const int kNoRenderProcessId = -1; const int kNoRenderProcessId = -1;
bool SchemeIsInSchemes(const std::string& scheme, bool SchemeIsInSchemes(const std::string& scheme,
...@@ -453,6 +457,30 @@ void GetMimeTypeOnUI(URLDataSourceImpl* source, ...@@ -453,6 +457,30 @@ void GetMimeTypeOnUI(URLDataSourceImpl* source,
namespace { namespace {
bool IsValidNetworkErrorCode(int error_code) {
scoped_ptr<base::DictionaryValue> error_codes = net::GetNetConstants();
const base::DictionaryValue* net_error_codes_dict = nullptr;
for (base::DictionaryValue::Iterator itr(*error_codes); !itr.IsAtEnd();
itr.Advance()) {
if (itr.key() == kNetworkErrorKey) {
itr.value().GetAsDictionary(&net_error_codes_dict);
break;
}
}
if (net_error_codes_dict != nullptr) {
for (base::DictionaryValue::Iterator itr(*net_error_codes_dict);
!itr.IsAtEnd(); itr.Advance()) {
int net_error_code;
itr.value().GetAsInteger(&net_error_code);
if (error_code == net_error_code)
return true;
}
}
return false;
}
class ChromeProtocolHandler class ChromeProtocolHandler
: public net::URLRequestJobFactory::ProtocolHandler { : public net::URLRequestJobFactory::ProtocolHandler {
public: public:
...@@ -489,6 +517,24 @@ class ChromeProtocolHandler ...@@ -489,6 +517,24 @@ class ChromeProtocolHandler
return new HistogramInternalsRequestJob(request, network_delegate); return new HistogramInternalsRequestJob(request, network_delegate);
} }
// Check for chrome://network-error/, which uses its own job type.
if (request->url().SchemeIs(kChromeUIScheme) &&
request->url().host() == kChromeUINetworkErrorHost) {
// Get the error code passed in via the request URL path.
std::basic_string<char> error_code_string =
request->url().path().substr(1);
int error_code;
if (base::StringToInt(error_code_string, &error_code)) {
// Check for a valid error code.
if (IsValidNetworkErrorCode(error_code) &&
error_code != net::Error::ERR_IO_PENDING) {
return new net::URLRequestErrorJob(request, network_delegate,
error_code);
}
}
}
// Fall back to using a custom handler // Fall back to using a custom handler
return new URLRequestChromeJob( return new URLRequestChromeJob(
request, network_delegate, request, network_delegate,
......
...@@ -107,4 +107,26 @@ TEST_F(UrlDataManagerBackendTest, CancelAfterFirstReadStarted) { ...@@ -107,4 +107,26 @@ TEST_F(UrlDataManagerBackendTest, CancelAfterFirstReadStarted) {
EXPECT_EQ("", cancel_delegate.data_received()); EXPECT_EQ("", cancel_delegate.data_received());
} }
// Check for a network error page request via chrome://network-error/.
TEST_F(UrlDataManagerBackendTest, ChromeNetworkErrorPageRequest) {
scoped_ptr<net::URLRequest> error_request =
url_request_context_.CreateRequest(
GURL("chrome://network-error/-105"), net::HIGHEST, &delegate_);
error_request->Start();
base::RunLoop().Run();
EXPECT_EQ(net::URLRequestStatus::FAILED, error_request->status().status());
EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, error_request->status().error());
}
// Check for an invalid network error page request via chrome://network-error/.
TEST_F(UrlDataManagerBackendTest, ChromeNetworkErrorPageRequestFailed) {
scoped_ptr<net::URLRequest> error_request =
url_request_context_.CreateRequest(
GURL("chrome://network-error/-123456789"), net::HIGHEST, &delegate_);
error_request->Start();
base::RunLoop().Run();
EXPECT_EQ(net::URLRequestStatus::FAILED, error_request->status().status());
EXPECT_EQ(net::ERR_INVALID_URL, error_request->status().error());
}
} // namespace content } // namespace content
...@@ -1043,6 +1043,8 @@ ...@@ -1043,6 +1043,8 @@
'browser/navigator_connect/service_port_service_impl.h', 'browser/navigator_connect/service_port_service_impl.h',
'browser/net/browser_online_state_observer.cc', 'browser/net/browser_online_state_observer.cc',
'browser/net/browser_online_state_observer.h', 'browser/net/browser_online_state_observer.h',
'browser/net/network_errors_listing_ui.cc',
'browser/net/network_errors_listing_ui.h',
'browser/net/quota_policy_cookie_store.cc', 'browser/net/quota_policy_cookie_store.cc',
'browser/net/quota_policy_cookie_store.h', 'browser/net/quota_policy_cookie_store.h',
'browser/net/view_blob_internals_job_factory.cc', 'browser/net/view_blob_internals_job_factory.cc',
......
...@@ -27,6 +27,9 @@ ...@@ -27,6 +27,9 @@
<include name="IDR_INDEXED_DB_INTERNALS_CSS" file="browser/resources/indexed_db/indexeddb_internals.css" flattenhtml="true" type="BINDATA" /> <include name="IDR_INDEXED_DB_INTERNALS_CSS" file="browser/resources/indexed_db/indexeddb_internals.css" flattenhtml="true" type="BINDATA" />
<include name="IDR_MEDIA_INTERNALS_HTML" file="browser/resources/media/media_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> <include name="IDR_MEDIA_INTERNALS_HTML" file="browser/resources/media/media_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_MEDIA_INTERNALS_JS" file="browser/resources/media/media_internals.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_MEDIA_INTERNALS_JS" file="browser/resources/media/media_internals.js" flattenhtml="true" type="BINDATA" />
<include name="IDR_NETWORK_ERROR_LISTING_HTML" file="browser/resources/net/network_errors_listing.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_NETWORK_ERROR_LISTING_JS" file="browser/resources/net/network_errors_listing.js" flattenhtml="true" type="BINDATA" />
<include name="IDR_NETWORK_ERROR_LISTING_CSS" file="browser/resources/net/network_errors_listing.css" flattenhtml="true" type="BINDATA" />
<include name="IDR_SERVICE_WORKER_INTERNALS_HTML" file="browser/resources/service_worker/serviceworker_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> <include name="IDR_SERVICE_WORKER_INTERNALS_HTML" file="browser/resources/service_worker/serviceworker_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_SERVICE_WORKER_INTERNALS_JS" file="browser/resources/service_worker/serviceworker_internals.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_SERVICE_WORKER_INTERNALS_JS" file="browser/resources/service_worker/serviceworker_internals.js" flattenhtml="true" type="BINDATA" />
<include name="IDR_SERVICE_WORKER_INTERNALS_CSS" file="browser/resources/service_worker/serviceworker_internals.css" flattenhtml="true" type="BINDATA" /> <include name="IDR_SERVICE_WORKER_INTERNALS_CSS" file="browser/resources/service_worker/serviceworker_internals.css" flattenhtml="true" type="BINDATA" />
......
...@@ -29,6 +29,8 @@ const char kChromeUIGpuHost[] = "gpu"; ...@@ -29,6 +29,8 @@ const char kChromeUIGpuHost[] = "gpu";
const char kChromeUIHistogramHost[] = "histograms"; const char kChromeUIHistogramHost[] = "histograms";
const char kChromeUIMediaInternalsHost[] = "media-internals"; const char kChromeUIMediaInternalsHost[] = "media-internals";
const char kChromeUINetworkViewCacheHost[] = "view-http-cache"; const char kChromeUINetworkViewCacheHost[] = "view-http-cache";
const char kChromeUINetworkErrorHost[] = "network-error";
const char kChromeUINetworkErrorsListingHost[] = "network-errors";
const char kChromeUIResourcesHost[] = "resources"; const char kChromeUIResourcesHost[] = "resources";
const char kChromeUIServiceWorkerInternalsHost[] = "serviceworker-internals"; const char kChromeUIServiceWorkerInternalsHost[] = "serviceworker-internals";
const char kChromeUITracingHost[] = "tracing"; const char kChromeUITracingHost[] = "tracing";
...@@ -44,6 +46,8 @@ const char kChromeUIGpuCrashURL[] = "chrome://gpucrash"; ...@@ -44,6 +46,8 @@ const char kChromeUIGpuCrashURL[] = "chrome://gpucrash";
const char kChromeUIGpuHangURL[] = "chrome://gpuhang"; const char kChromeUIGpuHangURL[] = "chrome://gpuhang";
const char kChromeUIHangURL[] = "chrome://hang"; const char kChromeUIHangURL[] = "chrome://hang";
const char kChromeUIKillURL[] = "chrome://kill"; const char kChromeUIKillURL[] = "chrome://kill";
const char kChromeUINetworkErrorURL[] = "chrome://network-error";
const char kChromeUINetworkErrorsListingURL[] = "chrome://network-errors";
const char kChromeUIPpapiFlashCrashURL[] = "chrome://ppapiflashcrash"; const char kChromeUIPpapiFlashCrashURL[] = "chrome://ppapiflashcrash";
const char kChromeUIPpapiFlashHangURL[] = "chrome://ppapiflashhang"; const char kChromeUIPpapiFlashHangURL[] = "chrome://ppapiflashhang";
const char kChromeUIServiceWorkerInternalsURL[] = const char kChromeUIServiceWorkerInternalsURL[] =
......
...@@ -36,6 +36,8 @@ CONTENT_EXPORT extern const char kChromeUIGpuHost[]; ...@@ -36,6 +36,8 @@ CONTENT_EXPORT extern const char kChromeUIGpuHost[];
CONTENT_EXPORT extern const char kChromeUIHistogramHost[]; CONTENT_EXPORT extern const char kChromeUIHistogramHost[];
CONTENT_EXPORT extern const char kChromeUIIndexedDBInternalsHost[]; CONTENT_EXPORT extern const char kChromeUIIndexedDBInternalsHost[];
CONTENT_EXPORT extern const char kChromeUIMediaInternalsHost[]; CONTENT_EXPORT extern const char kChromeUIMediaInternalsHost[];
CONTENT_EXPORT extern const char kChromeUINetworkErrorHost[];
CONTENT_EXPORT extern const char kChromeUINetworkErrorsListingHost[];
CONTENT_EXPORT extern const char kChromeUINetworkViewCacheHost[]; CONTENT_EXPORT extern const char kChromeUINetworkViewCacheHost[];
CONTENT_EXPORT extern const char kChromeUIResourcesHost[]; CONTENT_EXPORT extern const char kChromeUIResourcesHost[];
CONTENT_EXPORT extern const char kChromeUIServiceWorkerInternalsHost[]; CONTENT_EXPORT extern const char kChromeUIServiceWorkerInternalsHost[];
...@@ -53,6 +55,8 @@ CONTENT_EXPORT extern const char kChromeUIGpuCrashURL[]; ...@@ -53,6 +55,8 @@ CONTENT_EXPORT extern const char kChromeUIGpuCrashURL[];
CONTENT_EXPORT extern const char kChromeUIGpuHangURL[]; CONTENT_EXPORT extern const char kChromeUIGpuHangURL[];
CONTENT_EXPORT extern const char kChromeUIHangURL[]; CONTENT_EXPORT extern const char kChromeUIHangURL[];
CONTENT_EXPORT extern const char kChromeUIKillURL[]; CONTENT_EXPORT extern const char kChromeUIKillURL[];
CONTENT_EXPORT extern const char kChromeUINetworkErrorURL[];
CONTENT_EXPORT extern const char kChromeUINetworkErrorsListingURL[];
CONTENT_EXPORT extern const char kChromeUIPpapiFlashCrashURL[]; CONTENT_EXPORT extern const char kChromeUIPpapiFlashCrashURL[];
CONTENT_EXPORT extern const char kChromeUIPpapiFlashHangURL[]; CONTENT_EXPORT extern const char kChromeUIPpapiFlashHangURL[];
CONTENT_EXPORT extern const char kChromeUIServiceWorkerInternalsURL[]; CONTENT_EXPORT extern const char kChromeUIServiceWorkerInternalsURL[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment