| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| cr.define('extensions', function() { |
| 'use strict'; |
| |
| // A RegExp to roughly match acceptable patterns entered by the user. |
| // exec'ing() this RegExp will match the following groups: |
| // 0: Full matched string. |
| // 1: Scheme + scheme separator (e.g., 'https://'). |
| // 2: Scheme only (e.g., 'https'). |
| // 3: Match subdomains ('*.'). |
| // 4: Hostname (e.g., 'example.com'). |
| // 5: Port, including ':' separator (e.g., ':80'). |
| // 6: Path, include '/' separator (e.g., '/*'). |
| const patternRegExp = new RegExp( |
| '^' + |
| // Scheme; optional. |
| '((http|https|\\*)://)?' + |
| // Include subdomains specifier; optional. |
| '(\\*\\.)?' + |
| // Hostname, required. |
| '([a-z0-9\\.-]+\\.[a-z0-9]+)' + |
| // Port, optional. |
| '(:[0-9]+)?' + |
| // Path, optional but if present must be '/' or '/*'. |
| '(\\/\\*|\\/)?' + |
| '$'); |
| |
| function getPatternFromSite(site) { |
| let res = patternRegExp.exec(site); |
| assert(res); |
| let scheme = res[1] || '*://'; |
| let host = (res[3] || '') + res[4]; |
| let port = res[5] || ''; |
| let path = '/*'; |
| return scheme + host + port + path; |
| } |
| |
| const RuntimeHostsDialog = Polymer({ |
| is: 'extensions-runtime-hosts-dialog', |
| |
| properties: { |
| /** @type {!extensions.ItemDelegate} */ |
| delegate: Object, |
| |
| /** @type {string} */ |
| itemId: String, |
| |
| /** |
| * The site that this entry is currently managing. Only non-empty if this |
| * is for editing an existing entry. |
| * @type {?string} |
| */ |
| currentSite: { |
| type: String, |
| value: null, |
| }, |
| |
| /** |
| * Whether the dialog should update the host access to be "on specific |
| * sites" before adding a new host permission. |
| */ |
| updateHostAccess: { |
| type: Boolean, |
| value: false, |
| }, |
| |
| /** |
| * The site to add an exception for. |
| * @private |
| */ |
| site_: String, |
| |
| /** |
| * Whether the currently-entered input is valid. |
| * @private |
| */ |
| inputInvalid_: { |
| type: Boolean, |
| value: false, |
| }, |
| }, |
| |
| /** @override */ |
| attached: function() { |
| if (this.currentSite !== null) { |
| this.site_ = this.currentSite; |
| this.validate_(); |
| } |
| this.$.dialog.showModal(); |
| }, |
| |
| /** @return {boolean} */ |
| isOpen: function() { |
| return this.$.dialog.open; |
| }, |
| |
| /** |
| * Validates that the pattern entered is valid. |
| * @private |
| */ |
| validate_: function() { |
| // If input is empty, disable the action button, but don't show the red |
| // invalid message. |
| if (this.site_.trim().length == 0) { |
| this.inputInvalid_ = false; |
| return; |
| } |
| |
| let valid = patternRegExp.test(this.site_); |
| this.inputInvalid_ = !valid; |
| }, |
| |
| /** |
| * @return {string} |
| * @private |
| */ |
| computeDialogTitle_: function() { |
| const stringId = this.currentSite === null ? 'runtimeHostsDialogTitle' : |
| 'hostPermissionsEdit'; |
| return loadTimeData.getString(stringId); |
| }, |
| |
| /** |
| * @return {boolean} |
| * @private |
| */ |
| computeSubmitButtonDisabled_: function() { |
| return this.inputInvalid_ || this.site_.trim().length == 0; |
| }, |
| |
| /** |
| * @return {string} |
| * @private |
| */ |
| computeSubmitButtonLabel_: function() { |
| const stringId = this.currentSite === null ? 'add' : 'save'; |
| return loadTimeData.getString(stringId); |
| }, |
| |
| /** @private */ |
| onCancelTap_: function() { |
| this.$.dialog.cancel(); |
| }, |
| |
| /** |
| * The tap handler for the submit button (adds the pattern and closes |
| * the dialog). |
| * @private |
| */ |
| onSubmitTap_: function() { |
| if (this.currentSite !== null) |
| this.handleEdit_(); |
| else |
| this.handleAdd_(); |
| }, |
| |
| /** |
| * Handles adding a new site entry. |
| * @private |
| */ |
| handleAdd_: function() { |
| assert(!this.currentSite); |
| |
| if (this.updateHostAccess) { |
| this.delegate.setItemHostAccess( |
| this.itemId, chrome.developerPrivate.HostAccess.ON_SPECIFIC_SITES); |
| } |
| |
| this.addPermission_(); |
| }, |
| |
| /** |
| * Handles editing an existing site entry. |
| * @private |
| */ |
| handleEdit_: function() { |
| assert(this.currentSite); |
| assert( |
| !this.updateHostAccess, |
| 'Editing host permissions should only be possible if the host ' + |
| 'access is already set to specific sites.'); |
| |
| if (this.currentSite == this.site_) { |
| // No change in values, so no need to update anything. |
| this.$.dialog.close(); |
| return; |
| } |
| |
| // Editing an existing entry is done by removing the current site entry, |
| // and then adding the new one. |
| this.delegate.removeRuntimeHostPermission(this.itemId, this.currentSite) |
| .then(() => { |
| this.addPermission_(); |
| }); |
| }, |
| |
| /** |
| * Adds the runtime host permission through the delegate. If successful, |
| * closes the dialog; otherwise displays the invalid input message. |
| * @private |
| */ |
| addPermission_: function() { |
| let pattern = getPatternFromSite(this.site_); |
| this.delegate.addRuntimeHostPermission(this.itemId, pattern) |
| .then( |
| () => { |
| this.$.dialog.close(); |
| }, |
| () => { |
| this.inputInvalid_ = true; |
| }); |
| }, |
| }); |
| |
| return { |
| RuntimeHostsDialog: RuntimeHostsDialog, |
| getPatternFromSite: getPatternFromSite |
| }; |
| }); |