Commit 3eba56ae authored by Scott Chen's avatar Scott Chen Committed by Commit Bot

MD Extensions: Add devtools in error-console.

This CL ports over the runtime error stack-trace and dev-view button
that existed in the old extensions page.

Bug: 768609
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: I80fc989ca0b3bcc8af7483a2f475e673e2476674
Reviewed-on: https://chromium-review.googlesource.com/691181
Commit-Queue: Scott Chen <scottchen@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#506873}
parent 4441dd83
......@@ -7,6 +7,15 @@
<message name="IDS_MD_EXTENSIONS_ERROR_PAGE_HEADING" desc="The heading of the page displaying an extension's errors.">
Errors
</message>
<message name="IDS_MD_EXTENSIONS_ERROR_ANONYMOUS_FUNCTION" desc="The label indicating that an error was caused within an anonymous function in the code.">
anonymous function
</message>
<message name="IDS_MD_EXTENSIONS_ERROR_LAUNCH_DEVTOOLS" desc="The text for the button to view an extension error in the developer tools.">
View in Developer Tools
</message>
<message name="IDS_MD_EXTENSIONS_ERROR_STACK_TRACE" desc="The label for the stack trace of an extension's error.">
Stack Trace
</message>
<message name="IDS_MD_EXTENSIONS_ITEM_ERRORS" desc="The label of the button to bring the user to the page showing an extension's errors.">
Errors
</message>
......
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
<link rel="import" href="chrome://resources/html/cr.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
<link rel="import" href="chrome://extensions/code_section.html">
......@@ -13,7 +15,7 @@
<dom-module id="extensions-error-page">
<template>
<style include="cr-icons cr-hidden-style">
<style include="paper-button-style cr-icons cr-shared-style">
:host {
display: block;
height: 100%;
......@@ -77,6 +79,49 @@
.icon-severity-fatal {
content: url(error_severity_fatal.png);
}
#devtools-controls {
padding: 0 var(--cr-section-padding);
}
#stack-trace-heading {
@apply(--cr-title-text);
align-items: center;
cursor: pointer;
display: flex;
height: var(--cr-section-min-height);
}
#stack-trace-heading .subpage-arrow {
display: block;
margin: 0;
}
#stack-trace-heading .subpage-arrow.expanded {
transform: rotate(90deg);
}
#stack-trace-container {
list-style: none;
margin-top: 0;
padding: 0;
}
#stack-trace-container li {
cursor: pointer;
font-family: monospace;
padding: 4px;
}
#stack-trace-container li.selected,
#stack-trace-container li:hover {
background: var(--google-blue-100);
}
#dev-tool-button {
max-width: 300px;
}
</style>
<div id="main">
<div id="heading">
......@@ -105,9 +150,30 @@
could-not-display-code="$i18n{noErrorsToShow}">
</extensions-code-section>
</div>
<div id="devtools-controls">
<!--TODO-->
</div>
<template is="dom-if" if="[[isRuntimeError_]]">
<div id="devtools-controls">
<div id="stack-trace-heading" on-tap="onStackTraceTap_">
<button is="paper-icon-button-light" class$="subpage-arrow
[[getExpandedClass_(stackTraceExpanded_)]]">
</button>
$i18n{stackTrace}
</div>
<ul id="stack-trace-container" hidden="[[!stackTraceExpanded_]]">
<template is="dom-repeat" items="[[shownStackTrace_]]">
<li on-tap="onStackFrameTap_"
hidden="[[!shouldDisplayFrame_(item.url)]]"
class$="[[getStackFrameClass_(item, selectedStackFrame_)]]">
[[getStackTraceLabel_(item)]]
</li>
</template>
</ul>
<paper-button id="dev-tool-button" class="action-button"
disabled="[[!selectedError_.canInspect]]"
on-tap="onDevToolButtonTap_">
$i18n{openInDevtool}
</paper-button>
</div>
</template>
</div>
</template>
<script src="chrome://extensions/error_page.js"></script>
......
......@@ -11,22 +11,37 @@ cr.define('extensions', function() {
'use strict';
/** @interface */
const ErrorPageDelegate = function() {};
ErrorPageDelegate.prototype = {
class ErrorPageDelegate {
/**
* @param {string} extensionId
* @param {!Array<number>=} opt_errorIds
* @param {chrome.developerPrivate.ErrorType=} opt_type
* @param {!Array<number>=} errorIds
* @param {chrome.developerPrivate.ErrorType=} type
*/
deleteErrors: assertNotReached,
deleteErrors(extensionId, errorIds, type) {}
/**
* @param {chrome.developerPrivate.RequestFileSourceProperties} args
* @return {!Promise<!chrome.developerPrivate.RequestFileSourceResponse>}
*/
requestFileSource: assertNotReached,
};
requestFileSource(args) {}
/**
* @param {!chrome.developerPrivate.OpenDevToolsProperties} args
*/
openDevTools(args) {}
}
/**
* Get the URL relative to the main extension url. If the url is
* unassociated with the extension, this will be the full url.
* @param {string} url
* @param {?(ManifestError|RuntimeError)} error
* @return {string}
*/
function getRelativeUrl(url, error) {
const fullUrl = 'chrome-extension://' + error.extensionId + '/';
return url.startsWith(fullUrl) ? url.substring(fullUrl.length) : url;
}
const ErrorPage = Polymer({
is: 'extensions-error-page',
......@@ -40,6 +55,34 @@ cr.define('extensions', function() {
/** @private {?(ManifestError|RuntimeError)} */
selectedError_: Object,
/** @private {?chrome.developerPrivate.StackFrame}*/
selectedStackFrame_: {
type: Object,
value: function() {
return null;
},
},
/** @private */
isRuntimeError_: {
type: Boolean,
computed: 'computeIsRuntimeError_(selectedError_)',
},
shownStackTrace_: {
type: Array,
computed: 'computeShownStackTrace_(selectedError_)',
observer: 'onShownStackTraceChanged_'
},
/** @private */
stackTraceExpanded_: {
type: Boolean,
value: function() {
return false;
},
},
},
observers: [
......@@ -54,9 +97,8 @@ cr.define('extensions', function() {
*/
observeDataChanges_: function() {
assert(this.data);
const e = this.data.manifestErrors[0] || this.data.runtimeErrors[0];
if (e)
this.selectedError_ = e;
this.selectedError_ =
this.data.manifestErrors[0] || this.data.runtimeErrors[0] || null;
},
/**
......@@ -117,6 +159,11 @@ cr.define('extensions', function() {
* @private
*/
onSelectedErrorChanged_: function() {
if (!this.selectedError_) {
this.$['code-section'].code = null;
return;
}
const error = this.selectedError_;
const args = {
extensionId: error.extensionId,
......@@ -141,6 +188,125 @@ cr.define('extensions', function() {
});
},
/**
* @return {boolean}
* @private
*/
computeIsRuntimeError_: function() {
return !!this.selectedError_ &&
this.selectedError_.type == chrome.developerPrivate.ErrorType.RUNTIME;
},
/**
* @return {?Array<!chrome.developerPrivate.StackFrame>}
* @private
*/
computeShownStackTrace_: function() {
// Stack trace not applicable for non-runtime errors.
return this.selectedError_ &&
this.selectedError_.type ==
chrome.developerPrivate.ErrorType.RUNTIME ?
this.selectedError_.stackTrace :
null;
},
/** @private */
onShownStackTraceChanged_: function() {
this.selectedStackFrame_ =
this.shownStackTrace_ ? this.shownStackTrace_[0] : null;
},
/**
* The description is a human-readable summation of the frame, in the
* form "<relative_url>:<line_number> (function)", e.g.
* "myfile.js:25 (myFunction)".
* @param {!chrome.developerPrivate.StackFrame} frame
* @return {string}
* @private
*/
getStackTraceLabel_: function(frame) {
let description = getRelativeUrl(frame.url, this.selectedError_) + ':' +
frame.lineNumber;
if (frame.functionName) {
const functionName = frame.functionName == '(anonymous function)' ?
loadTimeData.getString('anonymousFunction') :
frame.functionName;
description += ' (' + functionName + ')';
}
return description;
},
/** @private */
getExpandedClass_: function() {
return this.stackTraceExpanded_ ? 'expanded' : '';
},
/**
* @param {chrome.developerPrivate.StackFrame} frame
* @return {string}
* @private
*/
getStackFrameClass_: function(frame) {
return frame == this.selectedStackFrame_ ? 'selected' : '';
},
/**
* This function is used to determine whether or not we want to show a
* stack frame. We don't want to show code from internal scripts.
* @param {string} url
* @return {boolean}
* @private
*/
shouldDisplayFrame_: function(url) {
// All our internal scripts are in the 'extensions::' namespace.
return !/^extensions::/.test(url);
},
/**
* @param {!Event} e
* @private
*/
onStackFrameTap_: function(e) {
const frame = (/** @type {!{model:Object}} */ (e)).model.item;
this.selectedStackFrame_ = frame;
this.delegate
.requestFileSource({
extensionId: this.selectedError_.extensionId,
message: this.selectedError_.message,
pathSuffix: getRelativeUrl(frame.url, this.selectedError_),
lineNumber: frame.lineNumber,
})
.then(code => {
this.$['code-section'].code = code;
});
},
/** @private */
onDevToolButtonTap_: function() {
// This guarantees renderProcessId and renderViewId.
assert(
this.selectedError_.type ==
chrome.developerPrivate.ErrorType.RUNTIME);
assert(this.selectedStackFrame_);
this.delegate.openDevTools({
renderProcessId: this.selectedError_.renderProcessId,
renderViewId: this.selectedError_.renderViewId,
url: this.selectedStackFrame_.url,
lineNumber: this.selectedStackFrame_.lineNumber || 0,
columnNumber: this.selectedStackFrame_.columnNumber || 0,
});
},
/** @private */
onStackTraceTap_: function() {
this.stackTraceExpanded_ = !this.stackTraceExpanded_;
},
/**
* Computes the class name for the error item depending on whether its
* the currently selected error.
......
......@@ -317,6 +317,11 @@ cr.define('extensions', function() {
});
});
}
/** @override */
openDevTools(args) {
chrome.developerPrivate.openDevTools(args);
}
}
cr.addSingletonGetter(Service);
......
......@@ -135,6 +135,11 @@ content::WebUIDataSource* CreateMdExtensionsSource() {
IDS_EXTENSIONS_INSTALL_DROP_TARGET);
source->AddLocalizedString("errorsPageHeading",
IDS_MD_EXTENSIONS_ERROR_PAGE_HEADING);
source->AddLocalizedString("anonymousFunction",
IDS_MD_EXTENSIONS_ERROR_ANONYMOUS_FUNCTION);
source->AddLocalizedString("openInDevtool",
IDS_MD_EXTENSIONS_ERROR_LAUNCH_DEVTOOLS);
source->AddLocalizedString("stackTrace", IDS_MD_EXTENSIONS_ERROR_STACK_TRACE);
source->AddLocalizedString("getMoreExtensions",
IDS_MD_EXTENSIONS_SIDEBAR_GET_MORE_EXTENSIONS);
source->AddLocalizedString("keyboardShortcuts",
......
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