Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / extensions / extension_error.js
index 0b2efef..d53f908 100644 (file)
@@ -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);
-
-      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;
-    },
-
-    /**
-     * 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;
-    },
+      this.insertBefore(iconNode, this.firstChild);
 
-    /**
-     * 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;
-      } else {
-        // Prefer |line| if available, or default to the line of the last stack
-        // frame.
-        requestFileSourceArgs.lineNumber =
-            line ? line : this.getLastPosition_('lineNumber');
-      }
+      var messageSpan = this.querySelector('.extension-error-message');
+      messageSpan.textContent = error.message;
+      messageSpan.title = error.message;
 
-      viewSource.addEventListener('click', function(e) {
-        chrome.send('extensionErrorRequestFileSource', [requestFileSourceArgs]);
-      });
-      viewSource.title = loadTimeData.getString('extensionErrorViewSource');
-      return viewSource;
-    },
+      var extensionUrl = 'chrome-extension://' + error.extensionId + '/';
+      var viewDetailsLink = this.querySelector('.extension-error-view-details');
 
-    /**
-     * 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_);
+      // 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 {
-        linkNode.textContent = this.error_.contextUrl;
+        var stringId = extensionUrl.toLowerCase() == 'manifest.json' ?
+            'extensionErrorViewManifest' : 'extensionErrorViewDetails';
+        viewDetailsLink.textContent = loadTimeData.getString(stringId);
+
+        viewDetailsLink.addEventListener('click', function(e) {
+          extensions.ExtensionErrorOverlay.getInstance().setErrorAndShowOverlay(
+              error, extensionUrl);
+        });
       }
-
-      // 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,16 +110,12 @@ 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_) {