Commit 8aa63120 authored by Ovidio Henriquez's avatar Ovidio Henriquez Committed by Commit Bot

Add chooser-exception-list Polymer element

This change adds the chooser-exception-list Polymer element to site
settings. This element will fetch the chooser exceptions from the C++
site and process them for display. The element uses a
chooser-exception-list-entry to render each chooser exception in the
list.

This change also adds to ability for the paper-tooltip to have its
target set manually. This allows the target to be set on the common
paper-tooltip element used in chooser-exception-list and site-list
without having to access its private `_target` property.

Design doc:
https://docs.google.com/document/d/1MPvsrWiVD_jAC8ELyk8njFpy6j1thfVU5aWT3TCWE8w

Bug: 854329
Change-Id: I3f127d2cf655b2d75157d7174a9bd5e434b583a8
Reviewed-on: https://chromium-review.googlesource.com/c/1357556
Commit-Queue: Ovidio Henriquez <odejesush@chromium.org>
Reviewed-by: default avatarDan Beam <dbeam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#622059}
parent 43fad067
...@@ -20,12 +20,11 @@ ...@@ -20,12 +20,11 @@
<link rel="import" href="../site_settings/category_default_setting.html"> <link rel="import" href="../site_settings/category_default_setting.html">
<link rel="import" href="../site_settings/category_setting_exceptions.html"> <link rel="import" href="../site_settings/category_setting_exceptions.html">
<!-- <!--
In order for the browser test for chooser-exception-list-entry to work In order for the browser test for chooser-exception-list to work
properly, the custom element needs to be a dependency of the settings page. properly, the custom element needs to be a dependency of the settings page.
TODO(https://crbug.com/854329): Update this import once TODO(https://crbug.com/854329): Delete this comment once this element is used.
chooser-exception-list is implemented.
--> -->
<link rel="import" href="../site_settings/chooser_exception_list_entry.html"> <link rel="import" href="../site_settings/chooser_exception_list.html">
<link rel="import" href="../site_settings/constants.html"> <link rel="import" href="../site_settings/constants.html">
<link rel="import" href="../site_settings/media_picker.html"> <link rel="import" href="../site_settings/media_picker.html">
<link rel="import" href="../site_settings/pdf_documents.html"> <link rel="import" href="../site_settings/pdf_documents.html">
......
...@@ -119,6 +119,12 @@ ...@@ -119,6 +119,12 @@
<structure name="IDR_SETTINGS_CATEGORY_SETTING_EXCEPTIONS_JS" <structure name="IDR_SETTINGS_CATEGORY_SETTING_EXCEPTIONS_JS"
file="site_settings/category_setting_exceptions.js" file="site_settings/category_setting_exceptions.js"
type="chrome_html" /> type="chrome_html" />
<structure name="IDR_SETTINGS_CHOOSER_EXCEPTION_LIST_HTML"
file="site_settings/chooser_exception_list.html"
type="chrome_html" />
<structure name="IDR_SETTINGS_CHOOSER_EXCEPTION_LIST_JS"
file="site_settings/chooser_exception_list.js"
type="chrome_html" />
<structure name="IDR_SETTINGS_CHOOSER_EXCEPTION_LIST_ENTRY_HTML" <structure name="IDR_SETTINGS_CHOOSER_EXCEPTION_LIST_ENTRY_HTML"
file="site_settings/chooser_exception_list_entry.html" file="site_settings/chooser_exception_list_entry.html"
type="chrome_html" /> type="chrome_html" />
......
...@@ -10,6 +10,7 @@ js_type_check("closure_compile") { ...@@ -10,6 +10,7 @@ js_type_check("closure_compile") {
":all_sites", ":all_sites",
":category_default_setting", ":category_default_setting",
":category_setting_exceptions", ":category_setting_exceptions",
":chooser_exception_list",
":chooser_exception_list_entry", ":chooser_exception_list_entry",
":constants", ":constants",
":cookie_info", ":cookie_info",
...@@ -75,6 +76,18 @@ js_library("category_setting_exceptions") { ...@@ -75,6 +76,18 @@ js_library("category_setting_exceptions") {
externs_list = [ "$externs_path/settings_private.js" ] externs_list = [ "$externs_path/settings_private.js" ]
} }
js_library("chooser_exception_list") {
deps = [
":chooser_exception_list_entry",
":constants",
":site_settings_behavior",
"//ui/webui/resources/js:i18n_behavior",
"//ui/webui/resources/js:list_property_update_behavior",
"//ui/webui/resources/js:web_ui_listener_behavior",
]
externs_list = [ "$externs_path/settings_private.js" ]
}
js_library("chooser_exception_list_entry") { js_library("chooser_exception_list_entry") {
deps = [ deps = [
":constants", ":constants",
......
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
<link rel="import" href="chrome://resources/html/i18n_behavior.html">
<link rel="import" href="chrome://resources/html/list_property_update_behavior.html">
<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html">
<link rel="import" href="../i18n_setup.html">
<link rel="import" href="../settings_shared_css.html">
<link rel="import" href="constants.html">
<link rel="import" href="chooser_exception_list_entry.html">
<link rel="import" href="site_settings_behavior.html">
<link rel="import" href="site_settings_prefs_browser_proxy.html">
<dom-module id="chooser-exception-list">
<template>
<style include="settings-shared">
paper-tooltip {
--paper-tooltip: var(--cr-tooltip);
}
</style>
<div class="list-frame" id="empty-list-message"
hidden$="[[hasExceptions_(chooserExceptions.*)]]">
<div class="list-item secondary">[[emptyListMessage_]]</div>
</div>
<template is="dom-repeat" items="[[chooserExceptions]]">
<chooser-exception-list-entry exception="[[item]]"
tabindex$="[[tabIndex]]" first$="[[!index]]"
iron-list-tab-index="[[tabIndex]]" on-show-tooltip="onShowTooltip_">
</chooser-exception-list-entry>
</template>
<paper-tooltip id="tooltip" manual-mode position="top">
[[tooltipText_]]
</paper-tooltip>
</template>
<script src="chooser_exception_list.js"></script>
</dom-module>
// 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.
/**
* @fileoverview
* 'chooser-exception-list' shows a list of chooser exceptions for a given
* chooser type.
*/
Polymer({
is: 'chooser-exception-list',
behaviors: [
I18nBehavior,
ListPropertyUpdateBehavior,
SiteSettingsBehavior,
WebUIListenerBehavior,
],
properties: {
/**
* Array of chooser exceptions to display in the widget.
* @type {!Array<ChooserException>}
*/
chooserExceptions: {
type: Array,
value: function() {
return [];
},
},
/**
* The string ID of the chooser type that this element is displaying data
* for.
* See site_settings/constants.js for possible values.
* @type {!settings.ChooserType}
*/
chooserType: {
observer: 'chooserTypeChanged_',
type: String,
value: settings.ChooserType.NONE,
},
/** @private */
emptyListMessage_: {
type: String,
value: '',
},
/** @private */
tooltipText_: String,
},
/** @override */
created: function() {
this.browserProxy_ =
settings.SiteSettingsPrefsBrowserProxyImpl.getInstance();
},
/** @override */
attached: function() {
this.addWebUIListener(
'contentSettingSitePermissionChanged',
this.objectWithinChooserTypeChanged_.bind(this));
},
/**
* Called when a chooser exception changes permission.
* @param {string} category The category of the exception that changed.
* @param {string} site The site that changed.
* @private
*/
objectWithinChooserTypeChanged_: function(category, site) {
if (category === this.category) {
this.chooserTypeChanged_();
}
},
/**
* Configures the visibility of the widget and shows the list.
* @private
*/
chooserTypeChanged_: function() {
if (this.chooserType == settings.ChooserType.NONE) {
return;
}
// Set the message to display when the exception list is empty.
switch (this.chooserType) {
case settings.ChooserType.USB_DEVICES:
this.emptyListMessage_ = this.i18n('noUsbDevicesFound');
default:
this.emptyListMessage_ = '';
}
this.populateList_();
},
/**
* Returns true if there are any chooser exceptions for this chooser type.
* @return {boolean}
* @private
*/
hasExceptions_: function() {
return this.chooserExceptions.length > 0;
},
/**
* Need to use a common tooltip since the tooltip in the entry is cut off from
* the iron-list.
* @param{!{detail: {target: HTMLElement, text: string}}} e
* @private
*/
onShowTooltip_: function(e) {
this.tooltipText_ = e.detail.text;
const target = e.detail.target;
// paper-tooltip normally determines the target from the |for| property,
// which is a selector. Here paper-tooltip is being reused by multiple
// potential targets.
this.$.tooltip.target = target;
const hide = () => {
this.$.tooltip.hide();
target.removeEventListener('mouseleave', hide);
target.removeEventListener('blur', hide);
target.removeEventListener('tap', hide);
this.$.tooltip.removeEventListener('mouseenter', hide);
};
target.addEventListener('mouseleave', hide);
target.addEventListener('blur', hide);
target.addEventListener('tap', hide);
this.$.tooltip.addEventListener('mouseenter', hide);
this.$.tooltip.show();
},
/**
* Populate the chooser exception list for display.
* @private
*/
populateList_: function() {
this.browserProxy_.getChooserExceptionList(this.chooserType)
.then(exceptionList => this.processExceptions_(exceptionList));
},
/**
* Process the chooser exception list returned from the native layer.
* @param {!Array<RawChooserException>} exceptionList
* @private
*/
processExceptions_: function(exceptionList) {
const exceptions = exceptionList.map(exception => {
const sites = exception.sites.map(this.expandSiteException);
return Object.assign(exception, {sites});
});
this.updateList('chooserExceptions', x => x.displayName, exceptions);
},
});
...@@ -255,11 +255,9 @@ Polymer({ ...@@ -255,11 +255,9 @@ Polymer({
const target = e.detail.target; const target = e.detail.target;
// paper-tooltip normally determines the target from the |for| property, // paper-tooltip normally determines the target from the |for| property,
// which is a selector. Here paper-tooltip is being reused by multiple // which is a selector. Here paper-tooltip is being reused by multiple
// potential targets. Since paper-tooltip does not expose a public property // potential targets.
// or method to update the target, the private property |_target| is
// updated directly.
const tooltip = this.$.tooltip; const tooltip = this.$.tooltip;
tooltip._target = target; tooltip.target = target;
/** @type {{updatePosition: Function}} */ (tooltip).updatePosition(); /** @type {{updatePosition: Function}} */ (tooltip).updatePosition();
const hide = () => { const hide = () => {
this.$.tooltip.hide(); this.$.tooltip.hide();
......
...@@ -72,6 +72,16 @@ let RawSiteException; ...@@ -72,6 +72,16 @@ let RawSiteException;
*/ */
let SiteException; let SiteException;
/**
* The chooser exception information passed from the C++ handler.
* See also: ChooserException.
* @typedef {{chooserType: !settings.ChooserType,
* displayName: string,
* object: Object,
* sites: Array<!RawSiteException>}}
*/
let RawChooserException;
/** /**
* The chooser exception after it has been converted/filtered for UI use. * The chooser exception after it has been converted/filtered for UI use.
* See also: RawChooserException. * See also: RawChooserException.
...@@ -152,6 +162,14 @@ cr.define('settings', function() { ...@@ -152,6 +162,14 @@ cr.define('settings', function() {
*/ */
getAllSites(contentTypes) {} getAllSites(contentTypes) {}
/**
* Gets the chooser exceptions for a particular chooser type.
* @param {settings.ChooserType} chooserType The chooser type to grab
* exceptions from.
* @return {!Promise<!Array<!RawChooserException>>}
*/
getChooserExceptionList(chooserType) {}
/** /**
* Converts a given number of bytes into a human-readable format, with data * Converts a given number of bytes into a human-readable format, with data
* units. * units.
...@@ -377,6 +395,11 @@ cr.define('settings', function() { ...@@ -377,6 +395,11 @@ cr.define('settings', function() {
return cr.sendWithPromise('getAllSites', contentTypes); return cr.sendWithPromise('getAllSites', contentTypes);
} }
/** @override */
getChooserExceptionList(chooserType) {
return cr.sendWithPromise('getChooserExceptionList', chooserType);
}
/** @override */ /** @override */
getFormattedBytes(numBytes) { getFormattedBytes(numBytes) {
return cr.sendWithPromise('getFormattedBytes', numBytes); return cr.sendWithPromise('getFormattedBytes', numBytes);
......
...@@ -1054,6 +1054,31 @@ TEST_F('CrSettingsSiteDataDetailsTest', 'All', function() { ...@@ -1054,6 +1054,31 @@ TEST_F('CrSettingsSiteDataDetailsTest', 'All', function() {
mocha.run(); mocha.run();
}); });
/**
* @constructor
* @extends {CrSettingsBrowserTest}
*/
function CrSettingsChooserExceptionListTest() {}
CrSettingsChooserExceptionListTest.prototype = {
__proto__: CrSettingsBrowserTest.prototype,
/** @override */
browsePreload: 'chrome://settings/site_settings/chooser_exception_list.html',
/** @override */
extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
'../test_browser_proxy.js',
'test_util.js',
'test_site_settings_prefs_browser_proxy.js',
'chooser_exception_list_tests.js',
]),
};
TEST_F('CrSettingsChooserExceptionListTest', 'All', function() {
mocha.run();
});
/** /**
* @constructor * @constructor
* @extends {CrSettingsBrowserTest} * @extends {CrSettingsBrowserTest}
......
...@@ -8,7 +8,9 @@ ...@@ -8,7 +8,9 @@
* @typedef {{defaults: !Object<settings.ContentSettingsTypes, * @typedef {{defaults: !Object<settings.ContentSettingsTypes,
* !DefaultContentSetting>, * !DefaultContentSetting>,
* exceptions: !Object<settings.ContentSettingsTypes, * exceptions: !Object<settings.ContentSettingsTypes,
* !Array<!RawSiteException>>}} * !Array<!RawSiteException>>,
* chooserExceptions: !Object<settings.ContentSettingsTypes,
* !Array<!RawChooserException>>}}
*/ */
let SiteSettingsPref; let SiteSettingsPref;
...@@ -23,11 +25,13 @@ class TestSiteSettingsPrefsBrowserProxy extends TestBrowserProxy { ...@@ -23,11 +25,13 @@ class TestSiteSettingsPrefsBrowserProxy extends TestBrowserProxy {
constructor() { constructor() {
super([ super([
'clearFlashPref', 'clearFlashPref',
'fetchBlockAutoplayStatus',
'fetchUsbDevices', 'fetchUsbDevices',
'fetchZoomLevels', 'fetchZoomLevels',
'getAllSites', 'getAllSites',
'getFormattedBytes', 'getChooserExceptionList',
'getDefaultValueForContentType', 'getDefaultValueForContentType',
'getFormattedBytes',
'getExceptionList', 'getExceptionList',
'getOriginPermissions', 'getOriginPermissions',
'isOriginValid', 'isOriginValid',
...@@ -45,14 +49,13 @@ class TestSiteSettingsPrefsBrowserProxy extends TestBrowserProxy { ...@@ -45,14 +49,13 @@ class TestSiteSettingsPrefsBrowserProxy extends TestBrowserProxy {
'setOriginPermissions', 'setOriginPermissions',
'setProtocolDefault', 'setProtocolDefault',
'updateIncognitoStatus', 'updateIncognitoStatus',
'fetchBlockAutoplayStatus',
]); ]);
/** @private {boolean} */ /** @private {boolean} */
this.hasIncognito_ = false; this.hasIncognito_ = false;
/** @private {!SiteSettingsPref} */ /** @private {!SiteSettingsPref} */
this.prefs_ = test_util.createSiteSettingsPrefs([], []); this.prefs_ = test_util.createSiteSettingsPrefs([], [], []);
/** @private {!Array<ZoomLevelEntry>} */ /** @private {!Array<ZoomLevelEntry>} */
this.zoomList_ = []; this.zoomList_ = [];
...@@ -267,6 +270,24 @@ class TestSiteSettingsPrefsBrowserProxy extends TestBrowserProxy { ...@@ -267,6 +270,24 @@ class TestSiteSettingsPrefsBrowserProxy extends TestBrowserProxy {
return Promise.resolve(pref); return Promise.resolve(pref);
} }
/** @override */
getChooserExceptionList(chooserType) {
let pref = this.prefs_.chooserExceptions[chooserType];
assert(pref != undefined, 'Pref is missing for ' + chooserType);
if (this.hasIncognito_) {
const incognitoElements = [];
for (let i = 0; i < pref.length; ++i) {
// Copy |pref[i]| to avoid changing the original |pref[i]|.
incognitoElements.push(Object.assign({}, pref[i], {incognito: true}));
}
pref.push(...incognitoElements);
}
this.methodCalled('getChooserExceptionList', chooserType);
return Promise.resolve(pref);
}
/** @override */ /** @override */
isOriginValid(origin) { isOriginValid(origin) {
this.methodCalled('isOriginValid', origin); this.methodCalled('isOriginValid', origin);
......
...@@ -126,6 +126,22 @@ cr.define('test_util', function() { ...@@ -126,6 +126,22 @@ cr.define('test_util', function() {
override); override);
} }
/**
* Helper to create a mock RawChooserException.
* @param {!settings.ChooserType} chooserType The chooser exception type.
* @param {Array<!SiteException>} sites A list of SiteExceptions corresponding
* to the chooser exception.
* @param {!Object=} override An object with a subset of the properties of
* RawChooserException. Properties defined in |override| will overwrite
* the defaults in this function's return value.
* @return {RawChooserException}
*/
function createRawChooserException(chooserType, sites, override) {
return Object.assign(
{chooserType: chooserType, displayName: '', object: {}, sites: sites},
override || {});
}
/** /**
* Helper to create a mock SiteSettingsPref. * Helper to create a mock SiteSettingsPref.
* @param {!Array<{setting: settings.ContentSettingsTypes, * @param {!Array<{setting: settings.ContentSettingsTypes,
...@@ -138,9 +154,15 @@ cr.define('test_util', function() { ...@@ -138,9 +154,15 @@ cr.define('test_util', function() {
* RawSiteExceptions and the content settings they apply to, which will * RawSiteExceptions and the content settings they apply to, which will
* overwrite the exceptions in the SiteSettingsPref returned by this * overwrite the exceptions in the SiteSettingsPref returned by this
* function. * function.
* @param {!Array<{setting: settings.ContentSettingsTypes,
* value: !Array<RawChooserException>}>} chooserExceptionsList
* A list of RawChooserExceptions and the chooser type that they apply to,
* which will overwrite the exceptions in the SiteSettingsPref returned by
* this function.
* @return {SiteSettingsPref} * @return {SiteSettingsPref}
*/ */
function createSiteSettingsPrefs(defaultsList, exceptionsList) { function createSiteSettingsPrefs(
defaultsList, exceptionsList, chooserExceptionsList = []) {
// These test defaults reflect the actual defaults assigned to each // These test defaults reflect the actual defaults assigned to each
// ContentSettingType, but keeping these in sync shouldn't matter for tests. // ContentSettingType, but keeping these in sync shouldn't matter for tests.
const defaults = {}; const defaults = {};
...@@ -172,15 +194,21 @@ cr.define('test_util', function() { ...@@ -172,15 +194,21 @@ cr.define('test_util', function() {
defaults[override.setting] = override.value; defaults[override.setting] = override.value;
}); });
const chooserExceptions = {};
const exceptions = {}; const exceptions = {};
for (let type in settings.ContentSettingsTypes) { for (let type in settings.ContentSettingsTypes) {
chooserExceptions[settings.ContentSettingsTypes[type]] = [];
exceptions[settings.ContentSettingsTypes[type]] = []; exceptions[settings.ContentSettingsTypes[type]] = [];
} }
exceptionsList.forEach((override) => { exceptionsList.forEach(override => {
exceptions[override.setting] = override.value; exceptions[override.setting] = override.value;
}); });
chooserExceptionsList.forEach(override => {
chooserExceptions[override.setting] = override.value;
});
return { return {
chooserExceptions: chooserExceptions,
defaults: defaults, defaults: defaults,
exceptions: exceptions, exceptions: exceptions,
}; };
...@@ -238,6 +266,7 @@ cr.define('test_util', function() { ...@@ -238,6 +266,7 @@ cr.define('test_util', function() {
createContentSettingTypeToValuePair: createContentSettingTypeToValuePair, createContentSettingTypeToValuePair: createContentSettingTypeToValuePair,
createDefaultContentSetting: createDefaultContentSetting, createDefaultContentSetting: createDefaultContentSetting,
createOriginInfo: createOriginInfo, createOriginInfo: createOriginInfo,
createRawChooserException: createRawChooserException,
createRawSiteException: createRawSiteException, createRawSiteException: createRawSiteException,
createSiteGroup: createSiteGroup, createSiteGroup: createSiteGroup,
createSiteSettingsPrefs: createSiteSettingsPrefs, createSiteSettingsPrefs: createSiteSettingsPrefs,
......
...@@ -111,6 +111,7 @@ CrSettingsCategorySettingExceptionsTest.* ...@@ -111,6 +111,7 @@ CrSettingsCategorySettingExceptionsTest.*
CrSettingsCertificateManagerTest.* CrSettingsCertificateManagerTest.*
CrSettingsChangePasswordPageTest.* CrSettingsChangePasswordPageTest.*
CrSettingsCheckboxTest.* CrSettingsCheckboxTest.*
CrSettingsChooserExceptionListTest.*
CrSettingsChooserExceptionListEntryTest.* CrSettingsChooserExceptionListEntryTest.*
CrSettingsChromeCleanupPageTest.* CrSettingsChromeCleanupPageTest.*
CrSettingsCrostiniPageTest.* CrSettingsCrostiniPageTest.*
......
...@@ -136,6 +136,45 @@ index 55714942c93c..46fb9adf6a04 100644 ...@@ -136,6 +136,45 @@ index 55714942c93c..46fb9adf6a04 100644
right: 0; right: 0;
left: auto; left: auto;
transform-origin: right top; transform-origin: right top;
diff --git a/components-chromium/paper-tooltip/paper-tooltip-extracted.js b/components-chromium/paper-tooltip/paper-tooltip-extracted.js
index 2830d229760c..2116f616a93b 100644
--- a/components-chromium/paper-tooltip/paper-tooltip-extracted.js
+++ b/components-chromium/paper-tooltip/paper-tooltip-extracted.js
@@ -123,12 +123,16 @@ Polymer({
},
/**
* Returns the target element that this tooltip is anchored to. It is
- * either the element given by the `for` attribute, or the immediate parent
- * of the tooltip.
+ * either the element given by the `for` attribute, the element manually
+ * specified through the `target` attribute, or the immediate parent of
+ * the tooltip.
*
* @type {Node}
*/
get target() {
+ if (this._manualTarget)
+ return this._manualTarget;
+
var parentNode = Polymer.dom(this).parentNode;
// If the parentNode is a document fragment, then we need to use the host.
var ownerRoot = Polymer.dom(this).getOwnerRoot();
@@ -142,6 +146,15 @@ Polymer({
return target;
},
+ /**
+ * Sets the target element that this tooltip will be anchored to.
+ * @param {Node} target
+ */
+ set target(target) {
+ this._manualTarget = target;
+ this._findTarget();
+ },
+
/**
* @return {void}
*/
diff --git a/components-chromium/polymer2/lib/legacy/legacy-element-mixin.html b/components-chromium/polymer2/lib/legacy/legacy-element-mixin.html diff --git a/components-chromium/polymer2/lib/legacy/legacy-element-mixin.html b/components-chromium/polymer2/lib/legacy/legacy-element-mixin.html
index 7306c651407e..9c88699a3924 100644 index 7306c651407e..9c88699a3924 100644
--- a/components-chromium/polymer2/lib/legacy/legacy-element-mixin.html --- a/components-chromium/polymer2/lib/legacy/legacy-element-mixin.html
......
...@@ -123,12 +123,16 @@ Polymer({ ...@@ -123,12 +123,16 @@ Polymer({
}, },
/** /**
* Returns the target element that this tooltip is anchored to. It is * Returns the target element that this tooltip is anchored to. It is
* either the element given by the `for` attribute, or the immediate parent * either the element given by the `for` attribute, the element manually
* of the tooltip. * specified through the `target` attribute, or the immediate parent of
* the tooltip.
* *
* @type {Node} * @type {Node}
*/ */
get target() { get target() {
if (this._manualTarget)
return this._manualTarget;
var parentNode = Polymer.dom(this).parentNode; var parentNode = Polymer.dom(this).parentNode;
// If the parentNode is a document fragment, then we need to use the host. // If the parentNode is a document fragment, then we need to use the host.
var ownerRoot = Polymer.dom(this).getOwnerRoot(); var ownerRoot = Polymer.dom(this).getOwnerRoot();
...@@ -142,6 +146,15 @@ Polymer({ ...@@ -142,6 +146,15 @@ Polymer({
return target; return target;
}, },
/**
* Sets the target element that this tooltip will be anchored to.
* @param {Node} target
*/
set target(target) {
this._manualTarget = target;
this._findTarget();
},
/** /**
* @return {void} * @return {void}
*/ */
......
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