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 @@ ...@@ -7,6 +7,15 @@
<message name="IDS_MD_EXTENSIONS_ERROR_PAGE_HEADING" desc="The heading of the page displaying an extension's errors."> <message name="IDS_MD_EXTENSIONS_ERROR_PAGE_HEADING" desc="The heading of the page displaying an extension's errors.">
Errors Errors
</message> </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."> <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 Errors
</message> </message>
......
<link rel="import" href="chrome://resources/html/polymer.html"> <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/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/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-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/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-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://resources/polymer/v1_0/paper-styles/color.html">
<link rel="import" href="chrome://extensions/code_section.html"> <link rel="import" href="chrome://extensions/code_section.html">
...@@ -13,7 +15,7 @@ ...@@ -13,7 +15,7 @@
<dom-module id="extensions-error-page"> <dom-module id="extensions-error-page">
<template> <template>
<style include="cr-icons cr-hidden-style"> <style include="paper-button-style cr-icons cr-shared-style">
:host { :host {
display: block; display: block;
height: 100%; height: 100%;
...@@ -77,6 +79,49 @@ ...@@ -77,6 +79,49 @@
.icon-severity-fatal { .icon-severity-fatal {
content: url(error_severity_fatal.png); 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> </style>
<div id="main"> <div id="main">
<div id="heading"> <div id="heading">
...@@ -105,9 +150,30 @@ ...@@ -105,9 +150,30 @@
could-not-display-code="$i18n{noErrorsToShow}"> could-not-display-code="$i18n{noErrorsToShow}">
</extensions-code-section> </extensions-code-section>
</div> </div>
<div id="devtools-controls"> <template is="dom-if" if="[[isRuntimeError_]]">
<!--TODO--> <div id="devtools-controls">
</div> <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> </div>
</template> </template>
<script src="chrome://extensions/error_page.js"></script> <script src="chrome://extensions/error_page.js"></script>
......
...@@ -11,22 +11,37 @@ cr.define('extensions', function() { ...@@ -11,22 +11,37 @@ cr.define('extensions', function() {
'use strict'; 'use strict';
/** @interface */ /** @interface */
const ErrorPageDelegate = function() {}; class ErrorPageDelegate {
ErrorPageDelegate.prototype = {
/** /**
* @param {string} extensionId * @param {string} extensionId
* @param {!Array<number>=} opt_errorIds * @param {!Array<number>=} errorIds
* @param {chrome.developerPrivate.ErrorType=} opt_type * @param {chrome.developerPrivate.ErrorType=} type
*/ */
deleteErrors: assertNotReached, deleteErrors(extensionId, errorIds, type) {}
/** /**
* @param {chrome.developerPrivate.RequestFileSourceProperties} args * @param {chrome.developerPrivate.RequestFileSourceProperties} args
* @return {!Promise<!chrome.developerPrivate.RequestFileSourceResponse>} * @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({ const ErrorPage = Polymer({
is: 'extensions-error-page', is: 'extensions-error-page',
...@@ -40,6 +55,34 @@ cr.define('extensions', function() { ...@@ -40,6 +55,34 @@ cr.define('extensions', function() {
/** @private {?(ManifestError|RuntimeError)} */ /** @private {?(ManifestError|RuntimeError)} */
selectedError_: Object, 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: [ observers: [
...@@ -54,9 +97,8 @@ cr.define('extensions', function() { ...@@ -54,9 +97,8 @@ cr.define('extensions', function() {
*/ */
observeDataChanges_: function() { observeDataChanges_: function() {
assert(this.data); assert(this.data);
const e = this.data.manifestErrors[0] || this.data.runtimeErrors[0]; this.selectedError_ =
if (e) this.data.manifestErrors[0] || this.data.runtimeErrors[0] || null;
this.selectedError_ = e;
}, },
/** /**
...@@ -117,6 +159,11 @@ cr.define('extensions', function() { ...@@ -117,6 +159,11 @@ cr.define('extensions', function() {
* @private * @private
*/ */
onSelectedErrorChanged_: function() { onSelectedErrorChanged_: function() {
if (!this.selectedError_) {
this.$['code-section'].code = null;
return;
}
const error = this.selectedError_; const error = this.selectedError_;
const args = { const args = {
extensionId: error.extensionId, extensionId: error.extensionId,
...@@ -141,6 +188,125 @@ cr.define('extensions', function() { ...@@ -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 * Computes the class name for the error item depending on whether its
* the currently selected error. * the currently selected error.
......
...@@ -317,6 +317,11 @@ cr.define('extensions', function() { ...@@ -317,6 +317,11 @@ cr.define('extensions', function() {
}); });
}); });
} }
/** @override */
openDevTools(args) {
chrome.developerPrivate.openDevTools(args);
}
} }
cr.addSingletonGetter(Service); cr.addSingletonGetter(Service);
......
...@@ -135,6 +135,11 @@ content::WebUIDataSource* CreateMdExtensionsSource() { ...@@ -135,6 +135,11 @@ content::WebUIDataSource* CreateMdExtensionsSource() {
IDS_EXTENSIONS_INSTALL_DROP_TARGET); IDS_EXTENSIONS_INSTALL_DROP_TARGET);
source->AddLocalizedString("errorsPageHeading", source->AddLocalizedString("errorsPageHeading",
IDS_MD_EXTENSIONS_ERROR_PAGE_HEADING); 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", source->AddLocalizedString("getMoreExtensions",
IDS_MD_EXTENSIONS_SIDEBAR_GET_MORE_EXTENSIONS); IDS_MD_EXTENSIONS_SIDEBAR_GET_MORE_EXTENSIONS);
source->AddLocalizedString("keyboardShortcuts", 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