зеркало из https://github.com/mozilla/gecko-dev.git
Merge fx-team to m-c. a=merge
This commit is contained in:
Коммит
7fbee60fc9
|
@ -600,7 +600,7 @@ SocialShare = {
|
||||||
sizeSocialPanelToContent(this.panel, iframe);
|
sizeSocialPanelToContent(this.panel, iframe);
|
||||||
},
|
},
|
||||||
|
|
||||||
sharePage: function(providerOrigin, graphData) {
|
sharePage: function(providerOrigin, graphData, target) {
|
||||||
// if providerOrigin is undefined, we use the last-used provider, or the
|
// if providerOrigin is undefined, we use the last-used provider, or the
|
||||||
// current/default provider. The provider selection in the share panel
|
// current/default provider. The provider selection in the share panel
|
||||||
// will call sharePage with an origin for us to switch to.
|
// will call sharePage with an origin for us to switch to.
|
||||||
|
@ -619,7 +619,8 @@ SocialShare = {
|
||||||
// in mozSocial API, or via nsContentMenu calls. If it is present, it MUST
|
// in mozSocial API, or via nsContentMenu calls. If it is present, it MUST
|
||||||
// define at least url. If it is undefined, we're sharing the current url in
|
// define at least url. If it is undefined, we're sharing the current url in
|
||||||
// the browser tab.
|
// the browser tab.
|
||||||
let sharedURI = graphData ? Services.io.newURI(graphData.url, null, null) :
|
let pageData = graphData ? graphData : this.currentShare;
|
||||||
|
let sharedURI = pageData ? Services.io.newURI(pageData.url, null, null) :
|
||||||
gBrowser.currentURI;
|
gBrowser.currentURI;
|
||||||
if (!this.canSharePage(sharedURI))
|
if (!this.canSharePage(sharedURI))
|
||||||
return;
|
return;
|
||||||
|
@ -628,7 +629,6 @@ SocialShare = {
|
||||||
// endpoints (e.g. oexchange) that do not support additional
|
// endpoints (e.g. oexchange) that do not support additional
|
||||||
// socialapi functionality. One tweak is that we shoot an event
|
// socialapi functionality. One tweak is that we shoot an event
|
||||||
// containing the open graph data.
|
// containing the open graph data.
|
||||||
let pageData = graphData ? graphData : this.currentShare;
|
|
||||||
if (!pageData || sharedURI == gBrowser.currentURI) {
|
if (!pageData || sharedURI == gBrowser.currentURI) {
|
||||||
pageData = OpenGraphBuilder.getData(gBrowser);
|
pageData = OpenGraphBuilder.getData(gBrowser);
|
||||||
if (graphData) {
|
if (graphData) {
|
||||||
|
@ -638,6 +638,10 @@ SocialShare = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// if this is a share of a selected item, get any microdata
|
||||||
|
if (!pageData.microdata && target) {
|
||||||
|
pageData.microdata = OpenGraphBuilder.getMicrodata(gBrowser, target);
|
||||||
|
}
|
||||||
this.currentShare = pageData;
|
this.currentShare = pageData;
|
||||||
|
|
||||||
let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData);
|
let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData);
|
||||||
|
@ -1353,10 +1357,10 @@ SocialMarks = {
|
||||||
return this._toolbarHelper;
|
return this._toolbarHelper;
|
||||||
},
|
},
|
||||||
|
|
||||||
markLink: function(aOrigin, aUrl) {
|
markLink: function(aOrigin, aUrl, aTarget) {
|
||||||
// find the button for this provider, and open it
|
// find the button for this provider, and open it
|
||||||
let id = this._toolbarHelper.idFromOrigin(aOrigin);
|
let id = this._toolbarHelper.idFromOrigin(aOrigin);
|
||||||
document.getElementById(id).markLink(aUrl);
|
document.getElementById(id).markLink(aUrl, aTarget);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1913,6 +1913,7 @@ function loadURI(uri, referrer, postData, allowThirdPartyFixup) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getShortcutOrURIAndPostData(aURL, aCallback) {
|
function getShortcutOrURIAndPostData(aURL, aCallback) {
|
||||||
|
let mayInheritPrincipal = false;
|
||||||
let postData = null;
|
let postData = null;
|
||||||
let shortcutURL = null;
|
let shortcutURL = null;
|
||||||
let keyword = aURL;
|
let keyword = aURL;
|
||||||
|
@ -1928,7 +1929,8 @@ function getShortcutOrURIAndPostData(aURL, aCallback) {
|
||||||
if (engine) {
|
if (engine) {
|
||||||
let submission = engine.getSubmission(param);
|
let submission = engine.getSubmission(param);
|
||||||
postData = submission.postData;
|
postData = submission.postData;
|
||||||
aCallback({ postData: submission.postData, url: submission.uri.spec });
|
aCallback({ postData: submission.postData, url: submission.uri.spec,
|
||||||
|
mayInheritPrincipal: mayInheritPrincipal });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1936,7 +1938,8 @@ function getShortcutOrURIAndPostData(aURL, aCallback) {
|
||||||
PlacesUtils.getURLAndPostDataForKeyword(keyword);
|
PlacesUtils.getURLAndPostDataForKeyword(keyword);
|
||||||
|
|
||||||
if (!shortcutURL) {
|
if (!shortcutURL) {
|
||||||
aCallback({ postData: postData, url: aURL });
|
aCallback({ postData: postData, url: aURL,
|
||||||
|
mayInheritPrincipal: mayInheritPrincipal });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1968,7 +1971,12 @@ function getShortcutOrURIAndPostData(aURL, aCallback) {
|
||||||
postData = getPostDataStream(escapedPostData, param, encodedParam,
|
postData = getPostDataStream(escapedPostData, param, encodedParam,
|
||||||
"application/x-www-form-urlencoded");
|
"application/x-www-form-urlencoded");
|
||||||
|
|
||||||
aCallback({ postData: postData, url: shortcutURL });
|
// This URL came from a bookmark, so it's safe to let it inherit the current
|
||||||
|
// document's principal.
|
||||||
|
mayInheritPrincipal = true;
|
||||||
|
|
||||||
|
aCallback({ postData: postData, url: shortcutURL,
|
||||||
|
mayInheritPrincipal: mayInheritPrincipal });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matches) {
|
if (matches) {
|
||||||
|
@ -1991,9 +1999,15 @@ function getShortcutOrURIAndPostData(aURL, aCallback) {
|
||||||
// the original URL.
|
// the original URL.
|
||||||
postData = null;
|
postData = null;
|
||||||
|
|
||||||
aCallback({ postData: postData, url: aURL });
|
aCallback({ postData: postData, url: aURL,
|
||||||
|
mayInheritPrincipal: mayInheritPrincipal });
|
||||||
} else {
|
} else {
|
||||||
aCallback({ postData: postData, url: shortcutURL });
|
// This URL came from a bookmark, so it's safe to let it inherit the current
|
||||||
|
// document's principal.
|
||||||
|
mayInheritPrincipal = true;
|
||||||
|
|
||||||
|
aCallback({ postData: postData, url: shortcutURL,
|
||||||
|
mayInheritPrincipal: mayInheritPrincipal });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2842,7 +2856,7 @@ const DOMLinkHandler = {
|
||||||
init: function() {
|
init: function() {
|
||||||
let mm = window.messageManager;
|
let mm = window.messageManager;
|
||||||
mm.addMessageListener("Link:AddFeed", this);
|
mm.addMessageListener("Link:AddFeed", this);
|
||||||
mm.addMessageListener("Link:AddIcon", this);
|
mm.addMessageListener("Link:SetIcon", this);
|
||||||
mm.addMessageListener("Link:AddSearch", this);
|
mm.addMessageListener("Link:AddSearch", this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2853,8 +2867,8 @@ const DOMLinkHandler = {
|
||||||
FeedHandler.addFeed(link, aMsg.target);
|
FeedHandler.addFeed(link, aMsg.target);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Link:AddIcon":
|
case "Link:SetIcon":
|
||||||
return this.addIcon(aMsg.target, aMsg.data.url);
|
return this.setIcon(aMsg.target, aMsg.data.url);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Link:AddSearch":
|
case "Link:AddSearch":
|
||||||
|
@ -2863,7 +2877,7 @@ const DOMLinkHandler = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addIcon: function(aBrowser, aURL) {
|
setIcon: function(aBrowser, aURL) {
|
||||||
if (gBrowser.isFailedIcon(aURL))
|
if (gBrowser.isFailedIcon(aURL))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -5181,7 +5195,8 @@ function middleMousePaste(event) {
|
||||||
if (where != "current" ||
|
if (where != "current" ||
|
||||||
lastLocationChange == gBrowser.selectedBrowser.lastLocationChange) {
|
lastLocationChange == gBrowser.selectedBrowser.lastLocationChange) {
|
||||||
openUILink(data.url, event,
|
openUILink(data.url, event,
|
||||||
{ ignoreButton: true });
|
{ ignoreButton: true,
|
||||||
|
disallowInheritPrincipal: !data.mayInheritPrincipal });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5189,26 +5204,9 @@ function middleMousePaste(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function stripUnsafeProtocolOnPaste(pasteData) {
|
function stripUnsafeProtocolOnPaste(pasteData) {
|
||||||
// Don't allow pasting in full URIs which inherit the security context.
|
// Don't allow pasting javascript URIs since we don't support
|
||||||
const URI_INHERITS_SECURITY_CONTEXT = Ci.nsIProtocolHandler.URI_INHERITS_SECURITY_CONTEXT;
|
// LOAD_FLAGS_DISALLOW_INHERIT_OWNER for those.
|
||||||
|
return pasteData.replace(/^(?:\s*javascript:)+/i, "");
|
||||||
let pastedURI;
|
|
||||||
try {
|
|
||||||
pastedURI = makeURI(pasteData.trim());
|
|
||||||
} catch (ex) {
|
|
||||||
return pasteData;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (Services.netutil.URIChainHasFlags(pastedURI, URI_INHERITS_SECURITY_CONTEXT)) {
|
|
||||||
pasteData = pastedURI.path.trim();
|
|
||||||
try {
|
|
||||||
pastedURI = makeURI(pasteData);
|
|
||||||
} catch (ex) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pasteData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDroppedLink(event, url, name)
|
function handleDroppedLink(event, url, name)
|
||||||
|
|
|
@ -1591,22 +1591,22 @@ nsContextMenu.prototype = {
|
||||||
},
|
},
|
||||||
markLink: function CM_markLink(origin) {
|
markLink: function CM_markLink(origin) {
|
||||||
// send link to social, if it is the page url linkURI will be null
|
// send link to social, if it is the page url linkURI will be null
|
||||||
SocialMarks.markLink(origin, this.linkURI ? this.linkURI.spec : null);
|
SocialMarks.markLink(origin, this.linkURI ? this.linkURI.spec : null, this.target);
|
||||||
},
|
},
|
||||||
shareLink: function CM_shareLink() {
|
shareLink: function CM_shareLink() {
|
||||||
SocialShare.sharePage(null, { url: this.linkURI.spec });
|
SocialShare.sharePage(null, { url: this.linkURI.spec }, this.target);
|
||||||
},
|
},
|
||||||
|
|
||||||
shareImage: function CM_shareImage() {
|
shareImage: function CM_shareImage() {
|
||||||
SocialShare.sharePage(null, { url: this.imageURL, previews: [ this.mediaURL ] });
|
SocialShare.sharePage(null, { url: this.imageURL, previews: [ this.mediaURL ] }, this.target);
|
||||||
},
|
},
|
||||||
|
|
||||||
shareVideo: function CM_shareVideo() {
|
shareVideo: function CM_shareVideo() {
|
||||||
SocialShare.sharePage(null, { url: this.mediaURL, source: this.mediaURL });
|
SocialShare.sharePage(null, { url: this.mediaURL, source: this.mediaURL }, this.target);
|
||||||
},
|
},
|
||||||
|
|
||||||
shareSelect: function CM_shareSelect(selection) {
|
shareSelect: function CM_shareSelect(selection) {
|
||||||
SocialShare.sharePage(null, { url: this.browser.currentURI.spec, text: selection });
|
SocialShare.sharePage(null, { url: this.browser.currentURI.spec, text: selection }, this.target);
|
||||||
},
|
},
|
||||||
|
|
||||||
savePageAs: function CM_savePageAs() {
|
savePageAs: function CM_savePageAs() {
|
||||||
|
|
|
@ -147,6 +147,7 @@
|
||||||
|
|
||||||
<method name="loadPanel">
|
<method name="loadPanel">
|
||||||
<parameter name="pageData"/>
|
<parameter name="pageData"/>
|
||||||
|
<parameter name="target"/>
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
let provider = this.provider;
|
let provider = this.provider;
|
||||||
let panel = this.panel;
|
let panel = this.panel;
|
||||||
|
@ -157,7 +158,13 @@
|
||||||
panel.appendChild(this.content);
|
panel.appendChild(this.content);
|
||||||
|
|
||||||
let URLTemplate = provider.markURL;
|
let URLTemplate = provider.markURL;
|
||||||
this.pageData = pageData || OpenGraphBuilder.getData(gBrowser);
|
pageData = pageData || OpenGraphBuilder.getData(gBrowser);
|
||||||
|
// if this is a share of a selected item, get any microdata
|
||||||
|
if (!pageData.microdata && target) {
|
||||||
|
pageData.microdata = OpenGraphBuilder.getMicrodata(gBrowser, target);
|
||||||
|
}
|
||||||
|
this.pageData = pageData;
|
||||||
|
|
||||||
let endpoint = OpenGraphBuilder.generateEndpointURL(URLTemplate, this.pageData);
|
let endpoint = OpenGraphBuilder.generateEndpointURL(URLTemplate, this.pageData);
|
||||||
|
|
||||||
// setup listeners
|
// setup listeners
|
||||||
|
@ -248,6 +255,7 @@
|
||||||
|
|
||||||
<method name="markLink">
|
<method name="markLink">
|
||||||
<parameter name="aUrl"/>
|
<parameter name="aUrl"/>
|
||||||
|
<parameter name="aTarget"/>
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
if (!aUrl) {
|
if (!aUrl) {
|
||||||
this.markCurrentPage(true);
|
this.markCurrentPage(true);
|
||||||
|
@ -259,7 +267,7 @@
|
||||||
// link, etc. inside the page. We also "update" the iframe to the
|
// link, etc. inside the page. We also "update" the iframe to the
|
||||||
// previous url when it is closed.
|
// previous url when it is closed.
|
||||||
this.content.setAttribute("src", "about:blank");
|
this.content.setAttribute("src", "about:blank");
|
||||||
this.loadPanel({ url: aUrl });
|
this.loadPanel({ url: aUrl }, aTarget);
|
||||||
this.openPanel(true);
|
this.openPanel(true);
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
|
@ -53,6 +53,7 @@ support-files =
|
||||||
file_bug970276_favicon1.ico
|
file_bug970276_favicon1.ico
|
||||||
file_bug970276_favicon2.ico
|
file_bug970276_favicon2.ico
|
||||||
file_dom_notifications.html
|
file_dom_notifications.html
|
||||||
|
file_favicon_change.html
|
||||||
file_fullscreen-window-open.html
|
file_fullscreen-window-open.html
|
||||||
get_user_media.html
|
get_user_media.html
|
||||||
head.js
|
head.js
|
||||||
|
@ -290,6 +291,7 @@ skip-if = e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome
|
||||||
[browser_duplicateIDs.js]
|
[browser_duplicateIDs.js]
|
||||||
[browser_drag.js]
|
[browser_drag.js]
|
||||||
skip-if = true # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638.
|
skip-if = true # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638.
|
||||||
|
[browser_favicon_change.js]
|
||||||
[browser_findbarClose.js]
|
[browser_findbarClose.js]
|
||||||
skip-if = e10s # Bug ?????? - test directly manipulates content (tries to grab an iframe directly from content)
|
skip-if = e10s # Bug ?????? - test directly manipulates content (tries to grab an iframe directly from content)
|
||||||
[browser_fullscreen-window-open.js]
|
[browser_fullscreen-window-open.js]
|
||||||
|
@ -311,6 +313,7 @@ skip-if = e10s # Bug ?????? - test directly manipulates content (gBrowser.conten
|
||||||
skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (bug 969405)
|
skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (bug 969405)
|
||||||
[browser_locationBarCommand.js]
|
[browser_locationBarCommand.js]
|
||||||
skip-if = os == "linux" || e10s # Linux: Intermittent failures, bug 917535; e10s: Bug ?????? - Focus issues (There should be no focused element - Got [object XULElement], expected null)
|
skip-if = os == "linux" || e10s # Linux: Intermittent failures, bug 917535; e10s: Bug ?????? - Focus issues (There should be no focused element - Got [object XULElement], expected null)
|
||||||
|
[browser_locationBarExternalLoad.js]
|
||||||
[browser_menuButtonFitts.js]
|
[browser_menuButtonFitts.js]
|
||||||
skip-if = os != "win" || e10s # The Fitts Law menu button is only supported on Windows (bug 969376); # Bug ?????? - URL bar issues ("There should be no focused element - Got [object XULElement], expected null")
|
skip-if = os != "win" || e10s # The Fitts Law menu button is only supported on Windows (bug 969376); # Bug ?????? - URL bar issues ("There should be no focused element - Got [object XULElement], expected null")
|
||||||
[browser_middleMouse_noJSPaste.js]
|
[browser_middleMouse_noJSPaste.js]
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const TEST_URL = "http://mochi.test:8888/browser/browser/base/content/test/general/file_favicon_change.html"
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
let extraTab = gBrowser.selectedTab = gBrowser.addTab();
|
||||||
|
let tabLoaded = promiseTabLoaded(extraTab);
|
||||||
|
extraTab.linkedBrowser.loadURI(TEST_URL);
|
||||||
|
let expectedFavicon = "http://example.org/one-icon";
|
||||||
|
let haveChanged = new Promise.defer();
|
||||||
|
let observer = new MutationObserver(function(mutations) {
|
||||||
|
for (let mut of mutations) {
|
||||||
|
if (mut.attributeName != "image") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let imageVal = extraTab.getAttribute("image").replace(/#.*$/, "");
|
||||||
|
if (!imageVal) {
|
||||||
|
// The value gets removed because it doesn't load.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
is(imageVal, expectedFavicon, "Favicon image should correspond to expected image.");
|
||||||
|
haveChanged.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(extraTab, {attributes: true});
|
||||||
|
yield tabLoaded;
|
||||||
|
yield haveChanged.promise;
|
||||||
|
haveChanged = new Promise.defer();
|
||||||
|
expectedFavicon = "http://example.org/other-icon";
|
||||||
|
let contentWin = extraTab.linkedBrowser.contentWindow;
|
||||||
|
let ev = new contentWin.CustomEvent("PleaseChangeFavicon", {});
|
||||||
|
contentWin.dispatchEvent(ev);
|
||||||
|
yield haveChanged.promise;
|
||||||
|
observer.disconnect();
|
||||||
|
gBrowser.removeTab(extraTab);
|
||||||
|
});
|
||||||
|
|
|
@ -11,9 +11,10 @@ function getPostDataString(aIS) {
|
||||||
return dataLines[dataLines.length-1];
|
return dataLines[dataLines.length-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
function keywordResult(aURL, aPostData) {
|
function keywordResult(aURL, aPostData, aIsUnsafe) {
|
||||||
this.url = aURL;
|
this.url = aURL;
|
||||||
this.postData = aPostData;
|
this.postData = aPostData;
|
||||||
|
this.isUnsafe = aIsUnsafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyWordData() {}
|
function keyWordData() {}
|
||||||
|
@ -52,20 +53,20 @@ var testData = [
|
||||||
new keywordResult("http://bmget-nosearch/", null)],
|
new keywordResult("http://bmget-nosearch/", null)],
|
||||||
|
|
||||||
[new searchKeywordData("searchget", "http://searchget/?search={searchTerms}", null, "foo4"),
|
[new searchKeywordData("searchget", "http://searchget/?search={searchTerms}", null, "foo4"),
|
||||||
new keywordResult("http://searchget/?search=foo4", null)],
|
new keywordResult("http://searchget/?search=foo4", null, true)],
|
||||||
|
|
||||||
[new searchKeywordData("searchpost", "http://searchpost/", "search={searchTerms}", "foo5"),
|
[new searchKeywordData("searchpost", "http://searchpost/", "search={searchTerms}", "foo5"),
|
||||||
new keywordResult("http://searchpost/", "search=foo5")],
|
new keywordResult("http://searchpost/", "search=foo5", true)],
|
||||||
|
|
||||||
[new searchKeywordData("searchpostget", "http://searchpostget/?search1={searchTerms}", "search2={searchTerms}", "foo6"),
|
[new searchKeywordData("searchpostget", "http://searchpostget/?search1={searchTerms}", "search2={searchTerms}", "foo6"),
|
||||||
new keywordResult("http://searchpostget/?search1=foo6", "search2=foo6")],
|
new keywordResult("http://searchpostget/?search1=foo6", "search2=foo6", true)],
|
||||||
|
|
||||||
// Bookmark keywords that don't take parameters should not be activated if a
|
// Bookmark keywords that don't take parameters should not be activated if a
|
||||||
// parameter is passed (bug 420328).
|
// parameter is passed (bug 420328).
|
||||||
[new bmKeywordData("bmget-noparam", "http://bmget-noparam/", null, "foo7"),
|
[new bmKeywordData("bmget-noparam", "http://bmget-noparam/", null, "foo7"),
|
||||||
new keywordResult(null, null)],
|
new keywordResult(null, null, true)],
|
||||||
[new bmKeywordData("bmpost-noparam", "http://bmpost-noparam/", "not_a=param", "foo8"),
|
[new bmKeywordData("bmpost-noparam", "http://bmpost-noparam/", "not_a=param", "foo8"),
|
||||||
new keywordResult(null, null)],
|
new keywordResult(null, null, true)],
|
||||||
|
|
||||||
// Test escaping (%s = escaped, %S = raw)
|
// Test escaping (%s = escaped, %S = raw)
|
||||||
// UTF-8 default
|
// UTF-8 default
|
||||||
|
@ -87,7 +88,7 @@ var testData = [
|
||||||
// getShortcutOrURIAndPostData for non-keywords (setupKeywords only adds keywords for
|
// getShortcutOrURIAndPostData for non-keywords (setupKeywords only adds keywords for
|
||||||
// bmKeywordData objects)
|
// bmKeywordData objects)
|
||||||
[{keyword: "http://gavinsharp.com"},
|
[{keyword: "http://gavinsharp.com"},
|
||||||
new keywordResult(null, null)]
|
new keywordResult(null, null, true)]
|
||||||
];
|
];
|
||||||
|
|
||||||
function test() {
|
function test() {
|
||||||
|
@ -108,6 +109,7 @@ function test() {
|
||||||
let expected = result.url || query;
|
let expected = result.url || query;
|
||||||
is(returnedData.url, expected, "got correct URL for " + data.keyword);
|
is(returnedData.url, expected, "got correct URL for " + data.keyword);
|
||||||
is(getPostDataString(returnedData.postData), result.postData, "got correct postData for " + data.keyword);
|
is(getPostDataString(returnedData.postData), result.postData, "got correct postData for " + data.keyword);
|
||||||
|
is(returnedData.mayInheritPrincipal, !result.isUnsafe, "got correct mayInheritPrincipal for " + data.keyword);
|
||||||
}
|
}
|
||||||
cleanupKeywords();
|
cleanupKeywords();
|
||||||
}).then(finish);
|
}).then(finish);
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
nextTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
let urls = [
|
||||||
|
"data:text/html,<body>hi"
|
||||||
|
];
|
||||||
|
|
||||||
|
function urlEnter(url) {
|
||||||
|
gURLBar.value = url;
|
||||||
|
gURLBar.focus();
|
||||||
|
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function urlClick(url) {
|
||||||
|
gURLBar.value = url;
|
||||||
|
gURLBar.focus();
|
||||||
|
let goButton = document.getElementById("urlbar-go-button");
|
||||||
|
EventUtils.synthesizeMouseAtCenter(goButton, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextTest() {
|
||||||
|
let url = urls.shift();
|
||||||
|
if (url) {
|
||||||
|
testURL(url, urlEnter, function () {
|
||||||
|
testURL(url, urlClick, nextTest);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testURL(url, loadFunc, endFunc) {
|
||||||
|
let tab = gBrowser.selectedTab = gBrowser.addTab();
|
||||||
|
registerCleanupFunction(function () {
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
});
|
||||||
|
addPageShowListener(function () {
|
||||||
|
let pagePrincipal = gBrowser.contentPrincipal;
|
||||||
|
loadFunc(url);
|
||||||
|
|
||||||
|
addPageShowListener(function () {
|
||||||
|
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
|
||||||
|
is(fm.focusedElement, null, "should be no focused element");
|
||||||
|
is(fm.focusedWindow, gBrowser.contentWindow, "content window should be focused");
|
||||||
|
|
||||||
|
ok(!gBrowser.contentPrincipal.equals(pagePrincipal),
|
||||||
|
"load of " + url + " by " + loadFunc.name + " should produce a page with a different principal");
|
||||||
|
endFunc();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPageShowListener(func) {
|
||||||
|
gBrowser.selectedBrowser.addEventListener("pageshow", function loadListener() {
|
||||||
|
gBrowser.selectedBrowser.removeEventListener("pageshow", loadListener, false);
|
||||||
|
func();
|
||||||
|
});
|
||||||
|
}
|
|
@ -7,13 +7,13 @@ let pairs = [
|
||||||
["javascript:", ""],
|
["javascript:", ""],
|
||||||
["javascript:1+1", "1+1"],
|
["javascript:1+1", "1+1"],
|
||||||
["javascript:document.domain", "document.domain"],
|
["javascript:document.domain", "document.domain"],
|
||||||
["data:text/html,<body>hi</body>", "text/html,<body>hi</body>"],
|
["data:text/html,<body>hi</body>", "data:text/html,<body>hi</body>"],
|
||||||
// Nested things get confusing because some things don't parse as URIs:
|
// Nested things get confusing because some things don't parse as URIs:
|
||||||
["javascript:javascript:alert('hi!')", "alert('hi!')"],
|
["javascript:javascript:alert('hi!')", "alert('hi!')"],
|
||||||
["data:data:text/html,<body>hi</body>", "text/html,<body>hi</body>"],
|
["data:data:text/html,<body>hi</body>", "data:data:text/html,<body>hi</body>"],
|
||||||
["javascript:data:javascript:alert('hi!')", "data:javascript:alert('hi!')"],
|
["javascript:data:javascript:alert('hi!')", "data:javascript:alert('hi!')"],
|
||||||
["javascript:data:text/html,javascript:alert('hi!')", "text/html,javascript:alert('hi!')"],
|
["javascript:data:text/html,javascript:alert('hi!')", "data:text/html,javascript:alert('hi!')"],
|
||||||
["data:data:text/html,javascript:alert('hi!')", "text/html,javascript:alert('hi!')"],
|
["data:data:text/html,javascript:alert('hi!')", "data:data:text/html,javascript:alert('hi!')"],
|
||||||
];
|
];
|
||||||
|
|
||||||
let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html><head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
|
<link rel="icon" href="http://example.org/one-icon" type="image/ico" id="i">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
window.addEventListener("PleaseChangeFavicon", function() {
|
||||||
|
var ico = document.getElementById("i");
|
||||||
|
ico.setAttribute("href", "http://example.org/other-icon");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body></html>
|
|
@ -9,6 +9,7 @@ support-files =
|
||||||
opengraph/shortlink_linkrel.html
|
opengraph/shortlink_linkrel.html
|
||||||
opengraph/shorturl_link.html
|
opengraph/shorturl_link.html
|
||||||
opengraph/shorturl_linkrel.html
|
opengraph/shorturl_linkrel.html
|
||||||
|
microdata.html
|
||||||
share.html
|
share.html
|
||||||
social_activate.html
|
social_activate.html
|
||||||
social_activate_iframe.html
|
social_activate_iframe.html
|
||||||
|
|
|
@ -170,5 +170,76 @@ var tests = {
|
||||||
}
|
}
|
||||||
port.postMessage({topic: "test-init"});
|
port.postMessage({topic: "test-init"});
|
||||||
executeSoon(runOneTest);
|
executeSoon(runOneTest);
|
||||||
|
},
|
||||||
|
testShareMicrodata: function(next) {
|
||||||
|
SocialService.addProvider(manifest, function(provider) {
|
||||||
|
let port = provider.getWorkerPort();
|
||||||
|
let target, testTab;
|
||||||
|
|
||||||
|
let expecting = JSON.stringify({
|
||||||
|
"url": "https://example.com/browser/browser/base/content/test/social/microdata.html",
|
||||||
|
"title": "My Blog",
|
||||||
|
"previews": [],
|
||||||
|
"microdata": {
|
||||||
|
"items": [{
|
||||||
|
"types": ["http://schema.org/BlogPosting"],
|
||||||
|
"properties": {
|
||||||
|
"headline": ["Progress report"],
|
||||||
|
"datePublished": ["2013-08-29"],
|
||||||
|
"url": ["https://example.com/browser/browser/base/content/test/social/microdata.html?comments=0"],
|
||||||
|
"comment": [{
|
||||||
|
"types": ["http://schema.org/UserComments"],
|
||||||
|
"properties": {
|
||||||
|
"url": ["https://example.com/browser/browser/base/content/test/social/microdata.html#c1"],
|
||||||
|
"creator": [{
|
||||||
|
"types": ["http://schema.org/Person"],
|
||||||
|
"properties": {
|
||||||
|
"name": ["Greg"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"commentTime": ["2013-08-29"]
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"types": ["http://schema.org/UserComments"],
|
||||||
|
"properties": {
|
||||||
|
"url": ["https://example.com/browser/browser/base/content/test/social/microdata.html#c2"],
|
||||||
|
"creator": [{
|
||||||
|
"types": ["http://schema.org/Person"],
|
||||||
|
"properties": {
|
||||||
|
"name": ["Charlotte"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"commentTime": ["2013-08-29"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
port.onmessage = function (e) {
|
||||||
|
let topic = e.data.topic;
|
||||||
|
switch (topic) {
|
||||||
|
case "got-share-data-message":
|
||||||
|
is(JSON.stringify(e.data.result), expecting, "microdata data ok");
|
||||||
|
gBrowser.removeTab(testTab);
|
||||||
|
SocialService.removeProvider(manifest.origin, next);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
port.postMessage({topic: "test-init"});
|
||||||
|
|
||||||
|
let url = "https://example.com/browser/browser/base/content/test/social/microdata.html"
|
||||||
|
addTab(url, function(tab) {
|
||||||
|
testTab = tab;
|
||||||
|
let doc = tab.linkedBrowser.contentDocument;
|
||||||
|
target = doc.getElementById("simple-hcard");
|
||||||
|
SocialShare.sharePage(manifest.origin, null, target);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,6 +280,56 @@ var tests = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
testMarkMicrodata: function(next) {
|
||||||
|
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
||||||
|
let port = provider.getWorkerPort();
|
||||||
|
let target, testTab;
|
||||||
|
|
||||||
|
// browser_share tests microdata on the full page, this is testing a
|
||||||
|
// specific target element.
|
||||||
|
let expecting = JSON.stringify({
|
||||||
|
"url": "https://example.com/browser/browser/base/content/test/social/microdata.html",
|
||||||
|
"microdata": {
|
||||||
|
"items": [{
|
||||||
|
"types": ["http://schema.org/UserComments"],
|
||||||
|
"properties": {
|
||||||
|
"url": ["https://example.com/browser/browser/base/content/test/social/microdata.html#c2"],
|
||||||
|
"creator": [{
|
||||||
|
"types": ["http://schema.org/Person"],
|
||||||
|
"properties": {
|
||||||
|
"name": ["Charlotte"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"commentTime": ["2013-08-29"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
port.onmessage = function (e) {
|
||||||
|
let topic = e.data.topic;
|
||||||
|
switch (topic) {
|
||||||
|
case "got-share-data-message":
|
||||||
|
is(JSON.stringify(e.data.result), expecting, "microdata data ok");
|
||||||
|
gBrowser.removeTab(testTab);
|
||||||
|
port.close();
|
||||||
|
next();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
port.postMessage({topic: "test-init"});
|
||||||
|
|
||||||
|
let url = "https://example.com/browser/browser/base/content/test/social/microdata.html"
|
||||||
|
addTab(url, function(tab) {
|
||||||
|
testTab = tab;
|
||||||
|
let doc = tab.linkedBrowser.contentDocument;
|
||||||
|
target = doc.getElementById("test-comment");
|
||||||
|
SocialMarks.markLink(manifest2.origin, url, target);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
testButtonOnDisable: function(next) {
|
testButtonOnDisable: function(next) {
|
||||||
// enable the provider now
|
// enable the provider now
|
||||||
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<head>
|
||||||
|
<title>My Blog</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<article itemscope itemtype="http://schema.org/BlogPosting">
|
||||||
|
<header>
|
||||||
|
<h1 itemprop="headline">Progress report</h1>
|
||||||
|
<p><time itemprop="datePublished" datetime="2013-08-29">today</time></p>
|
||||||
|
<link itemprop="url" href="?comments=0">
|
||||||
|
</header>
|
||||||
|
<p>All in all, he's doing well with his swim lessons. The biggest thing was he had trouble
|
||||||
|
putting his head in, but we got it down.</p>
|
||||||
|
<section>
|
||||||
|
<h1>Comments</h1>
|
||||||
|
<article itemprop="comment" itemscope itemtype="http://schema.org/UserComments" id="c1">
|
||||||
|
<link itemprop="url" href="#c1">
|
||||||
|
<footer>
|
||||||
|
<p>Posted by: <span itemprop="creator" itemscope itemtype="http://schema.org/Person">
|
||||||
|
<span itemprop="name">Greg</span>
|
||||||
|
</span></p>
|
||||||
|
<p><time itemprop="commentTime" datetime="2013-08-29">15 minutes ago</time></p>
|
||||||
|
</footer>
|
||||||
|
<p>Ha!</p>
|
||||||
|
</article>
|
||||||
|
<article id="test-comment" itemprop="comment" itemscope itemtype="http://schema.org/UserComments" id="c2">
|
||||||
|
<link itemprop="url" href="#c2">
|
||||||
|
<footer>
|
||||||
|
<p>Posted by: <span itemprop="creator" itemscope itemtype="http://schema.org/Person">
|
||||||
|
<span itemprop="name">Charlotte</span>
|
||||||
|
</span></p>
|
||||||
|
<p><time itemprop="commentTime" datetime="2013-08-29">5 minutes ago</time></p>
|
||||||
|
</footer>
|
||||||
|
<p>When you say "we got it down"...</p>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</body>
|
|
@ -34,6 +34,8 @@
|
||||||
shareData = JSON.parse(e.detail);
|
shareData = JSON.parse(e.detail);
|
||||||
updateTextNode(document.getElementById("shared"), shareData.url);
|
updateTextNode(document.getElementById("shared"), shareData.url);
|
||||||
socialMarkUpdate(true);
|
socialMarkUpdate(true);
|
||||||
|
var port = navigator.mozSocial.getWorker().port;
|
||||||
|
port.postMessage({topic: "share-data-message", result: shareData});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -265,6 +265,7 @@
|
||||||
return; // Do nothing for right clicks
|
return; // Do nothing for right clicks
|
||||||
|
|
||||||
var url = this.value;
|
var url = this.value;
|
||||||
|
var mayInheritPrincipal = false;
|
||||||
var postData = null;
|
var postData = null;
|
||||||
|
|
||||||
var action = this._parseActionUrl(url);
|
var action = this._parseActionUrl(url);
|
||||||
|
@ -287,7 +288,7 @@
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this._canonizeURL(aTriggeringEvent, response => {
|
this._canonizeURL(aTriggeringEvent, response => {
|
||||||
[url, postData] = response;
|
[url, postData, mayInheritPrincipal] = response;
|
||||||
if (url) {
|
if (url) {
|
||||||
matchLastLocationChange = (lastLocationChange ==
|
matchLastLocationChange = (lastLocationChange ==
|
||||||
gBrowser.selectedBrowser.lastLocationChange);
|
gBrowser.selectedBrowser.lastLocationChange);
|
||||||
|
@ -309,10 +310,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadCurrent() {
|
function loadCurrent() {
|
||||||
let webnav = Ci.nsIWebNavigation;
|
openUILinkIn(url, "current", {
|
||||||
let flags = webnav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
|
allowThirdPartyFixup: true,
|
||||||
webnav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
|
disallowInheritPrincipal: !mayInheritPrincipal,
|
||||||
gBrowser.loadURIWithFlags(url, flags, null, null, postData);
|
postData: postData
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focus the content area before triggering loads, since if the load
|
// Focus the content area before triggering loads, since if the load
|
||||||
|
@ -422,7 +424,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
getShortcutOrURIAndPostData(url, data => {
|
getShortcutOrURIAndPostData(url, data => {
|
||||||
aCallback([data.url, data.postData]);
|
aCallback([data.url, data.postData, data.mayInheritPrincipal]);
|
||||||
});
|
});
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
|
@ -281,11 +281,18 @@ function openLinkIn(url, where, params) {
|
||||||
getBoolPref("browser.tabs.loadInBackground");
|
getBoolPref("browser.tabs.loadInBackground");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let uriObj;
|
||||||
|
if (where == "current") {
|
||||||
|
try {
|
||||||
|
uriObj = Services.io.newURI(url, null, null);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
if (where == "current" && w.gBrowser.selectedTab.pinned) {
|
if (where == "current" && w.gBrowser.selectedTab.pinned) {
|
||||||
try {
|
try {
|
||||||
let uriObj = Services.io.newURI(url, null, null);
|
// nsIURI.host can throw for non-nsStandardURL nsIURIs.
|
||||||
if (!uriObj.schemeIs("javascript") &&
|
if (!uriObj || (!uriObj.schemeIs("javascript") &&
|
||||||
w.gBrowser.currentURI.host != uriObj.host) {
|
w.gBrowser.currentURI.host != uriObj.host)) {
|
||||||
where = "tab";
|
where = "tab";
|
||||||
loadInBackground = false;
|
loadInBackground = false;
|
||||||
}
|
}
|
||||||
|
@ -302,12 +309,19 @@ function openLinkIn(url, where, params) {
|
||||||
switch (where) {
|
switch (where) {
|
||||||
case "current":
|
case "current":
|
||||||
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||||
|
|
||||||
if (aAllowThirdPartyFixup) {
|
if (aAllowThirdPartyFixup) {
|
||||||
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
|
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
|
||||||
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
|
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
|
||||||
}
|
}
|
||||||
if (aDisallowInheritPrincipal)
|
|
||||||
|
// LOAD_FLAGS_DISALLOW_INHERIT_OWNER isn't supported for javascript URIs,
|
||||||
|
// i.e. it causes them not to load at all. Callers should strip
|
||||||
|
// "javascript:" from pasted strings to protect users from malicious URIs
|
||||||
|
// (see stripUnsafeProtocolOnPaste).
|
||||||
|
if (aDisallowInheritPrincipal && !(uriObj && uriObj.schemeIs("javascript")))
|
||||||
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
|
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
|
||||||
|
|
||||||
w.gBrowser.loadURIWithFlags(url, flags, aReferrerURI, null, aPostData);
|
w.gBrowser.loadURIWithFlags(url, flags, aReferrerURI, null, aPostData);
|
||||||
break;
|
break;
|
||||||
case "tabshifted":
|
case "tabshifted":
|
||||||
|
|
|
@ -467,7 +467,6 @@ var BookmarkPropertiesPanel = {
|
||||||
// The order here is important! We have to uninit the panel first, otherwise
|
// The order here is important! We have to uninit the panel first, otherwise
|
||||||
// late changes could force it to commit more transactions.
|
// late changes could force it to commit more transactions.
|
||||||
gEditItemOverlay.uninitPanel(true);
|
gEditItemOverlay.uninitPanel(true);
|
||||||
gEditItemOverlay = null;
|
|
||||||
this._endBatch();
|
this._endBatch();
|
||||||
window.arguments[0].performed = true;
|
window.arguments[0].performed = true;
|
||||||
},
|
},
|
||||||
|
@ -477,7 +476,6 @@ var BookmarkPropertiesPanel = {
|
||||||
// changes done as part of Undo may change the panel contents and by
|
// changes done as part of Undo may change the panel contents and by
|
||||||
// that force it to commit more transactions.
|
// that force it to commit more transactions.
|
||||||
gEditItemOverlay.uninitPanel(true);
|
gEditItemOverlay.uninitPanel(true);
|
||||||
gEditItemOverlay = null;
|
|
||||||
this._endBatch();
|
this._endBatch();
|
||||||
PlacesUtils.transactionManager.undoTransaction();
|
PlacesUtils.transactionManager.undoTransaction();
|
||||||
window.arguments[0].performed = false;
|
window.arguments[0].performed = false;
|
||||||
|
|
|
@ -209,6 +209,12 @@ var gEditItemOverlay = {
|
||||||
// observe only tags changes, through bookmarks.
|
// observe only tags changes, through bookmarks.
|
||||||
if (this._itemId != -1 || this._uri || this._multiEdit)
|
if (this._itemId != -1 || this._uri || this._multiEdit)
|
||||||
PlacesUtils.bookmarks.addObserver(this, false);
|
PlacesUtils.bookmarks.addObserver(this, false);
|
||||||
|
|
||||||
|
this._element("namePicker").addEventListener("blur", this);
|
||||||
|
this._element("locationField").addEventListener("blur", this);
|
||||||
|
this._element("tagsField").addEventListener("blur", this);
|
||||||
|
this._element("keywordField").addEventListener("blur", this);
|
||||||
|
this._element("descriptionField").addEventListener("blur", this);
|
||||||
window.addEventListener("unload", this, false);
|
window.addEventListener("unload", this, false);
|
||||||
this._observersAdded = true;
|
this._observersAdded = true;
|
||||||
}
|
}
|
||||||
|
@ -388,6 +394,12 @@ var gEditItemOverlay = {
|
||||||
if (this._itemId != -1 || this._uri || this._multiEdit)
|
if (this._itemId != -1 || this._uri || this._multiEdit)
|
||||||
PlacesUtils.bookmarks.removeObserver(this);
|
PlacesUtils.bookmarks.removeObserver(this);
|
||||||
|
|
||||||
|
this._element("namePicker").removeEventListener("blur", this);
|
||||||
|
this._element("locationField").removeEventListener("blur", this);
|
||||||
|
this._element("tagsField").removeEventListener("blur", this);
|
||||||
|
this._element("keywordField").removeEventListener("blur", this);
|
||||||
|
this._element("descriptionField").removeEventListener("blur", this);
|
||||||
|
|
||||||
this._observersAdded = false;
|
this._observersAdded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,7 +540,7 @@ var gEditItemOverlay = {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
onNamePickerChange: function EIO_onNamePickerChange() {
|
onNamePickerBlur: function EIO_onNamePickerBlur() {
|
||||||
if (this._itemId == -1)
|
if (this._itemId == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -879,6 +891,11 @@ var gEditItemOverlay = {
|
||||||
this._element("tagsField").value = tags.join(", ");
|
this._element("tagsField").value = tags.join(", ");
|
||||||
this._updateTags();
|
this._updateTags();
|
||||||
break;
|
break;
|
||||||
|
case "blur":
|
||||||
|
let replaceFn = (str, firstLetter) => firstLetter.toUpperCase();
|
||||||
|
let nodeName = aEvent.target.id.replace(/editBMPanel_(\w)/, replaceFn);
|
||||||
|
this["on" + nodeName + "Blur"]();
|
||||||
|
break;
|
||||||
case "unload":
|
case "unload":
|
||||||
this.uninitPanel(false);
|
this.uninitPanel(false);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
control="editBMPanel_namePicker"
|
control="editBMPanel_namePicker"
|
||||||
observes="paneElementsBroadcaster"/>
|
observes="paneElementsBroadcaster"/>
|
||||||
<textbox id="editBMPanel_namePicker"
|
<textbox id="editBMPanel_namePicker"
|
||||||
onblur="gEditItemOverlay.onNamePickerChange();"
|
|
||||||
observes="paneElementsBroadcaster"/>
|
observes="paneElementsBroadcaster"/>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
@ -45,7 +44,6 @@
|
||||||
observes="paneElementsBroadcaster"/>
|
observes="paneElementsBroadcaster"/>
|
||||||
<textbox id="editBMPanel_locationField"
|
<textbox id="editBMPanel_locationField"
|
||||||
class="uri-element"
|
class="uri-element"
|
||||||
onblur="gEditItemOverlay.onLocationFieldBlur();"
|
|
||||||
observes="paneElementsBroadcaster"/>
|
observes="paneElementsBroadcaster"/>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
@ -150,7 +148,6 @@
|
||||||
completedefaultindex="true"
|
completedefaultindex="true"
|
||||||
tabscrolling="true"
|
tabscrolling="true"
|
||||||
showcommentcolumn="true"
|
showcommentcolumn="true"
|
||||||
onblur="gEditItemOverlay.onTagsFieldBlur();"
|
|
||||||
observes="paneElementsBroadcaster"
|
observes="paneElementsBroadcaster"
|
||||||
placeholder="&editBookmarkOverlay.tagsEmptyDesc.label;"/>
|
placeholder="&editBookmarkOverlay.tagsEmptyDesc.label;"/>
|
||||||
<button id="editBMPanel_tagsSelectorExpander"
|
<button id="editBMPanel_tagsSelectorExpander"
|
||||||
|
@ -180,7 +177,6 @@
|
||||||
control="editBMPanel_keywordField"
|
control="editBMPanel_keywordField"
|
||||||
observes="paneElementsBroadcaster"/>
|
observes="paneElementsBroadcaster"/>
|
||||||
<textbox id="editBMPanel_keywordField"
|
<textbox id="editBMPanel_keywordField"
|
||||||
onblur="gEditItemOverlay.onKeywordFieldBlur();"
|
|
||||||
observes="paneElementsBroadcaster"/>
|
observes="paneElementsBroadcaster"/>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
@ -193,7 +189,6 @@
|
||||||
observes="paneElementsBroadcaster"/>
|
observes="paneElementsBroadcaster"/>
|
||||||
<textbox id="editBMPanel_descriptionField"
|
<textbox id="editBMPanel_descriptionField"
|
||||||
multiline="true"
|
multiline="true"
|
||||||
onblur="gEditItemOverlay.onDescriptionFieldBlur();"
|
|
||||||
observes="paneElementsBroadcaster"/>
|
observes="paneElementsBroadcaster"/>
|
||||||
</row>
|
</row>
|
||||||
</rows>
|
</rows>
|
||||||
|
|
|
@ -127,7 +127,7 @@ gTests.push({
|
||||||
PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
|
PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
|
||||||
"Node title is correct");
|
"Node title is correct");
|
||||||
// Blur the field and ensure root's name has not been changed.
|
// Blur the field and ensure root's name has not been changed.
|
||||||
this.window.gEditItemOverlay.onNamePickerChange();
|
this.window.gEditItemOverlay.onNamePickerBlur();
|
||||||
is(namepicker.value,
|
is(namepicker.value,
|
||||||
PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
|
PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
|
||||||
"Root title is correct");
|
"Root title is correct");
|
||||||
|
|
|
@ -1693,10 +1693,8 @@ var gApplicationsPane = {
|
||||||
var typeItem = this._list.selectedItem;
|
var typeItem = this._list.selectedItem;
|
||||||
var handlerInfo = this._handledTypes[typeItem.type];
|
var handlerInfo = this._handledTypes[typeItem.type];
|
||||||
|
|
||||||
openDialog("chrome://browser/content/preferences/applicationManager.xul",
|
gSubDialog.open("chrome://browser/content/preferences/applicationManager.xul",
|
||||||
"",
|
"resizable=no", handlerInfo);
|
||||||
"modal,centerscreen,resizable=no",
|
|
||||||
handlerInfo);
|
|
||||||
|
|
||||||
// Rebuild the actions menu so that we revert to the previous selection,
|
// Rebuild the actions menu so that we revert to the previous selection,
|
||||||
// or "Always ask" if the previous default application has been removed
|
// or "Always ask" if the previous default application has been removed
|
||||||
|
@ -1757,9 +1755,8 @@ var gApplicationsPane = {
|
||||||
params.filename = null;
|
params.filename = null;
|
||||||
params.handlerApp = null;
|
params.handlerApp = null;
|
||||||
|
|
||||||
window.openDialog("chrome://global/content/appPicker.xul", null,
|
gSubDialog.open("chrome://global/content/appPicker.xul",
|
||||||
"chrome,modal,centerscreen,titlebar,dialog=yes",
|
null, params);
|
||||||
params);
|
|
||||||
|
|
||||||
if (this.isValidHandlerApp(params.handlerApp)) {
|
if (this.isValidHandlerApp(params.handlerApp)) {
|
||||||
handlerApp = params.handlerApp;
|
handlerApp = params.handlerApp;
|
||||||
|
|
|
@ -222,12 +222,19 @@ TranslationUI.prototype = {
|
||||||
// Check if we should never show the infobar for this language.
|
// Check if we should never show the infobar for this language.
|
||||||
let neverForLangs =
|
let neverForLangs =
|
||||||
Services.prefs.getCharPref("browser.translation.neverForLanguages");
|
Services.prefs.getCharPref("browser.translation.neverForLanguages");
|
||||||
if (neverForLangs.split(",").indexOf(this.detectedLanguage) != -1)
|
if (neverForLangs.split(",").indexOf(this.detectedLanguage) != -1) {
|
||||||
|
TranslationHealthReport.recordAutoRejectedTranslationOffer();
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// or if we should never show the infobar for this domain.
|
// or if we should never show the infobar for this domain.
|
||||||
let perms = Services.perms;
|
let perms = Services.perms;
|
||||||
return perms.testExactPermission(aURI, "translate") != perms.DENY_ACTION;
|
if (perms.testExactPermission(aURI, "translate") == perms.DENY_ACTION) {
|
||||||
|
TranslationHealthReport.recordAutoRejectedTranslationOffer();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
showTranslationUI: function(aDetectedLanguage) {
|
showTranslationUI: function(aDetectedLanguage) {
|
||||||
|
@ -298,6 +305,20 @@ let TranslationHealthReport = {
|
||||||
this._withProvider(provider => provider.recordMissedTranslationOpportunity(language));
|
this._withProvider(provider => provider.recordMissedTranslationOpportunity(language));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record an automatically rejected translation offer in the health
|
||||||
|
* report. A translation offer is automatically rejected when a user
|
||||||
|
* has previously clicked "Never translate this language" or "Never
|
||||||
|
* translate this site", which results in the infobar not being shown for
|
||||||
|
* the translation opportunity.
|
||||||
|
*
|
||||||
|
* These translation opportunities should still be recorded in addition to
|
||||||
|
* recording the automatic rejection of the offer.
|
||||||
|
*/
|
||||||
|
recordAutoRejectedTranslationOffer: function () {
|
||||||
|
this._withProvider(provider => provider.recordAutoRejectedTranslationOffer());
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record a translation in the health report.
|
* Record a translation in the health report.
|
||||||
* @param langFrom
|
* @param langFrom
|
||||||
|
@ -413,6 +434,7 @@ TranslationMeasurement1.prototype = Object.freeze({
|
||||||
showOriginalContent: DAILY_COUNTER_FIELD,
|
showOriginalContent: DAILY_COUNTER_FIELD,
|
||||||
detectLanguageEnabled: DAILY_LAST_NUMERIC_FIELD,
|
detectLanguageEnabled: DAILY_LAST_NUMERIC_FIELD,
|
||||||
showTranslationUI: DAILY_LAST_NUMERIC_FIELD,
|
showTranslationUI: DAILY_LAST_NUMERIC_FIELD,
|
||||||
|
autoRejectedTranslationOffer: DAILY_COUNTER_FIELD,
|
||||||
},
|
},
|
||||||
|
|
||||||
shouldIncludeField: function (field) {
|
shouldIncludeField: function (field) {
|
||||||
|
@ -511,6 +533,15 @@ TranslationProvider.prototype = Object.freeze({
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
recordAutoRejectedTranslationOffer: function (date=new Date()) {
|
||||||
|
let m = this.getMeasurement(TranslationMeasurement1.prototype.name,
|
||||||
|
TranslationMeasurement1.prototype.version);
|
||||||
|
|
||||||
|
return this._enqueueTelemetryStorageTask(function* recordTask() {
|
||||||
|
yield m.incrementDailyCounter("autoRejectedTranslationOffer", date);
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
recordTranslation: function (langFrom, langTo, numCharacters, date=new Date()) {
|
recordTranslation: function (langFrom, langTo, numCharacters, date=new Date()) {
|
||||||
let m = this.getMeasurement(TranslationMeasurement1.prototype.name,
|
let m = this.getMeasurement(TranslationMeasurement1.prototype.name,
|
||||||
TranslationMeasurement1.prototype.version);
|
TranslationMeasurement1.prototype.version);
|
||||||
|
|
|
@ -36,7 +36,8 @@ let MetricsChecker = {
|
||||||
showOriginal: day.get("showOriginalContent") || 0,
|
showOriginal: day.get("showOriginalContent") || 0,
|
||||||
detectedLanguageChangedBefore: day.get("detectedLanguageChangedBefore") || 0,
|
detectedLanguageChangedBefore: day.get("detectedLanguageChangedBefore") || 0,
|
||||||
detectedLanguageChangeAfter: day.get("detectedLanguageChangedAfter") || 0,
|
detectedLanguageChangeAfter: day.get("detectedLanguageChangedAfter") || 0,
|
||||||
targetLanguageChanged: day.get("targetLanguageChanged") || 0
|
targetLanguageChanged: day.get("targetLanguageChanged") || 0,
|
||||||
|
autoRejectedOffers: day.get("autoRejectedTranslationOffer") || 0
|
||||||
};
|
};
|
||||||
this._metricsTime = metricsTime;
|
this._metricsTime = metricsTime;
|
||||||
}),
|
}),
|
||||||
|
@ -159,6 +160,19 @@ add_task(function* test_language_change() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_task(function* test_never_offer_translation() {
|
||||||
|
Services.prefs.setCharPref("browser.translation.neverForLanguages", "fr");
|
||||||
|
|
||||||
|
let tab = yield offerTranslatationFor("<h1>Hallo Welt!</h1>", "fr");
|
||||||
|
|
||||||
|
yield MetricsChecker.checkAdditions({
|
||||||
|
autoRejectedOffers: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
Services.prefs.clearUserPref("browser.translation.neverForLanguages")
|
||||||
|
});
|
||||||
|
|
||||||
function getInfobarElement(browser, anonid) {
|
function getInfobarElement(browser, anonid) {
|
||||||
let notif = browser.translationUI
|
let notif = browser.translationUI
|
||||||
.notificationBox.getNotificationWithValue("translation");
|
.notificationBox.getNotificationWithValue("translation");
|
||||||
|
|
|
@ -232,6 +232,11 @@ add_task(function* test_show_original() {
|
||||||
yield test_simple_counter("recordShowOriginalContent", "showOriginalContent");
|
yield test_simple_counter("recordShowOriginalContent", "showOriginalContent");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_task(function* test_show_original() {
|
||||||
|
yield test_simple_counter("recordAutoRejectedTranslationOffer",
|
||||||
|
"autoRejectedTranslationOffer");
|
||||||
|
});
|
||||||
|
|
||||||
add_task(function* test_collect_daily() {
|
add_task(function* test_collect_daily() {
|
||||||
let storage = yield Metrics.Storage("translation");
|
let storage = yield Metrics.Storage("translation");
|
||||||
let provider = new TranslationProvider();
|
let provider = new TranslationProvider();
|
||||||
|
@ -291,6 +296,8 @@ add_task(function* test_healthreporter_json() {
|
||||||
|
|
||||||
yield provider.recordDeniedTranslationOffer();
|
yield provider.recordDeniedTranslationOffer();
|
||||||
|
|
||||||
|
yield provider.recordAutoRejectedTranslationOffer();
|
||||||
|
|
||||||
yield provider.recordShowOriginalContent();
|
yield provider.recordShowOriginalContent();
|
||||||
|
|
||||||
yield reporter.collectMeasurements();
|
yield reporter.collectMeasurements();
|
||||||
|
@ -329,6 +336,9 @@ add_task(function* test_healthreporter_json() {
|
||||||
Assert.ok("deniedTranslationOffer" in translations);
|
Assert.ok("deniedTranslationOffer" in translations);
|
||||||
Assert.equal(translations["deniedTranslationOffer"], 1);
|
Assert.equal(translations["deniedTranslationOffer"], 1);
|
||||||
|
|
||||||
|
Assert.ok("autoRejectedTranslationOffer" in translations);
|
||||||
|
Assert.equal(translations["autoRejectedTranslationOffer"], 1);
|
||||||
|
|
||||||
Assert.ok("showOriginalContent" in translations);
|
Assert.ok("showOriginalContent" in translations);
|
||||||
Assert.equal(translations["showOriginalContent"], 1);
|
Assert.equal(translations["showOriginalContent"], 1);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -357,6 +367,8 @@ add_task(function* test_healthreporter_json2() {
|
||||||
|
|
||||||
yield provider.recordDeniedTranslationOffer();
|
yield provider.recordDeniedTranslationOffer();
|
||||||
|
|
||||||
|
yield provider.recordAutoRejectedTranslationOffer();
|
||||||
|
|
||||||
yield provider.recordShowOriginalContent();
|
yield provider.recordShowOriginalContent();
|
||||||
|
|
||||||
yield reporter.collectMeasurements();
|
yield reporter.collectMeasurements();
|
||||||
|
@ -378,6 +390,7 @@ add_task(function* test_healthreporter_json2() {
|
||||||
Assert.ok(!("detectedLanguageChangedBefore" in translations));
|
Assert.ok(!("detectedLanguageChangedBefore" in translations));
|
||||||
Assert.ok(!("detectedLanguageChangedAfter" in translations));
|
Assert.ok(!("detectedLanguageChangedAfter" in translations));
|
||||||
Assert.ok(!("deniedTranslationOffer" in translations));
|
Assert.ok(!("deniedTranslationOffer" in translations));
|
||||||
|
Assert.ok(!("autoRejectedTranslationOffer" in translations));
|
||||||
Assert.ok(!("showOriginalContent" in translations));
|
Assert.ok(!("showOriginalContent" in translations));
|
||||||
} finally {
|
} finally {
|
||||||
reporter._shutdown();
|
reporter._shutdown();
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const promise = require("devtools/toolkit/deprecated-sync-thenables");
|
const promise = require("devtools/toolkit/deprecated-sync-thenables");
|
||||||
|
loader.lazyGetter(this, "EventEmitter", () => require("devtools/toolkit/event-emitter"));
|
||||||
loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup);
|
loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup);
|
||||||
|
|
||||||
// Maximum number of selector suggestions shown in the panel.
|
// Maximum number of selector suggestions shown in the panel.
|
||||||
|
@ -14,6 +14,10 @@ const MAX_SUGGESTIONS = 15;
|
||||||
/**
|
/**
|
||||||
* Converts any input box on a page to a CSS selector search and suggestion box.
|
* Converts any input box on a page to a CSS selector search and suggestion box.
|
||||||
*
|
*
|
||||||
|
* Emits 'processing-done' event when it is done processing the current
|
||||||
|
* keypress, search request or selection from the list, whether that led to a
|
||||||
|
* search or not.
|
||||||
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param InspectorPanel aInspector
|
* @param InspectorPanel aInspector
|
||||||
* The InspectorPanel whose `walker` attribute should be used for
|
* The InspectorPanel whose `walker` attribute should be used for
|
||||||
|
@ -61,6 +65,7 @@ function SelectorSearch(aInspector, aInputNode) {
|
||||||
// For testing, we need to be able to wait for the most recent node request
|
// For testing, we need to be able to wait for the most recent node request
|
||||||
// to finish. Tests can watch this promise for that.
|
// to finish. Tests can watch this promise for that.
|
||||||
this._lastQuery = promise.resolve(null);
|
this._lastQuery = promise.resolve(null);
|
||||||
|
EventEmitter.decorate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.SelectorSearch = SelectorSearch;
|
exports.SelectorSearch = SelectorSearch;
|
||||||
|
@ -183,6 +188,7 @@ SelectorSearch.prototype = {
|
||||||
_onHTMLSearch: function() {
|
_onHTMLSearch: function() {
|
||||||
let query = this.searchBox.value;
|
let query = this.searchBox.value;
|
||||||
if (query == this._lastSearched) {
|
if (query == this._lastSearched) {
|
||||||
|
this.emit("processing-done");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._lastSearched = query;
|
this._lastSearched = query;
|
||||||
|
@ -196,6 +202,7 @@ SelectorSearch.prototype = {
|
||||||
if (this.searchPopup.isOpen) {
|
if (this.searchPopup.isOpen) {
|
||||||
this.searchPopup.hidePopup();
|
this.searchPopup.hidePopup();
|
||||||
}
|
}
|
||||||
|
this.emit("processing-done");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +265,7 @@ SelectorSearch.prototype = {
|
||||||
}
|
}
|
||||||
this.searchBox.classList.add("devtools-no-search-result");
|
this.searchBox.classList.add("devtools-no-search-result");
|
||||||
return this.showSuggestions();
|
return this.showSuggestions();
|
||||||
});
|
}).then(() => this.emit("processing-done"));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -332,7 +339,10 @@ SelectorSearch.prototype = {
|
||||||
aEvent.preventDefault();
|
aEvent.preventDefault();
|
||||||
aEvent.stopPropagation();
|
aEvent.stopPropagation();
|
||||||
if (this._searchResults && this._searchResults.length > 0) {
|
if (this._searchResults && this._searchResults.length > 0) {
|
||||||
this._lastQuery = this._selectResult(this._searchIndex);
|
this._lastQuery = this._selectResult(this._searchIndex).then(() => this.emit("processing-done"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.emit("processing-done");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -393,6 +403,7 @@ SelectorSearch.prototype = {
|
||||||
this._onHTMLSearch();
|
this._onHTMLSearch();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
this.emit("processing-done");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -48,8 +48,7 @@ skip-if = true # Bug 1028609
|
||||||
[browser_inspector_search-02.js]
|
[browser_inspector_search-02.js]
|
||||||
[browser_inspector_search-03.js]
|
[browser_inspector_search-03.js]
|
||||||
[browser_inspector_select-last-selected.js]
|
[browser_inspector_select-last-selected.js]
|
||||||
# [browser_inspector_search-navigation.js]
|
[browser_inspector_search-navigation.js]
|
||||||
# Disabled for too many intermittent failures (bug 851349)
|
|
||||||
[browser_inspector_sidebarstate.js]
|
[browser_inspector_sidebarstate.js]
|
||||||
[browser_inspector_switch-to-inspector-on-pick.js]
|
[browser_inspector_switch-to-inspector-on-pick.js]
|
||||||
[browser_inspector_update-on-navigation.js]
|
[browser_inspector_update-on-navigation.js]
|
||||||
|
|
|
@ -1,162 +1,73 @@
|
||||||
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
function test()
|
// Check that searchbox value is correct when suggestions popup is navigated
|
||||||
{
|
// with keyboard.
|
||||||
requestLongerTimeout(2);
|
|
||||||
|
|
||||||
let inspector, searchBox, state, panel;
|
// Test data as pairs of [key to press, expected content of searchbox].
|
||||||
let panelOpeningStates = [0, 3, 9, 14, 17];
|
const KEY_STATES = [
|
||||||
let panelClosingStates = [2, 8, 13, 16];
|
["d", "d"],
|
||||||
|
["i", "di"],
|
||||||
|
["v", "div"],
|
||||||
|
[".", "div."],
|
||||||
|
["VK_UP", "div.c1"],
|
||||||
|
["VK_DOWN", "div.l1"],
|
||||||
|
["VK_DOWN", "div.l1"],
|
||||||
|
["VK_BACK_SPACE", "div.l"],
|
||||||
|
["VK_TAB", "div.l1"],
|
||||||
|
[" ", "div.l1 "],
|
||||||
|
["VK_UP", "div.l1 div"],
|
||||||
|
["VK_UP", "div.l1 div"],
|
||||||
|
[".", "div.l1 div."],
|
||||||
|
["VK_TAB", "div.l1 div.c1"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 div.c"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 div."],
|
||||||
|
["VK_BACK_SPACE", "div.l1 div"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 di"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 d"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 "],
|
||||||
|
["VK_UP", "div.l1 div"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 di"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 d"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 "],
|
||||||
|
["VK_UP", "div.l1 div"],
|
||||||
|
["VK_UP", "div.l1 div"],
|
||||||
|
["VK_TAB", "div.l1 div"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 di"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 d"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 "],
|
||||||
|
["VK_DOWN", "div.l1 div"],
|
||||||
|
["VK_DOWN", "div.l1 span"],
|
||||||
|
["VK_DOWN", "div.l1 span"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 spa"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 sp"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 s"],
|
||||||
|
["VK_BACK_SPACE", "div.l1 "],
|
||||||
|
["VK_BACK_SPACE", "div.l1"],
|
||||||
|
["VK_BACK_SPACE", "div.l"],
|
||||||
|
["VK_BACK_SPACE", "div."],
|
||||||
|
["VK_BACK_SPACE", "div"],
|
||||||
|
["VK_BACK_SPACE", "di"],
|
||||||
|
["VK_BACK_SPACE", "d"],
|
||||||
|
["VK_BACK_SPACE", ""],
|
||||||
|
];
|
||||||
|
|
||||||
// The various states of the inspector: [key, query]
|
const TEST_URL = TEST_URL_ROOT +
|
||||||
// [
|
"doc_inspector_search-suggestions.html";
|
||||||
// what key to press,
|
|
||||||
// what should be the text in the searchbox
|
|
||||||
// ]
|
|
||||||
let keyStates = [
|
|
||||||
["d", "d"],
|
|
||||||
["i", "di"],
|
|
||||||
["v", "div"],
|
|
||||||
[".", "div."],
|
|
||||||
["VK_UP", "div.c1"],
|
|
||||||
["VK_DOWN", "div.l1"],
|
|
||||||
["VK_DOWN", "div.l1"],
|
|
||||||
["VK_BACK_SPACE", "div.l"],
|
|
||||||
["VK_TAB", "div.l1"],
|
|
||||||
[" ", "div.l1 "],
|
|
||||||
["VK_UP", "div.l1 DIV"],
|
|
||||||
["VK_UP", "div.l1 DIV"],
|
|
||||||
[".", "div.l1 DIV."],
|
|
||||||
["VK_TAB", "div.l1 DIV.c1"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 DIV.c"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 DIV."],
|
|
||||||
["VK_BACK_SPACE", "div.l1 DIV"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 DI"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 D"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 "],
|
|
||||||
["VK_UP", "div.l1 DIV"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 DI"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 D"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 "],
|
|
||||||
["VK_UP", "div.l1 DIV"],
|
|
||||||
["VK_UP", "div.l1 DIV"],
|
|
||||||
["VK_TAB", "div.l1 DIV"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 DI"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 D"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 "],
|
|
||||||
["VK_DOWN", "div.l1 DIV"],
|
|
||||||
["VK_DOWN", "div.l1 SPAN"],
|
|
||||||
["VK_DOWN", "div.l1 SPAN"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 SPA"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 SP"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 S"],
|
|
||||||
["VK_BACK_SPACE", "div.l1 "],
|
|
||||||
["VK_BACK_SPACE", "div.l1"],
|
|
||||||
["VK_BACK_SPACE", "div.l"],
|
|
||||||
["VK_BACK_SPACE", "div."],
|
|
||||||
["VK_BACK_SPACE", "div"],
|
|
||||||
["VK_BACK_SPACE", "di"],
|
|
||||||
["VK_BACK_SPACE", "d"],
|
|
||||||
["VK_BACK_SPACE", ""],
|
|
||||||
];
|
|
||||||
|
|
||||||
gBrowser.selectedTab = gBrowser.addTab();
|
let test = asyncTest(function* () {
|
||||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
let { inspector } = yield openInspectorForURL(TEST_URL);
|
||||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
yield focusSearchBoxUsingShortcut(inspector.panelWin);
|
||||||
waitForFocus(setupTest, content);
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
content.location = "http://mochi.test:8888/browser/browser/devtools/inspector/test/doc_inspector_search-suggestions.html";
|
for (let [key, query] of KEY_STATES) {
|
||||||
|
info("Pressing key " + key + " to get searchbox value as " + query);
|
||||||
|
|
||||||
function $(id) {
|
let done = inspector.searchSuggestions.once("processing-done");
|
||||||
if (id == null) return null;
|
|
||||||
return content.document.getElementById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupTest()
|
|
||||||
{
|
|
||||||
openInspector(startTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
function startTest(aInspector)
|
|
||||||
{
|
|
||||||
inspector = aInspector;
|
|
||||||
searchBox =
|
|
||||||
inspector.panelWin.document.getElementById("inspector-searchbox");
|
|
||||||
panel = inspector.searchSuggestions.searchPopup._list;
|
|
||||||
|
|
||||||
focusSearchBoxUsingShortcut(inspector.panelWin, function() {
|
|
||||||
searchBox.addEventListener("keypress", checkState, true);
|
|
||||||
panel.addEventListener("keypress", checkState, true);
|
|
||||||
checkStateAndMoveOn(0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkStateAndMoveOn(index) {
|
|
||||||
if (index == keyStates.length) {
|
|
||||||
finishUp();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let [key, query] = keyStates[index];
|
|
||||||
state = index;
|
|
||||||
|
|
||||||
info("pressing key " + key + " to get searchbox value as " + query);
|
|
||||||
EventUtils.synthesizeKey(key, {}, inspector.panelWin);
|
EventUtils.synthesizeKey(key, {}, inspector.panelWin);
|
||||||
|
yield done;
|
||||||
|
|
||||||
|
is(inspector.searchBox.value, query, "The searchbox value is correct.");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
function checkState(event) {
|
|
||||||
if (event && event.keyCode != event.DOM_VK_UP &&
|
|
||||||
event.keyCode != event.DOM_VK_DOWN) {
|
|
||||||
info("Should wait before server sends the qSA response.");
|
|
||||||
inspector.searchSuggestions._lastQuery
|
|
||||||
.then(() => checkState(), () => checkState());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (panelOpeningStates.indexOf(state) != -1 &&
|
|
||||||
!inspector.searchSuggestions.searchPopup.isOpen) {
|
|
||||||
info("Panel is not open, should wait before it shows up.");
|
|
||||||
panel.parentNode.addEventListener("popupshown", function retry() {
|
|
||||||
panel.parentNode.removeEventListener("popupshown", retry, false);
|
|
||||||
info("Panel is visible now");
|
|
||||||
executeSoon(checkState);
|
|
||||||
}, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (panelClosingStates.indexOf(state) != -1 &&
|
|
||||||
panel.parentNode.state != "closed") {
|
|
||||||
info("Panel is open, should wait for it to close.");
|
|
||||||
panel.parentNode.addEventListener("popuphidden", function retry() {
|
|
||||||
panel.parentNode.removeEventListener("popuphidden", retry, false);
|
|
||||||
info("Panel is hidden now");
|
|
||||||
executeSoon(checkState);
|
|
||||||
}, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Using setTimout as the "command" event fires at delay after keypress
|
|
||||||
window.setTimeout(function() {
|
|
||||||
let [key, query] = keyStates[state];
|
|
||||||
|
|
||||||
if (searchBox.value == query) {
|
|
||||||
ok(true, "The suggestion at " + state + "th step on " +
|
|
||||||
"pressing " + key + " key is correct.");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
info("value is not correct, waiting longer for state " + state +
|
|
||||||
" with panel " + panel.parentNode.state);
|
|
||||||
checkState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
checkStateAndMoveOn(state + 1);
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
function finishUp() {
|
|
||||||
searchBox = null;
|
|
||||||
panel = null;
|
|
||||||
gBrowser.removeCurrentTab();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,11 +21,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
||||||
this.ContentLinkHandler = {
|
this.ContentLinkHandler = {
|
||||||
init: function(chromeGlobal) {
|
init: function(chromeGlobal) {
|
||||||
chromeGlobal.addEventListener("DOMLinkAdded", (event) => {
|
chromeGlobal.addEventListener("DOMLinkAdded", (event) => {
|
||||||
this.onLinkAdded(event, chromeGlobal);
|
this.onLinkEvent(event, chromeGlobal);
|
||||||
|
}, false);
|
||||||
|
chromeGlobal.addEventListener("DOMLinkChanged", (event) => {
|
||||||
|
this.onLinkEvent(event, chromeGlobal);
|
||||||
}, false);
|
}, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
onLinkAdded: function(event, chromeGlobal) {
|
onLinkEvent: function(event, chromeGlobal) {
|
||||||
var link = event.originalTarget;
|
var link = event.originalTarget;
|
||||||
var rel = link.rel && link.rel.toLowerCase();
|
var rel = link.rel && link.rel.toLowerCase();
|
||||||
if (!link || !link.ownerDocument || !rel || !link.href)
|
if (!link || !link.ownerDocument || !rel || !link.href)
|
||||||
|
@ -47,7 +50,7 @@ this.ContentLinkHandler = {
|
||||||
switch (relVal) {
|
switch (relVal) {
|
||||||
case "feed":
|
case "feed":
|
||||||
case "alternate":
|
case "alternate":
|
||||||
if (!feedAdded) {
|
if (!feedAdded && event.type == "DOMLinkAdded") {
|
||||||
if (!rels.feed && rels.alternate && rels.stylesheet)
|
if (!rels.feed && rels.alternate && rels.stylesheet)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -69,11 +72,11 @@ this.ContentLinkHandler = {
|
||||||
if (!uri)
|
if (!uri)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
[iconAdded] = chromeGlobal.sendSyncMessage("Link:AddIcon", {url: uri.spec});
|
[iconAdded] = chromeGlobal.sendSyncMessage("Link:SetIcon", {url: uri.spec});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "search":
|
case "search":
|
||||||
if (!searchAdded) {
|
if (!searchAdded && event.type == "DOMLinkAdded") {
|
||||||
var type = link.type && link.type.toLowerCase();
|
var type = link.type && link.type.toLowerCase();
|
||||||
type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
|
type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
|
||||||
|
|
||||||
|
|
|
@ -498,7 +498,7 @@ this.OpenGraphBuilder = {
|
||||||
return endpointURL;
|
return endpointURL;
|
||||||
},
|
},
|
||||||
|
|
||||||
getData: function(browser) {
|
getData: function(browser, target) {
|
||||||
let res = {
|
let res = {
|
||||||
url: this._validateURL(browser, browser.currentURI.spec),
|
url: this._validateURL(browser, browser.currentURI.spec),
|
||||||
title: browser.contentDocument.title,
|
title: browser.contentDocument.title,
|
||||||
|
@ -507,9 +507,14 @@ this.OpenGraphBuilder = {
|
||||||
this._getMetaData(browser, res);
|
this._getMetaData(browser, res);
|
||||||
this._getLinkData(browser, res);
|
this._getLinkData(browser, res);
|
||||||
this._getPageData(browser, res);
|
this._getPageData(browser, res);
|
||||||
|
res.microdata = this.getMicrodata(browser, target);
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getMicrodata: function (browser, target) {
|
||||||
|
return getMicrodata(browser.contentDocument, target);
|
||||||
|
},
|
||||||
|
|
||||||
_getMetaData: function(browser, o) {
|
_getMetaData: function(browser, o) {
|
||||||
// query for standardized meta data
|
// query for standardized meta data
|
||||||
let els = browser.contentDocument
|
let els = browser.contentDocument
|
||||||
|
@ -522,7 +527,14 @@ this.OpenGraphBuilder = {
|
||||||
if (!value)
|
if (!value)
|
||||||
continue;
|
continue;
|
||||||
value = unescapeService.unescape(value.trim());
|
value = unescapeService.unescape(value.trim());
|
||||||
switch (el.getAttribute("property") || el.getAttribute("name")) {
|
let key = el.getAttribute("property") || el.getAttribute("name");
|
||||||
|
if (!key)
|
||||||
|
continue;
|
||||||
|
// There are a wide array of possible meta tags, expressing articles,
|
||||||
|
// products, etc. so all meta tags are passed through but we touch up the
|
||||||
|
// most common attributes.
|
||||||
|
o[key] = value;
|
||||||
|
switch (key) {
|
||||||
case "title":
|
case "title":
|
||||||
case "og:title":
|
case "og:title":
|
||||||
o.title = value;
|
o.title = value;
|
||||||
|
@ -577,6 +589,19 @@ this.OpenGraphBuilder = {
|
||||||
case "image_src":
|
case "image_src":
|
||||||
o.previews.push(url);
|
o.previews.push(url);
|
||||||
break;
|
break;
|
||||||
|
case "alternate":
|
||||||
|
// expressly for oembed support but we're liberal here and will let
|
||||||
|
// other alternate links through. oembed defines an href, supplied by
|
||||||
|
// the site, where you can fetch additional meta data about a page.
|
||||||
|
// We'll let the client fetch the oembed data themselves, but they
|
||||||
|
// need the data from this link.
|
||||||
|
if (!o.alternate)
|
||||||
|
o.alternate = [];
|
||||||
|
o.alternate.push({
|
||||||
|
"type": el.getAttribute("type"),
|
||||||
|
"href": el.getAttribute("href"),
|
||||||
|
"title": el.getAttribute("title")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -610,3 +635,43 @@ this.OpenGraphBuilder = {
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// getMicrodata (and getObject) based on wg algorythm to convert microdata to json
|
||||||
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/microdata-2.html#json
|
||||||
|
function getMicrodata(document, target) {
|
||||||
|
|
||||||
|
function _getObject(item) {
|
||||||
|
let result = {};
|
||||||
|
if (item.itemType.length)
|
||||||
|
result.types = [i for (i of item.itemType)];
|
||||||
|
if (item.itemId)
|
||||||
|
result.itemId = item.itemid;
|
||||||
|
if (item.properties.length)
|
||||||
|
result.properties = {};
|
||||||
|
for (let elem of item.properties) {
|
||||||
|
let value;
|
||||||
|
if (elem.itemScope)
|
||||||
|
value = _getObject(elem);
|
||||||
|
else if (elem.itemValue)
|
||||||
|
value = elem.itemValue;
|
||||||
|
// handle mis-formatted microdata
|
||||||
|
else if (elem.hasAttribute("content"))
|
||||||
|
value = elem.getAttribute("content");
|
||||||
|
|
||||||
|
for (let prop of elem.itemProp) {
|
||||||
|
if (!result.properties[prop])
|
||||||
|
result.properties[prop] = [];
|
||||||
|
result.properties[prop].push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = { items: [] };
|
||||||
|
let elms = target ? [target] : document.getItems();
|
||||||
|
for (let el of elms) {
|
||||||
|
if (el.itemScope)
|
||||||
|
result.items.push(_getObject(el));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ _JAVA_HARNESS := \
|
||||||
RobocopShare2.java \
|
RobocopShare2.java \
|
||||||
RobocopUtils.java \
|
RobocopUtils.java \
|
||||||
PaintedSurface.java \
|
PaintedSurface.java \
|
||||||
|
StructuredLogger.java \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
java-harness := $(addprefix $(srcdir)/,$(_JAVA_HARNESS))
|
java-harness := $(addprefix $(srcdir)/,$(_JAVA_HARNESS))
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
// This implements the structured logging API described here: http://mozbase.readthedocs.org/en/latest/mozlog_structured.html
|
||||||
|
public class StructuredLogger {
|
||||||
|
private final static HashSet<String> validTestStatus = new HashSet<String>(Arrays.asList("PASS", "FAIL", "TIMEOUT", "NOTRUN", "ASSERT"));
|
||||||
|
private final static HashSet<String> validTestEnd = new HashSet<String>(Arrays.asList("PASS", "FAIL", "OK", "ERROR", "TIMEOUT",
|
||||||
|
"CRASH", "ASSERT", "SKIP"));
|
||||||
|
|
||||||
|
private String mName;
|
||||||
|
private String mComponent;
|
||||||
|
private LoggerCallback mCallback;
|
||||||
|
|
||||||
|
static public interface LoggerCallback {
|
||||||
|
public void call(String output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A default logger callback that prints the JSON output to stdout.
|
||||||
|
* This is not to be used in robocop as we write to a log file. */
|
||||||
|
static class StandardLoggerCallback implements LoggerCallback {
|
||||||
|
public void call(String output) {
|
||||||
|
System.out.println(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StructuredLogger(String name, String component, LoggerCallback callback) {
|
||||||
|
mName = name;
|
||||||
|
mComponent = component;
|
||||||
|
mCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StructuredLogger(String name, String component) {
|
||||||
|
this(name, component, new StandardLoggerCallback());
|
||||||
|
}
|
||||||
|
|
||||||
|
public StructuredLogger(String name) {
|
||||||
|
this(name, null, new StandardLoggerCallback());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void suiteStart(List<String> tests, Map<String, Object> runInfo) {
|
||||||
|
HashMap<String, Object> data = new HashMap<String, Object>();
|
||||||
|
data.put("tests", tests);
|
||||||
|
if (runInfo != null) {
|
||||||
|
data.put("run_info", runInfo);
|
||||||
|
}
|
||||||
|
this.logData("suite_start", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void suiteStart(List<String> tests) {
|
||||||
|
this.suiteStart(tests, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void suiteEnd() {
|
||||||
|
this.logData("suite_end");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStart(String test) {
|
||||||
|
HashMap<String, Object> data = new HashMap<String, Object>();
|
||||||
|
data.put("test", test);
|
||||||
|
this.logData("test_start", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStatus(String test, String subtest, String status, String expected, String message) {
|
||||||
|
status = status.toUpperCase();
|
||||||
|
if (!StructuredLogger.validTestStatus.contains(status)) {
|
||||||
|
throw new IllegalArgumentException("Unrecognized status: " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, Object> data = new HashMap<String, Object>();
|
||||||
|
data.put("test", test);
|
||||||
|
data.put("subtest", subtest);
|
||||||
|
data.put("status", status);
|
||||||
|
|
||||||
|
if (message != null) {
|
||||||
|
data.put("message", message);
|
||||||
|
}
|
||||||
|
if (!expected.equals(status)) {
|
||||||
|
data.put("expected", expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logData("test_status", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStatus(String test, String subtest, String status, String message) {
|
||||||
|
this.testStatus(test, subtest, status, "PASS", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEnd(String test, String status, String expected, String message, Map<String, Object> extra) {
|
||||||
|
status = status.toUpperCase();
|
||||||
|
if (!StructuredLogger.validTestEnd.contains(status)) {
|
||||||
|
throw new IllegalArgumentException("Unrecognized status: " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, Object> data = new HashMap<String, Object>();
|
||||||
|
data.put("test", test);
|
||||||
|
data.put("status", status);
|
||||||
|
|
||||||
|
if (message != null) {
|
||||||
|
data.put("message", message);
|
||||||
|
}
|
||||||
|
if (extra != null) {
|
||||||
|
data.put("extra", extra);
|
||||||
|
}
|
||||||
|
if (!expected.equals(status) && !status.equals("SKIP")) {
|
||||||
|
data.put("expected", expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logData("test_end", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEnd(String test, String status, String expected, String message) {
|
||||||
|
this.testEnd(test, status, expected, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEnd(String test, String status, String message) {
|
||||||
|
this.testEnd(test, status, "OK", message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void debug(String message) {
|
||||||
|
this.log("debug", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void info(String message) {
|
||||||
|
this.log("info", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void warning(String message) {
|
||||||
|
this.log("warning", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(String message) {
|
||||||
|
this.log("error", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void critical(String message) {
|
||||||
|
this.log("critical", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void log(String level, String message) {
|
||||||
|
HashMap<String, Object> data = new HashMap<String, Object>();
|
||||||
|
data.put("message", message);
|
||||||
|
data.put("level", level);
|
||||||
|
this.logData("log", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap<String, Object> makeLogData(String action, Map<String, Object> data) {
|
||||||
|
HashMap<String, Object> allData = new HashMap<String, Object>();
|
||||||
|
allData.put("action", action);
|
||||||
|
allData.put("time", System.currentTimeMillis());
|
||||||
|
allData.put("thread", JSONObject.NULL);
|
||||||
|
allData.put("pid", JSONObject.NULL);
|
||||||
|
allData.put("source", mName);
|
||||||
|
if (mComponent != null) {
|
||||||
|
allData.put("component", mComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
allData.putAll(data);
|
||||||
|
|
||||||
|
return allData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logData(String action, Map<String, Object> data) {
|
||||||
|
HashMap<String, Object> logData = this.makeLogData(action, data);
|
||||||
|
JSONObject jsonObject = new JSONObject(logData);
|
||||||
|
mCallback.call(jsonObject.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logData(String action) {
|
||||||
|
this.logData(action, new HashMap<String, Object>());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -27,6 +27,7 @@
|
||||||
#include "nsMathUtils.h"
|
#include "nsMathUtils.h"
|
||||||
#include "nsTArrayForwardDeclare.h"
|
#include "nsTArrayForwardDeclare.h"
|
||||||
#include "Units.h"
|
#include "Units.h"
|
||||||
|
#include "mozilla/dom/AutocompleteInfoBinding.h"
|
||||||
|
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
// Undefine LoadImage to prevent naming conflict with Windows.
|
// Undefine LoadImage to prevent naming conflict with Windows.
|
||||||
|
@ -2045,8 +2046,22 @@ public:
|
||||||
*
|
*
|
||||||
* @return whether aAttr was valid and can be cached.
|
* @return whether aAttr was valid and can be cached.
|
||||||
*/
|
*/
|
||||||
static AutocompleteAttrState SerializeAutocompleteAttribute(const nsAttrValue* aAttr,
|
static AutocompleteAttrState
|
||||||
nsAString& aResult);
|
SerializeAutocompleteAttribute(const nsAttrValue* aAttr,
|
||||||
|
nsAString& aResult,
|
||||||
|
AutocompleteAttrState aCachedState =
|
||||||
|
eAutocompleteAttrState_Unknown);
|
||||||
|
|
||||||
|
/* Variation that is used to retrieve a dictionary of the parts of the
|
||||||
|
* autocomplete attribute.
|
||||||
|
*
|
||||||
|
* @return whether aAttr was valid and can be cached.
|
||||||
|
*/
|
||||||
|
static AutocompleteAttrState
|
||||||
|
SerializeAutocompleteAttribute(const nsAttrValue* aAttr,
|
||||||
|
mozilla::dom::AutocompleteInfo& aInfo,
|
||||||
|
AutocompleteAttrState aCachedState =
|
||||||
|
eAutocompleteAttrState_Unknown);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This will parse aSource, to extract the value of the pseudo attribute
|
* This will parse aSource, to extract the value of the pseudo attribute
|
||||||
|
@ -2205,8 +2220,9 @@ private:
|
||||||
static void* AllocClassMatchingInfo(nsINode* aRootNode,
|
static void* AllocClassMatchingInfo(nsINode* aRootNode,
|
||||||
const nsString* aClasses);
|
const nsString* aClasses);
|
||||||
|
|
||||||
|
// Fills in aInfo with the tokens from the supplied autocomplete attribute.
|
||||||
static AutocompleteAttrState InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrVal,
|
static AutocompleteAttrState InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrVal,
|
||||||
nsAString& aResult);
|
mozilla::dom::AutocompleteInfo& aInfo);
|
||||||
|
|
||||||
static nsIXPConnect *sXPConnect;
|
static nsIXPConnect *sXPConnect;
|
||||||
|
|
||||||
|
|
|
@ -766,17 +766,73 @@ nsContentUtils::IsAutocompleteEnabled(nsIDOMHTMLInputElement* aInput)
|
||||||
|
|
||||||
nsContentUtils::AutocompleteAttrState
|
nsContentUtils::AutocompleteAttrState
|
||||||
nsContentUtils::SerializeAutocompleteAttribute(const nsAttrValue* aAttr,
|
nsContentUtils::SerializeAutocompleteAttribute(const nsAttrValue* aAttr,
|
||||||
nsAString& aResult)
|
nsAString& aResult,
|
||||||
|
AutocompleteAttrState aCachedState)
|
||||||
{
|
{
|
||||||
AutocompleteAttrState state = InternalSerializeAutocompleteAttribute(aAttr, aResult);
|
if (!aAttr ||
|
||||||
if (state == eAutocompleteAttrState_Valid) {
|
aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
|
||||||
ASCIIToLower(aResult);
|
return aCachedState;
|
||||||
} else {
|
|
||||||
aResult.Truncate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
|
||||||
|
uint32_t atomCount = aAttr->GetAtomCount();
|
||||||
|
for (uint32_t i = 0; i < atomCount; i++) {
|
||||||
|
if (i != 0) {
|
||||||
|
aResult.Append(' ');
|
||||||
|
}
|
||||||
|
aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
|
||||||
|
}
|
||||||
|
nsContentUtils::ASCIIToLower(aResult);
|
||||||
|
return aCachedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
aResult.Truncate();
|
||||||
|
|
||||||
|
mozilla::dom::AutocompleteInfo info;
|
||||||
|
AutocompleteAttrState state =
|
||||||
|
InternalSerializeAutocompleteAttribute(aAttr, info);
|
||||||
|
if (state == eAutocompleteAttrState_Valid) {
|
||||||
|
// Concatenate the info fields.
|
||||||
|
aResult = info.mSection;
|
||||||
|
|
||||||
|
if (!info.mAddressType.IsEmpty()) {
|
||||||
|
if (!aResult.IsEmpty()) {
|
||||||
|
aResult += ' ';
|
||||||
|
}
|
||||||
|
aResult += info.mAddressType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.mContactType.IsEmpty()) {
|
||||||
|
if (!aResult.IsEmpty()) {
|
||||||
|
aResult += ' ';
|
||||||
|
}
|
||||||
|
aResult += info.mContactType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.mFieldName.IsEmpty()) {
|
||||||
|
if (!aResult.IsEmpty()) {
|
||||||
|
aResult += ' ';
|
||||||
|
}
|
||||||
|
aResult += info.mFieldName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsContentUtils::AutocompleteAttrState
|
||||||
|
nsContentUtils::SerializeAutocompleteAttribute(const nsAttrValue* aAttr,
|
||||||
|
mozilla::dom::AutocompleteInfo& aInfo,
|
||||||
|
AutocompleteAttrState aCachedState)
|
||||||
|
{
|
||||||
|
if (!aAttr ||
|
||||||
|
aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
|
||||||
|
return aCachedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
return InternalSerializeAutocompleteAttribute(aAttr, aInfo);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to validate the @autocomplete tokens.
|
* Helper to validate the @autocomplete tokens.
|
||||||
*
|
*
|
||||||
|
@ -784,7 +840,7 @@ nsContentUtils::SerializeAutocompleteAttribute(const nsAttrValue* aAttr,
|
||||||
*/
|
*/
|
||||||
nsContentUtils::AutocompleteAttrState
|
nsContentUtils::AutocompleteAttrState
|
||||||
nsContentUtils::InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrVal,
|
nsContentUtils::InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrVal,
|
||||||
nsAString& aResult)
|
mozilla::dom::AutocompleteInfo& aInfo)
|
||||||
{
|
{
|
||||||
// No sandbox attribute so we are done
|
// No sandbox attribute so we are done
|
||||||
if (!aAttrVal) {
|
if (!aAttrVal) {
|
||||||
|
@ -801,6 +857,7 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrV
|
||||||
AutocompleteCategory category;
|
AutocompleteCategory category;
|
||||||
nsAttrValue enumValue;
|
nsAttrValue enumValue;
|
||||||
|
|
||||||
|
nsAutoString str;
|
||||||
bool result = enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
|
bool result = enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
|
||||||
if (result) {
|
if (result) {
|
||||||
// Off/Automatic/Normal categories.
|
// Off/Automatic/Normal categories.
|
||||||
|
@ -809,7 +866,9 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrV
|
||||||
if (numTokens > 1) {
|
if (numTokens > 1) {
|
||||||
return eAutocompleteAttrState_Invalid;
|
return eAutocompleteAttrState_Invalid;
|
||||||
}
|
}
|
||||||
enumValue.ToString(aResult);
|
enumValue.ToString(str);
|
||||||
|
ASCIIToLower(str);
|
||||||
|
aInfo.mFieldName.Assign(str);
|
||||||
return eAutocompleteAttrState_Valid;
|
return eAutocompleteAttrState_Valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -837,7 +896,9 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrV
|
||||||
category = eAutocompleteCategory_CONTACT;
|
category = eAutocompleteCategory_CONTACT;
|
||||||
}
|
}
|
||||||
|
|
||||||
enumValue.ToString(aResult);
|
enumValue.ToString(str);
|
||||||
|
ASCIIToLower(str);
|
||||||
|
aInfo.mFieldName.Assign(str);
|
||||||
|
|
||||||
// We are done if this was the only token.
|
// We are done if this was the only token.
|
||||||
if (numTokens == 1) {
|
if (numTokens == 1) {
|
||||||
|
@ -851,10 +912,10 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrV
|
||||||
nsAttrValue contactFieldHint;
|
nsAttrValue contactFieldHint;
|
||||||
result = contactFieldHint.ParseEnumValue(tokenString, kAutocompleteContactFieldHintTable, false);
|
result = contactFieldHint.ParseEnumValue(tokenString, kAutocompleteContactFieldHintTable, false);
|
||||||
if (result) {
|
if (result) {
|
||||||
aResult.Insert(' ', 0);
|
|
||||||
nsAutoString contactFieldHintString;
|
nsAutoString contactFieldHintString;
|
||||||
contactFieldHint.ToString(contactFieldHintString);
|
contactFieldHint.ToString(contactFieldHintString);
|
||||||
aResult.Insert(contactFieldHintString, 0);
|
ASCIIToLower(contactFieldHintString);
|
||||||
|
aInfo.mContactType.Assign(contactFieldHintString);
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return eAutocompleteAttrState_Valid;
|
return eAutocompleteAttrState_Valid;
|
||||||
}
|
}
|
||||||
|
@ -866,16 +927,21 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrV
|
||||||
// Check for billing/shipping tokens
|
// Check for billing/shipping tokens
|
||||||
nsAttrValue fieldHint;
|
nsAttrValue fieldHint;
|
||||||
if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable, false)) {
|
if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable, false)) {
|
||||||
aResult.Insert(' ', 0);
|
|
||||||
nsString fieldHintString;
|
nsString fieldHintString;
|
||||||
fieldHint.ToString(fieldHintString);
|
fieldHint.ToString(fieldHintString);
|
||||||
aResult.Insert(fieldHintString, 0);
|
ASCIIToLower(fieldHintString);
|
||||||
|
aInfo.mAddressType.Assign(fieldHintString);
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return eAutocompleteAttrState_Valid;
|
return eAutocompleteAttrState_Valid;
|
||||||
}
|
}
|
||||||
--index;
|
--index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the fields as the autocomplete attribute is invalid.
|
||||||
|
aInfo.mAddressType.Truncate();
|
||||||
|
aInfo.mContactType.Truncate();
|
||||||
|
aInfo.mFieldName.Truncate();
|
||||||
|
|
||||||
return eAutocompleteAttrState_Invalid;
|
return eAutocompleteAttrState_Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1529,23 +1529,10 @@ HTMLInputElement::GetAutocomplete(nsAString& aValue)
|
||||||
{
|
{
|
||||||
aValue.Truncate(0);
|
aValue.Truncate(0);
|
||||||
const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
|
const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
|
||||||
if (!attributeVal ||
|
|
||||||
mAutocompleteAttrState == nsContentUtils::eAutocompleteAttrState_Invalid) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
if (mAutocompleteAttrState == nsContentUtils::eAutocompleteAttrState_Valid) {
|
|
||||||
uint32_t atomCount = attributeVal->GetAtomCount();
|
|
||||||
for (uint32_t i = 0; i < atomCount; i++) {
|
|
||||||
if (i != 0) {
|
|
||||||
aValue.Append(' ');
|
|
||||||
}
|
|
||||||
aValue.Append(nsDependentAtomString(attributeVal->AtomAt(i)));
|
|
||||||
}
|
|
||||||
nsContentUtils::ASCIIToLower(aValue);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
mAutocompleteAttrState = nsContentUtils::SerializeAutocompleteAttribute(attributeVal, aValue);
|
mAutocompleteAttrState =
|
||||||
|
nsContentUtils::SerializeAutocompleteAttribute(attributeVal, aValue,
|
||||||
|
mAutocompleteAttrState);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1555,6 +1542,15 @@ HTMLInputElement::SetAutocomplete(const nsAString& aValue)
|
||||||
return SetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete, nullptr, aValue, true);
|
return SetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete, nullptr, aValue, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HTMLInputElement::GetAutocompleteInfo(AutocompleteInfo& aInfo)
|
||||||
|
{
|
||||||
|
const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
|
||||||
|
mAutocompleteAttrState =
|
||||||
|
nsContentUtils::SerializeAutocompleteAttribute(attributeVal, aInfo,
|
||||||
|
mAutocompleteAttrState);
|
||||||
|
}
|
||||||
|
|
||||||
int32_t
|
int32_t
|
||||||
HTMLInputElement::TabIndexDefault()
|
HTMLInputElement::TabIndexDefault()
|
||||||
{
|
{
|
||||||
|
|
|
@ -370,6 +370,8 @@ public:
|
||||||
SetHTMLAttr(nsGkAtoms::autocomplete, aValue, aRv);
|
SetHTMLAttr(nsGkAtoms::autocomplete, aValue, aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetAutocompleteInfo(AutocompleteInfo& aInfo);
|
||||||
|
|
||||||
bool Autofocus() const
|
bool Autofocus() const
|
||||||
{
|
{
|
||||||
return GetBoolAttr(nsGkAtoms::autofocus);
|
return GetBoolAttr(nsGkAtoms::autofocus);
|
||||||
|
|
|
@ -320,6 +320,7 @@ HTMLLinkElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||||
// to get updated information about the visitedness from Link.
|
// to get updated information about the visitedness from Link.
|
||||||
if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
|
if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
|
||||||
Link::ResetLinkState(!!aNotify, true);
|
Link::ResetLinkState(!!aNotify, true);
|
||||||
|
CreateAndDispatchEvent(OwnerDoc(), NS_LITERAL_STRING("DOMLinkChanged"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None &&
|
if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None &&
|
||||||
|
@ -382,6 +383,7 @@ HTMLLinkElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
|
||||||
// to get updated information about the visitedness from Link.
|
// to get updated information about the visitedness from Link.
|
||||||
if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
|
if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
|
||||||
Link::ResetLinkState(!!aNotify, false);
|
Link::ResetLinkState(!!aNotify, false);
|
||||||
|
CreateAndDispatchEvent(OwnerDoc(), NS_LITERAL_STRING("DOMLinkChanged"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[DEFAULT]
|
||||||
|
[test_autocompleteinfo.html]
|
|
@ -0,0 +1,99 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
Test getAutocompleteInfo() on <input>
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<title>Test for getAutocompleteInfo()</title>
|
||||||
|
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content" style="display: none">
|
||||||
|
<form>
|
||||||
|
<input id="input"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var values = [
|
||||||
|
// Missing or empty attribute
|
||||||
|
[undefined, {}],
|
||||||
|
["", {}],
|
||||||
|
|
||||||
|
// One token
|
||||||
|
["on", {fieldName: "on" }],
|
||||||
|
["On", {fieldName: "on" }],
|
||||||
|
["off", {fieldName: "off" } ],
|
||||||
|
["username", {fieldName: "username" }],
|
||||||
|
[" username ", {fieldName: "username" }],
|
||||||
|
["foobar", {}],
|
||||||
|
|
||||||
|
// Two tokens
|
||||||
|
["on off", {}],
|
||||||
|
["off on", {}],
|
||||||
|
["username tel", {}],
|
||||||
|
["tel username ", {}],
|
||||||
|
[" username tel ", {}],
|
||||||
|
["tel mobile", {}],
|
||||||
|
["tel shipping", {}],
|
||||||
|
["shipping tel", {addressType: "shipping", fieldName: "tel"}],
|
||||||
|
["shipPING tel", {addressType: "shipping", fieldName: "tel"}],
|
||||||
|
["mobile tel", {contactType: "mobile", fieldName: "tel"}],
|
||||||
|
[" MoBiLe TeL ", {contactType: "mobile", fieldName: "tel"}],
|
||||||
|
["XXX tel", {}],
|
||||||
|
["XXX username", {}],
|
||||||
|
|
||||||
|
// Three tokens
|
||||||
|
["billing invalid tel", {}],
|
||||||
|
["___ mobile tel", {}],
|
||||||
|
["mobile foo tel", {}],
|
||||||
|
["mobile tel foo", {}],
|
||||||
|
["tel mobile billing", {}],
|
||||||
|
["billing mobile tel", {addressType: "billing", contactType: "mobile", fieldName: "tel"}],
|
||||||
|
[" BILLing MoBiLE tEl ", {addressType: "billing", contactType: "mobile", fieldName: "tel"}],
|
||||||
|
["billing home tel", {addressType: "billing", contactType: "home", fieldName: "tel"}],
|
||||||
|
|
||||||
|
// Four tokens (invalid)
|
||||||
|
["billing billing mobile tel", {}],
|
||||||
|
|
||||||
|
// Five tokens (invalid)
|
||||||
|
["billing billing billing mobile tel", {}],
|
||||||
|
];
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
const fieldid = "input";
|
||||||
|
var field = document.getElementById(fieldid);
|
||||||
|
for (var test of values) {
|
||||||
|
if (typeof(test[0]) === "undefined")
|
||||||
|
field.removeAttribute("autocomplete");
|
||||||
|
else
|
||||||
|
field.setAttribute("autocomplete", test[0]);
|
||||||
|
|
||||||
|
var info = field.getAutocompleteInfo();
|
||||||
|
|
||||||
|
is(info.section, "section" in test[1] ? test[1].section : "",
|
||||||
|
"Checking autocompleteInfo.section for " + fieldid + ": " + test[0]);
|
||||||
|
is(info.addressType, "addressType" in test[1] ? test[1].addressType : "",
|
||||||
|
"Checking autocompleteInfo.addressType for " + fieldid + ": " + test[0]);
|
||||||
|
is(info.contactType, "contactType" in test[1] ? test[1].contactType : "",
|
||||||
|
"Checking autocompleteInfo.contactType for " + fieldid + ": " + test[0]);
|
||||||
|
is(info.fieldName, "fieldName" in test[1] ? test[1].fieldName : "",
|
||||||
|
"Checking autocompleteInfo.fieldName for " + fieldid + ": " + test[0]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
SpecialPowers.pushPrefEnv({"set": [["dom.forms.autocomplete.experimental", true]]}, start);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
MOCHITEST_MANIFESTS += ['forms/mochitest.ini', 'mochitest.ini']
|
MOCHITEST_MANIFESTS += ['forms/mochitest.ini', 'mochitest.ini']
|
||||||
|
|
||||||
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
|
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini', 'forms/chrome.ini']
|
||||||
|
|
||||||
BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This dictionary is used for the input, textarea and select element's
|
||||||
|
* getAutocompleteInfo method.
|
||||||
|
*/
|
||||||
|
|
||||||
|
dictionary AutocompleteInfo {
|
||||||
|
DOMString section = "";
|
||||||
|
DOMString addressType = "";
|
||||||
|
DOMString contactType = "";
|
||||||
|
DOMString fieldName = "";
|
||||||
|
};
|
|
@ -167,6 +167,9 @@ partial interface HTMLInputElement {
|
||||||
readonly attribute HTMLInputElement? ownerNumberControl;
|
readonly attribute HTMLInputElement? ownerNumberControl;
|
||||||
|
|
||||||
boolean mozIsTextField(boolean aExcludePassword);
|
boolean mozIsTextField(boolean aExcludePassword);
|
||||||
|
|
||||||
|
[ChromeOnly]
|
||||||
|
AutocompleteInfo getAutocompleteInfo();
|
||||||
};
|
};
|
||||||
|
|
||||||
partial interface HTMLInputElement {
|
partial interface HTMLInputElement {
|
||||||
|
|
|
@ -39,6 +39,7 @@ WEBIDL_FILES = [
|
||||||
'AudioStreamTrack.webidl',
|
'AudioStreamTrack.webidl',
|
||||||
'AudioTrack.webidl',
|
'AudioTrack.webidl',
|
||||||
'AudioTrackList.webidl',
|
'AudioTrackList.webidl',
|
||||||
|
'AutocompleteInfo.webidl',
|
||||||
'BarProp.webidl',
|
'BarProp.webidl',
|
||||||
'BatteryManager.webidl',
|
'BatteryManager.webidl',
|
||||||
'BeforeUnloadEvent.webidl',
|
'BeforeUnloadEvent.webidl',
|
||||||
|
|
|
@ -2299,8 +2299,10 @@ public class BrowserApp extends GeckoApp
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openOptionsMenu() {
|
public void openOptionsMenu() {
|
||||||
if (!hasTabsSideBar() && areTabsShown())
|
if (areTabsShown()) {
|
||||||
|
mTabsPanel.showMenu();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Scroll custom menu to the top
|
// Scroll custom menu to the top
|
||||||
if (mMenuPanel != null)
|
if (mMenuPanel != null)
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
android:padding="@dimen/browser_toolbar_button_padding"
|
android:padding="@dimen/browser_toolbar_button_padding"
|
||||||
android:src="@drawable/menu_tabs"
|
android:src="@drawable/menu_tabs"
|
||||||
android:contentDescription="@string/menu"
|
android:contentDescription="@string/menu"
|
||||||
android:background="@drawable/action_bar_button"/>
|
android:background="@drawable/action_bar_button"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
</merge>
|
</merge>
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
android:padding="@dimen/browser_toolbar_button_padding"
|
android:padding="@dimen/browser_toolbar_button_padding"
|
||||||
android:src="@drawable/menu_tabs"
|
android:src="@drawable/menu_tabs"
|
||||||
android:contentDescription="@string/menu"
|
android:contentDescription="@string/menu"
|
||||||
android:background="@drawable/action_bar_button"/>
|
android:background="@drawable/action_bar_button"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
</merge>
|
</merge>
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.mozilla.gecko.Telemetry;
|
||||||
import org.mozilla.gecko.TelemetryContract;
|
import org.mozilla.gecko.TelemetryContract;
|
||||||
import org.mozilla.gecko.animation.PropertyAnimator;
|
import org.mozilla.gecko.animation.PropertyAnimator;
|
||||||
import org.mozilla.gecko.animation.ViewHelper;
|
import org.mozilla.gecko.animation.ViewHelper;
|
||||||
|
import org.mozilla.gecko.util.HardwareUtils;
|
||||||
import org.mozilla.gecko.widget.GeckoPopupMenu;
|
import org.mozilla.gecko.widget.GeckoPopupMenu;
|
||||||
import org.mozilla.gecko.widget.IconTabWidget;
|
import org.mozilla.gecko.widget.IconTabWidget;
|
||||||
|
|
||||||
|
@ -172,20 +173,23 @@ public class TabsPanel extends LinearLayout
|
||||||
mMenuButton.setOnClickListener(new Button.OnClickListener() {
|
mMenuButton.setOnClickListener(new Button.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
final Menu menu = mPopupMenu.getMenu();
|
showMenu();
|
||||||
|
|
||||||
// Each panel has a "+" shortcut button, so don't show it for that panel.
|
|
||||||
menu.findItem(R.id.new_tab).setVisible(mCurrentPanel != Panel.NORMAL_TABS);
|
|
||||||
menu.findItem(R.id.new_private_tab).setVisible(mCurrentPanel != Panel.PRIVATE_TABS);
|
|
||||||
|
|
||||||
// Only show "Clear * tabs" for current panel.
|
|
||||||
menu.findItem(R.id.close_all_tabs).setVisible(mCurrentPanel == Panel.NORMAL_TABS);
|
|
||||||
menu.findItem(R.id.close_private_tabs).setVisible(mCurrentPanel == Panel.PRIVATE_TABS);
|
|
||||||
|
|
||||||
mPopupMenu.show();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mPopupMenu.setAnchor(mMenuButton);
|
}
|
||||||
|
|
||||||
|
public void showMenu() {
|
||||||
|
final Menu menu = mPopupMenu.getMenu();
|
||||||
|
|
||||||
|
// Each panel has a "+" shortcut button, so don't show it for that panel.
|
||||||
|
menu.findItem(R.id.new_tab).setVisible(mCurrentPanel != Panel.NORMAL_TABS);
|
||||||
|
menu.findItem(R.id.new_private_tab).setVisible(mCurrentPanel != Panel.PRIVATE_TABS);
|
||||||
|
|
||||||
|
// Only show "Clear * tabs" for current panel.
|
||||||
|
menu.findItem(R.id.close_all_tabs).setVisible(mCurrentPanel == Panel.NORMAL_TABS);
|
||||||
|
menu.findItem(R.id.close_private_tabs).setVisible(mCurrentPanel == Panel.PRIVATE_TABS);
|
||||||
|
|
||||||
|
mPopupMenu.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTab() {
|
private void addTab() {
|
||||||
|
@ -427,7 +431,7 @@ public class TabsPanel extends LinearLayout
|
||||||
|
|
||||||
mAddTab.setVisibility(View.INVISIBLE);
|
mAddTab.setVisibility(View.INVISIBLE);
|
||||||
|
|
||||||
mMenuButton.setVisibility(View.INVISIBLE);
|
mMenuButton.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
if (mFooter != null)
|
if (mFooter != null)
|
||||||
mFooter.setVisibility(View.VISIBLE);
|
mFooter.setVisibility(View.VISIBLE);
|
||||||
|
@ -435,8 +439,13 @@ public class TabsPanel extends LinearLayout
|
||||||
mAddTab.setVisibility(View.VISIBLE);
|
mAddTab.setVisibility(View.VISIBLE);
|
||||||
mAddTab.setImageLevel(index);
|
mAddTab.setImageLevel(index);
|
||||||
|
|
||||||
mMenuButton.setVisibility(View.VISIBLE);
|
|
||||||
mMenuButton.setEnabled(true);
|
if (!HardwareUtils.hasMenuButton()) {
|
||||||
|
mMenuButton.setVisibility(View.VISIBLE);
|
||||||
|
mPopupMenu.setAnchor(mMenuButton);
|
||||||
|
} else {
|
||||||
|
mPopupMenu.setAnchor(mAddTab);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSideBar()) {
|
if (isSideBar()) {
|
||||||
|
|
|
@ -4157,6 +4157,13 @@ Tab.prototype = {
|
||||||
if (!sameDocument) {
|
if (!sameDocument) {
|
||||||
// XXX This code assumes that this is the earliest hook we have at which
|
// XXX This code assumes that this is the earliest hook we have at which
|
||||||
// browser.contentDocument is changed to the new document we're loading
|
// browser.contentDocument is changed to the new document we're loading
|
||||||
|
|
||||||
|
// We have a new browser and a new window, so the old browserWidth and
|
||||||
|
// browserHeight are no longer valid. We need to force-set the browser
|
||||||
|
// size to ensure it sets the CSS viewport size before the document
|
||||||
|
// has a chance to check it.
|
||||||
|
this.setBrowserSize(kDefaultCSSViewportWidth, kDefaultCSSViewportHeight, true);
|
||||||
|
|
||||||
this.contentDocumentIsDisplayed = false;
|
this.contentDocumentIsDisplayed = false;
|
||||||
this.hasTouchListener = false;
|
this.hasTouchListener = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -4460,9 +4467,11 @@ Tab.prototype = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setBrowserSize: function(aWidth, aHeight) {
|
setBrowserSize: function(aWidth, aHeight, aForce) {
|
||||||
if (fuzzyEquals(this.browserWidth, aWidth) && fuzzyEquals(this.browserHeight, aHeight)) {
|
if (!aForce) {
|
||||||
return;
|
if (fuzzyEquals(this.browserWidth, aWidth) && fuzzyEquals(this.browserHeight, aHeight)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.browserWidth = aWidth;
|
this.browserWidth = aWidth;
|
||||||
|
|
|
@ -1630,6 +1630,10 @@ deniedTranslationOffer
|
||||||
Integer count of the number of times the user opted-out offered
|
Integer count of the number of times the user opted-out offered
|
||||||
page translation, either by the Not Now button or by the notification's
|
page translation, either by the Not Now button or by the notification's
|
||||||
close button in the "offer" state.
|
close button in the "offer" state.
|
||||||
|
autoRejectedTranlationOffer
|
||||||
|
Integer count of the number of times the user is not offered page
|
||||||
|
translation because they had previously clicked "Never translate this
|
||||||
|
language" or "Never translate this site".
|
||||||
showOriginalContent
|
showOriginalContent
|
||||||
Integer count of the number of times the user activated the Show Original
|
Integer count of the number of times the user activated the Show Original
|
||||||
command.
|
command.
|
||||||
|
@ -1672,6 +1676,7 @@ Example
|
||||||
"detectedLanguageChangedAfter": 2,
|
"detectedLanguageChangedAfter": 2,
|
||||||
"targetLanguageChanged": 0,
|
"targetLanguageChanged": 0,
|
||||||
"deniedTranslationOffer": 3,
|
"deniedTranslationOffer": 3,
|
||||||
|
"autoRejectedTranlationOffer": 1,
|
||||||
"showOriginalContent": 2,
|
"showOriginalContent": 2,
|
||||||
"translationOpportunityCountsByLanguage": {
|
"translationOpportunityCountsByLanguage": {
|
||||||
"fr": 100,
|
"fr": 100,
|
||||||
|
|
|
@ -61,7 +61,6 @@ let initTable = [
|
||||||
["io", "@mozilla.org/network/io-service;1", "nsIIOService2"],
|
["io", "@mozilla.org/network/io-service;1", "nsIIOService2"],
|
||||||
["locale", "@mozilla.org/intl/nslocaleservice;1", "nsILocaleService"],
|
["locale", "@mozilla.org/intl/nslocaleservice;1", "nsILocaleService"],
|
||||||
["logins", "@mozilla.org/login-manager;1", "nsILoginManager"],
|
["logins", "@mozilla.org/login-manager;1", "nsILoginManager"],
|
||||||
["netutil", "@mozilla.org/network/util;1", "nsINetUtil"],
|
|
||||||
["obs", "@mozilla.org/observer-service;1", "nsIObserverService"],
|
["obs", "@mozilla.org/observer-service;1", "nsIObserverService"],
|
||||||
["perms", "@mozilla.org/permissionmanager;1", "nsIPermissionManager"],
|
["perms", "@mozilla.org/permissionmanager;1", "nsIPermissionManager"],
|
||||||
["prompt", "@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService"],
|
["prompt", "@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService"],
|
||||||
|
|
Загрузка…
Ссылка в новой задаче