diff options
Diffstat (limited to 'chromium/chrome/browser/resources/extensions/extension_error.js')
-rw-r--r-- | chromium/chrome/browser/resources/extensions/extension_error.js | 326 |
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); |