Commit f97b7c27 authored by ericzeng's avatar ericzeng Committed by Commit bot

Open embedded extension options from the extension action context menu

This CL allows the extension action context menu to use the new embedded
extension options feature. If the enable-embedded-extension-options flag
is enabled, instead of opening an extension's options page in it's own
tab, it will open chrome://extensions, scroll down to the correct
extension, and then open the embedded options page popup.

The context menu opens a link to chrome://extensions/ with the query
string ?options=|extensionId|. This query string is appended and
removed whenever the embedded extension options popup is opened or
closed.

BUG=386842

Review URL: https://codereview.chromium.org/484033003

Cr-Commit-Position: refs/heads/master@{#291826}
parent 94f4bd9c
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "apps/app_window.h" #include "apps/app_window.h"
#include "apps/app_window_registry.h" #include "apps/app_window_registry.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/chrome_extension_function.h" #include "chrome/browser/extensions/chrome_extension_function.h"
#include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/extensions/tab_helper.h"
...@@ -19,6 +20,7 @@ ...@@ -19,6 +20,7 @@
#include "chrome/browser/ui/browser_iterator.h" #include "chrome/browser/ui/browser_iterator.h"
#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/extensions/api/tabs.h" #include "chrome/common/extensions/api/tabs.h"
...@@ -31,6 +33,7 @@ ...@@ -31,6 +33,7 @@
#include "extensions/common/constants.h" #include "extensions/common/constants.h"
#include "extensions/common/error_utils.h" #include "extensions/common/error_utils.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/feature_switch.h"
#include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/incognito_info.h" #include "extensions/common/manifest_handlers/incognito_info.h"
#include "extensions/common/permissions/api_permission.h" #include "extensions/common/permissions/api_permission.h"
...@@ -566,16 +569,33 @@ void ExtensionTabUtil::OpenOptionsPage(const Extension* extension, ...@@ -566,16 +569,33 @@ void ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
browser = displayer->browser(); browser = displayer->browser();
} }
content::OpenURLParams params(ManifestURL::GetOptionsPage(extension), if (FeatureSwitch::embedded_extension_options()->IsEnabled()) {
content::Referrer(), // If embedded extension options are enabled, open chrome://extensions
SINGLETON_TAB, // in a new tab and show the extension options in an embedded popup.
content::PAGE_TRANSITION_LINK, chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams(
false); browser, GURL(chrome::kChromeUIExtensionsURL)));
browser->OpenURL(params); params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
browser->window()->Show();
WebContents* web_contents = GURL::Replacements replacements;
browser->tab_strip_model()->GetActiveWebContents(); std::string query =
web_contents->GetDelegate()->ActivateContents(web_contents); base::StringPrintf("options=%s", extension->id().c_str());
replacements.SetQueryStr(query);
params.url = params.url.ReplaceComponents(replacements);
chrome::ShowSingletonTabOverwritingNTP(browser, params);
} else {
// Otherwise open a new tab with the extension's options page
content::OpenURLParams params(ManifestURL::GetOptionsPage(extension),
content::Referrer(),
SINGLETON_TAB,
content::PAGE_TRANSITION_LINK,
false);
browser->OpenURL(params);
browser->window()->Show();
WebContents* web_contents =
browser->tab_strip_model()->GetActiveWebContents();
web_contents->GetDelegate()->ActivateContents(web_contents);
}
} }
} // namespace extensions } // namespace extensions
...@@ -33,6 +33,15 @@ cr.define('options', function() { ...@@ -33,6 +33,15 @@ cr.define('options', function() {
ExtensionsList.prototype = { ExtensionsList.prototype = {
__proto__: HTMLDivElement.prototype, __proto__: HTMLDivElement.prototype,
/**
* Indicates whether an embedded options page that was navigated to through
* the '?options=' URL query has been shown to the user. This is necessary
* to prevent showExtensionNodes_ from opening the options more than once.
* @type {boolean}
* @private
*/
optionsShown_: false,
/** @override */ /** @override */
decorate: function() { decorate: function() {
this.textContent = ''; this.textContent = '';
...@@ -44,6 +53,10 @@ cr.define('options', function() { ...@@ -44,6 +53,10 @@ cr.define('options', function() {
return parseQueryParams(document.location)['id']; return parseQueryParams(document.location)['id'];
}, },
getOptionsQueryParam_: function() {
return parseQueryParams(document.location)['options'];
},
/** /**
* Creates all extension items from scratch. * Creates all extension items from scratch.
* @private * @private
...@@ -53,16 +66,12 @@ cr.define('options', function() { ...@@ -53,16 +66,12 @@ cr.define('options', function() {
this.data_.extensions.forEach(this.createNode_, this); this.data_.extensions.forEach(this.createNode_, this);
var idToHighlight = this.getIdQueryParam_(); var idToHighlight = this.getIdQueryParam_();
if (idToHighlight && $(idToHighlight)) { if (idToHighlight && $(idToHighlight))
// Scroll offset should be calculated slightly higher than the actual this.scrollToNode_(idToHighlight);
// offset of the element being scrolled to, so that it ends up not all
// the way at the top. That way it is clear that there are more elements var idToOpenOptions = this.getOptionsQueryParam_();
// above the element being scrolled to. if (idToOpenOptions && $(idToOpenOptions))
var scrollFudge = 1.2; this.showEmbeddedExtensionOptions_(idToOpenOptions, true);
var scrollTop = $(idToHighlight).offsetTop - scrollFudge *
$(idToHighlight).clientHeight;
setScrollTopForDocument(document, scrollTop);
}
if (this.data_.extensions.length == 0) if (this.data_.extensions.length == 0)
this.classList.add('empty-extension-list'); this.classList.add('empty-extension-list');
...@@ -70,6 +79,22 @@ cr.define('options', function() { ...@@ -70,6 +79,22 @@ cr.define('options', function() {
this.classList.remove('empty-extension-list'); this.classList.remove('empty-extension-list');
}, },
/**
* Scrolls the page down to the extension node with the given id.
* @param {string} extensionId The id of the extension to scroll to.
* @private
*/
scrollToNode_: function(extensionId) {
// Scroll offset should be calculated slightly higher than the actual
// offset of the element being scrolled to, so that it ends up not all
// the way at the top. That way it is clear that there are more elements
// above the element being scrolled to.
var scrollFudge = 1.2;
var scrollTop = $(extensionId).offsetTop - scrollFudge *
$(extensionId).clientHeight;
setScrollTopForDocument(document, scrollTop);
},
/** /**
* Synthesizes and initializes an HTML element for the extension metadata * Synthesizes and initializes an HTML element for the extension metadata
* given in |extension|. * given in |extension|.
...@@ -194,10 +219,7 @@ cr.define('options', function() { ...@@ -194,10 +219,7 @@ cr.define('options', function() {
var options = node.querySelector('.options-link'); var options = node.querySelector('.options-link');
options.addEventListener('click', function(e) { options.addEventListener('click', function(e) {
if (this.data_.enableEmbeddedExtensionOptions) { if (this.data_.enableEmbeddedExtensionOptions) {
extensions.ExtensionOptionsOverlay.getInstance(). this.showEmbeddedExtensionOptions_(extension.id, false);
setExtensionAndShowOverlay(extension.id,
extension.name,
extension.icon);
} else { } else {
chrome.send('extensionSettingsOptions', [extension.id]); chrome.send('extensionSettingsOptions', [extension.id]);
} }
...@@ -417,6 +439,43 @@ cr.define('options', function() { ...@@ -417,6 +439,43 @@ cr.define('options', function() {
setScrollTopForDocument(document, topScroll); setScrollTopForDocument(document, topScroll);
} }
}, },
/**
* Opens the extension options overlay for the extension with the given id.
* @param {string} extensionId The id of extension whose options page should
* be displayed.
* @param {boolean} scroll Whether the page should scroll to the extension
* @private
*/
showEmbeddedExtensionOptions_: function(extensionId, scroll) {
if (this.optionsShown_)
return;
// Get the extension from the given id.
var extension = this.data_.extensions.filter(function(extension) {
return extension.id == extensionId;
})[0];
if (!extension)
return;
if (scroll)
this.scrollToNode_(extensionId);
// Add the options query string. Corner case: the 'options' query string
// will clobber the 'id' query string if the options link is clicked when
// 'id' is in the URL, or if both query strings are in the URL.
uber.replaceState({}, '?options=' + extensionId);
extensions.ExtensionOptionsOverlay.getInstance().
setExtensionAndShowOverlay(extensionId,
extension.name,
extension.icon);
this.optionsShown_ = true;
$('overlay').addEventListener('cancelOverlay', function() {
this.optionsShown_ = false;
}.bind(this));
},
}; };
return { return {
......
...@@ -55,6 +55,9 @@ cr.define('extensions', function() { ...@@ -55,6 +55,9 @@ cr.define('extensions', function() {
$('extension-options-overlay-guest').removeChild(extensionoptions); $('extension-options-overlay-guest').removeChild(extensionoptions);
$('extension-options-overlay-icon').removeAttribute('src'); $('extension-options-overlay-icon').removeAttribute('src');
// Remove the options query string.
uber.replaceState({}, '');
}, },
/** /**
...@@ -94,7 +97,7 @@ cr.define('extensions', function() { ...@@ -94,7 +97,7 @@ cr.define('extensions', function() {
extensionoptions.setDeferAutoSize(true); extensionoptions.setDeferAutoSize(true);
extensionoptions.onclose = function() { extensionoptions.onclose = function() {
this.handleDismiss_(); cr.dispatchSimpleEvent($('overlay'), 'cancelOverlay');
}.bind(this); }.bind(this);
// Resize the overlay if the <extensionoptions> changes size. // Resize the overlay if the <extensionoptions> changes size.
......
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