summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/browser/resources/extensions/extension_error.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/resources/extensions/extension_error.js')
-rw-r--r--chromium/chrome/browser/resources/extensions/extension_error.js326
1 files changed, 56 insertions, 270 deletions
diff --git a/chromium/chrome/browser/resources/extensions/extension_error.js b/chromium/chrome/browser/resources/extensions/extension_error.js
index 0b2efef4ef7..9ef47cf73d8 100644
--- a/chromium/chrome/browser/resources/extensions/extension_error.js
+++ b/chromium/chrome/browser/resources/extensions/extension_error.js
@@ -2,32 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+<include src="extension_error_overlay.js"></include>
+
cr.define('extensions', function() {
'use strict';
/**
- * Returns whether or not a given |url| is associated with an extension.
- * @param {string} url The url to examine.
- * @param {string} extensionUrl The url of the extension.
- * @return {boolean} Whether or not the url is associated with the extension.
- */
- function isExtensionUrl(url, extensionUrl) {
- return url.substring(0, extensionUrl.length) == extensionUrl;
- }
-
- /**
- * 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 The url to make relative.
- * @param {string} extensionUrl The host for which the url is relative.
- * @return {string} The url relative to the host.
- */
- function getRelativeUrl(url, extensionUrl) {
- return isExtensionUrl(url, extensionUrl) ?
- url.substring(extensionUrl.length) : url;
- }
-
- /**
* Clone a template within the extension error template collection.
* @param {string} templateName The class name of the template to clone.
* @return {HTMLElement} The clone of the template.
@@ -38,19 +18,26 @@ cr.define('extensions', function() {
}
/**
+ * Checks that an Extension ID follows the proper format (i.e., is 32
+ * characters long, is lowercase, and contains letters in the range [a, p]).
+ * @param {string} id The Extension ID to test.
+ * @return {boolean} Whether or not the ID is valid.
+ */
+ function idIsValid(id) {
+ return /^[a-p]{32}$/.test(id);
+ }
+
+ /**
* Creates a new ExtensionError HTMLElement; this is used to show a
* notification to the user when an error is caused by an extension.
* @param {Object} error The error the element should represent.
- * @param {string} templateName The name of the template to clone for the
- * error ('extension-error-[detailed|simple]-wrapper').
* @constructor
* @extends {HTMLDivElement}
*/
- function ExtensionError(error, templateName) {
- var div = cloneTemplate(templateName);
+ function ExtensionError(error) {
+ var div = cloneTemplate('extension-error-metadata');
div.__proto__ = ExtensionError.prototype;
- div.error_ = error;
- div.decorate();
+ div.decorate(error);
return div;
}
@@ -58,235 +45,41 @@ cr.define('extensions', function() {
__proto__: HTMLDivElement.prototype,
/** @override */
- decorate: function() {
- var metadata = cloneTemplate('extension-error-metadata');
-
+ decorate: function(error) {
// Add an additional class for the severity level.
- if (this.error_.level == 0)
- metadata.classList.add('extension-error-severity-info');
- else if (this.error_.level == 1)
- metadata.classList.add('extension-error-severity-warning');
+ if (error.level == 0)
+ this.classList.add('extension-error-severity-info');
+ else if (error.level == 1)
+ this.classList.add('extension-error-severity-warning');
else
- metadata.classList.add('extension-error-severity-fatal');
+ this.classList.add('extension-error-severity-fatal');
var iconNode = document.createElement('img');
iconNode.className = 'extension-error-icon';
- metadata.insertBefore(iconNode, metadata.firstChild);
-
- // Add a property for the extension's base url in order to determine if
- // a url belongs to the extension.
- this.extensionUrl_ =
- 'chrome-extension://' + this.error_.extensionId + '/';
-
- metadata.querySelector('.extension-error-message').textContent =
- this.error_.message;
-
- metadata.appendChild(this.createViewSourceAndInspect_(
- getRelativeUrl(this.error_.source, this.extensionUrl_),
- this.error_.source));
-
- // The error template may specify a <summary> to put template metadata in.
- // If not, just append it to the top-level element.
- var metadataContainer = this.querySelector('summary') || this;
- metadataContainer.appendChild(metadata);
+ this.insertBefore(iconNode, this.firstChild);
- var detailsNode = this.querySelector('.extension-error-details');
- if (detailsNode && this.error_.contextUrl)
- detailsNode.appendChild(this.createContextNode_());
- if (detailsNode && this.error_.stackTrace) {
- var stackNode = this.createStackNode_();
- if (stackNode)
- detailsNode.appendChild(this.createStackNode_());
- }
- },
-
- /**
- * Return a div with text |description|. If it's possible to view the source
- * for |url|, linkify the div to do so. Attach an inspect button if it's
- * possible to open the inspector for |url|.
- * @param {string} description a human-friendly description the location
- * (e.g., filename, line).
- * @param {string} url The url of the resource to view.
- * @param {?number} line An optional line number of the resource.
- * @param {?number} column An optional column number of the resource.
- * @return {HTMLElement} The created node, either a link or plaintext.
- * @private
- */
- createViewSourceAndInspect_: function(description, url, line, column) {
- var errorLinks = document.createElement('div');
- errorLinks.className = 'extension-error-links';
-
- if (this.error_.canInspect)
- errorLinks.appendChild(this.createInspectLink_(url, line, column));
-
- if (this.canViewSource_(url))
- var viewSource = this.createViewSourceLink_(url, line);
- else
- var viewSource = document.createElement('div');
- viewSource.className = 'extension-error-view-source';
- viewSource.textContent = description;
- errorLinks.appendChild(viewSource);
- return errorLinks;
- },
+ var messageSpan = this.querySelector('.extension-error-message');
+ messageSpan.textContent = error.message;
+ messageSpan.title = error.message;
- /**
- * Determine whether we can view the source of a given url.
- * @param {string} url The url of the resource to view.
- * @return {boolean} Whether or not we can view the source for the url.
- * @private
- */
- canViewSource_: function(url) {
- return isExtensionUrl(url, this.extensionUrl_) || url == 'manifest.json';
- },
-
- /**
- * Determine whether or not we should display the url to the user. We don't
- * want to include any of our own code in stack traces.
- * @param {string} url The url in question.
- * @return {boolean} True if the url should be displayed, and false
- * otherwise (i.e., if it is an internal script).
- */
- shouldDisplayForUrl_: function(url) {
- var extensionsNamespace = 'extensions::';
- // All our internal scripts are in the 'extensions::' namespace.
- return url.substr(0, extensionsNamespace.length) != extensionsNamespace;
- },
+ var extensionUrl = 'chrome-extension://' + error.extensionId + '/';
+ var viewDetailsLink = this.querySelector('.extension-error-view-details');
- /**
- * Create a clickable node to view the source for the given url.
- * @param {string} url The url to the resource to view.
- * @param {?number} line An optional line number of the resource (for
- * source files).
- * @return {HTMLElement} The clickable node to view the source.
- * @private
- */
- createViewSourceLink_: function(url, line) {
- var viewSource = document.createElement('a');
- viewSource.href = 'javascript:void(0)';
- var relativeUrl = getRelativeUrl(url, this.extensionUrl_);
- var requestFileSourceArgs = { 'extensionId': this.error_.extensionId,
- 'message': this.error_.message,
- 'pathSuffix': relativeUrl };
- if (relativeUrl == 'manifest.json') {
- requestFileSourceArgs.manifestKey = this.error_.manifestKey;
- requestFileSourceArgs.manifestSpecific = this.error_.manifestSpecific;
+ // If we cannot open the file source and there are no external frames in
+ // the stack, then there are no details to display.
+ if (!extensions.ExtensionErrorOverlay.canShowOverlayForError(
+ error, extensionUrl)) {
+ viewDetailsLink.hidden = true;
} else {
- // Prefer |line| if available, or default to the line of the last stack
- // frame.
- requestFileSourceArgs.lineNumber =
- line ? line : this.getLastPosition_('lineNumber');
+ var stringId = extensionUrl.toLowerCase() == 'manifest.json' ?
+ 'extensionErrorViewManifest' : 'extensionErrorViewDetails';
+ viewDetailsLink.textContent = loadTimeData.getString(stringId);
+
+ viewDetailsLink.addEventListener('click', function(e) {
+ extensions.ExtensionErrorOverlay.getInstance().setErrorAndShowOverlay(
+ error, extensionUrl);
+ });
}
-
- viewSource.addEventListener('click', function(e) {
- chrome.send('extensionErrorRequestFileSource', [requestFileSourceArgs]);
- });
- viewSource.title = loadTimeData.getString('extensionErrorViewSource');
- return viewSource;
- },
-
- /**
- * Check the most recent stack frame to get the last position in the code.
- * @param {string} type The position type, i.e. '[line|column]Number'.
- * @return {?number} The last position of the given |type|, or undefined if
- * there is no stack trace to check.
- * @private
- */
- getLastPosition_: function(type) {
- var stackTrace = this.error_.stackTrace;
- return stackTrace && stackTrace[0] ? stackTrace[0][type] : undefined;
- },
-
- /**
- * Create an "Inspect" link, in the form of an icon.
- * @param {?string} url The url of the resource to inspect; if absent, the
- * render view (and no particular resource) is inspected.
- * @param {?number} line An optional line number of the resource.
- * @param {?number} column An optional column number of the resource.
- * @return {HTMLImageElement} The created "Inspect" link for the resource.
- * @private
- */
- createInspectLink_: function(url, line, column) {
- var linkWrapper = document.createElement('a');
- linkWrapper.href = 'javascript:void(0)';
- var inspectIcon = document.createElement('img');
- inspectIcon.className = 'extension-error-inspect';
- inspectIcon.title = loadTimeData.getString('extensionErrorInspect');
-
- inspectIcon.addEventListener('click', function(e) {
- chrome.send('extensionErrorOpenDevTools',
- [{'renderProcessId': this.error_.renderProcessId,
- 'renderViewId': this.error_.renderViewId,
- 'url': url,
- 'lineNumber': line ? line :
- this.getLastPosition_('lineNumber'),
- 'columnNumber': column ? column :
- this.getLastPosition_('columnNumber')}]);
- }.bind(this));
- linkWrapper.appendChild(inspectIcon);
- return linkWrapper;
- },
-
- /**
- * Get the context node for this error. This will attempt to link to the
- * context in which the error occurred, and can be either an extension page
- * or an external page.
- * @return {HTMLDivElement} The context node for the error, including the
- * label and a link to the context.
- * @private
- */
- createContextNode_: function() {
- var node = cloneTemplate('extension-error-context-wrapper');
- var linkNode = node.querySelector('a');
- if (isExtensionUrl(this.error_.contextUrl, this.extensionUrl_)) {
- linkNode.textContent = getRelativeUrl(this.error_.contextUrl,
- this.extensionUrl_);
- } else {
- linkNode.textContent = this.error_.contextUrl;
- }
-
- // Prepend a link to inspect the context page, if possible.
- if (this.error_.canInspect)
- node.insertBefore(this.createInspectLink_(), linkNode);
-
- linkNode.href = this.error_.contextUrl;
- linkNode.target = '_blank';
- return node;
- },
-
- /**
- * Get a node for the stack trace for this error. Each stack frame will
- * include a resource url, line number, and function name (possibly
- * anonymous). If possible, these frames will also be linked for viewing the
- * source and inspection.
- * @return {HTMLDetailsElement} The stack trace node for this error, with
- * all stack frames nested in a details-summary object.
- * @private
- */
- createStackNode_: function() {
- var node = cloneTemplate('extension-error-stack-trace');
- var listNode = node.querySelector('.extension-error-stack-trace-list');
- this.error_.stackTrace.forEach(function(frame) {
- if (!this.shouldDisplayForUrl_(frame.url))
- return;
- var frameNode = document.createElement('div');
- var description = getRelativeUrl(frame.url, this.extensionUrl_) +
- ':' + frame.lineNumber;
- if (frame.functionName) {
- var functionName = frame.functionName == '(anonymous function)' ?
- loadTimeData.getString('extensionErrorAnonymousFunction') :
- frame.functionName;
- description += ' (' + functionName + ')';
- }
- frameNode.appendChild(this.createViewSourceAndInspect_(
- description, frame.url, frame.lineNumber, frame.columnNumber));
- listNode.appendChild(
- document.createElement('li')).appendChild(frameNode);
- }, this);
-
- if (listNode.childElementCount == 0)
- return undefined;
-
- return node;
},
};
@@ -294,16 +87,13 @@ cr.define('extensions', function() {
* A variable length list of runtime or manifest errors for a given extension.
* @param {Array.<Object>} errors The list of extension errors with which
* to populate the list.
- * @param {string} title The i18n key for the title of the error list, i.e.
- * 'extensionErrors[Manifest,Runtime]Errors'.
* @constructor
* @extends {HTMLDivElement}
*/
- function ExtensionErrorList(errors, title) {
+ function ExtensionErrorList(errors) {
var div = cloneTemplate('extension-error-list');
div.__proto__ = ExtensionErrorList.prototype;
div.errors_ = errors;
- div.title_ = title;
div.decorate();
return div;
}
@@ -320,25 +110,16 @@ cr.define('extensions', function() {
/** @override */
decorate: function() {
- this.querySelector('.extension-error-list-title').textContent =
- loadTimeData.getString(this.title_);
-
this.contents_ = this.querySelector('.extension-error-list-contents');
this.errors_.forEach(function(error) {
- this.contents_.appendChild(document.createElement('li')).appendChild(
- new ExtensionError(error,
- error.contextUrl || error.stackTrace ?
- 'extension-error-detailed-wrapper' :
- 'extension-error-simple-wrapper'));
+ if (idIsValid(error.extensionId)) {
+ this.contents_.appendChild(document.createElement('li')).appendChild(
+ new ExtensionError(error));
+ }
}, this);
- if (this.contents_.children.length > this.MAX_ERRORS_TO_SHOW_) {
- for (var i = this.MAX_ERRORS_TO_SHOW_;
- i < this.contents_.children.length; ++i) {
- this.contents_.children[i].hidden = true;
- }
+ if (this.contents_.children.length > this.MAX_ERRORS_TO_SHOW_)
this.initShowMoreButton_();
- }
},
/**
@@ -347,14 +128,19 @@ cr.define('extensions', function() {
* @private
*/
initShowMoreButton_: function() {
- var button = this.querySelector('.extension-error-list-show-more a');
+ var button = this.querySelector('.extension-error-list-show-more button');
button.hidden = false;
button.isShowingAll = false;
+ var listContents = this.querySelector('.extension-error-list-contents');
+ listContents.addEventListener('webkitTransitionEnd', function(e) {
+ if (listContents.classList.contains('active'))
+ listContents.classList.add('scrollable');
+ });
button.addEventListener('click', function(e) {
- for (var i = this.MAX_ERRORS_TO_SHOW_;
- i < this.contents_.children.length; ++i) {
- this.contents_.children[i].hidden = button.isShowingAll;
- }
+ // Disable scrolling while transitioning. If the element is active,
+ // scrolling is enabled when the transition ends.
+ listContents.classList.toggle('active');
+ listContents.classList.remove('scrollable');
var message = button.isShowingAll ? 'extensionErrorsShowMore' :
'extensionErrorsShowFewer';
button.textContent = loadTimeData.getString(message);