Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-07-31 13:06:48 +02:00
commit 2b3b46e44e
66 changed files with 840 additions and 189 deletions

View File

@ -802,7 +802,8 @@ var WebappsHelper = {
if (!aManifest)
return;
let manifest = new ManifestHelper(aManifest, json.origin);
let manifest = new ManifestHelper(aManifest, json.origin,
json.manifestURL);
let payload = {
timestamp: json.timestamp,
url: manifest.fullLaunchPath(json.startPoint),

View File

@ -167,7 +167,8 @@ let AlertsHelper = {
this._listeners[uid] = listener;
appsService.getManifestFor(listener.manifestURL).then((manifest) => {
let helper = new ManifestHelper(manifest, listener.manifestURL);
let app = appsService.getAppByManifestURL(listener.manifestURL);
let helper = new ManifestHelper(manifest, app.origin, app.manifestURL);
let getNotificationURLFor = function(messages) {
if (!messages) {
return null;
@ -179,8 +180,7 @@ let AlertsHelper = {
return helper.fullLaunchPath();
} else if (typeof message === "object" &&
kNotificationSystemMessageName in message) {
return helper.resolveFromOrigin(
message[kNotificationSystemMessageName]);
return helper.resolveURL(message[kNotificationSystemMessageName]);
}
}
@ -220,7 +220,8 @@ let AlertsHelper = {
// If we have a manifest URL, get the icon and title from the manifest
// to prevent spoofing.
appsService.getManifestFor(manifestURL).then((manifest) => {
let helper = new ManifestHelper(manifest, manifestURL);
let app = appsService.getAppByManifestURL(manifestURL);
let helper = new ManifestHelper(manifest, app.origin, manifestURL);
send(helper.name, helper.iconURLForSize(kNotificationIconSize));
});
},

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b67ddd7d40b52e65199478b8d6631c2c28fdf41d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6548f8a89654727eb3a65200ef14298e106eb099"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b67ddd7d40b52e65199478b8d6631c2c28fdf41d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6548f8a89654727eb3a65200ef14298e106eb099"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="999e945b85c578c503ad445c2285940f16aacdae">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b67ddd7d40b52e65199478b8d6631c2c28fdf41d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6548f8a89654727eb3a65200ef14298e106eb099"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b67ddd7d40b52e65199478b8d6631c2c28fdf41d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6548f8a89654727eb3a65200ef14298e106eb099"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b67ddd7d40b52e65199478b8d6631c2c28fdf41d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6548f8a89654727eb3a65200ef14298e106eb099"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "ed0e789fe38f92979644b18b5fb5e991feab8582",
"revision": "2d20953fd97b5df7e2450ef558b9eeb9962ca1b4",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b67ddd7d40b52e65199478b8d6631c2c28fdf41d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6548f8a89654727eb3a65200ef14298e106eb099"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b67ddd7d40b52e65199478b8d6631c2c28fdf41d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6548f8a89654727eb3a65200ef14298e106eb099"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b67ddd7d40b52e65199478b8d6631c2c28fdf41d"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="6548f8a89654727eb3a65200ef14298e106eb099"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b67ddd7d40b52e65199478b8d6631c2c28fdf41d"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6548f8a89654727eb3a65200ef14298e106eb099"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -821,26 +821,26 @@ pref("plugin.state.f5 sam inspection host plugin", 2);
pref("plugin.state.nprobloxproxy", 2);
#endif
#ifdef XP_MACOSX
pref("plugins.state.nproblox", 2);
pref("plugin.state.nproblox", 2);
#endif
// Box Edit, bug 1029654
#ifdef XP_WIN
pref("plugins.state.npboxedit", 2);
pref("plugin.state.npboxedit", 2);
#endif
#ifdef XP_MACOSX
pref("plugins.state.box edit", 2);
pref("plugin.state.box edit", 2);
#endif
// Nexus Personal, bug 1024965
#ifdef XP_WIN
pref("plugins.state.np_prsnl", 2);
pref("plugin.state.np_prsnl", 2);
#endif
#ifdef XP_MACOSX
pref("plugins.state.personalplugin", 2);
pref("plugin.state.personalplugin", 2);
#endif
#ifdef UNIX_BUT_NOT_MAC
pref("plugins.state.libplugins", 2);
pref("plugin.state.libplugins", 2);
#endif
// display door hanger if flash not installed

View File

@ -464,11 +464,12 @@ SocialShare = {
return;
this.panel.hidden = false;
// create and initialize the panel for this window
let iframe = document.createElement("iframe");
let iframe = document.createElement("browser");
iframe.setAttribute("type", "content");
iframe.setAttribute("class", "social-share-frame");
iframe.setAttribute("context", "contentAreaContextMenu");
iframe.setAttribute("tooltip", "aHTMLTooltip");
iframe.setAttribute("disableglobalhistory", "true");
iframe.setAttribute("flex", "1");
panel.appendChild(iframe);
this.populateProviderMenu();
@ -591,7 +592,15 @@ SocialShare = {
this.anchor.removeAttribute("open");
this.iframe.removeEventListener("click", this._onclick, true);
this.iframe.setAttribute("src", "data:text/plain;charset=utf8,");
// make sure that the frame is unloaded after it is hidden
this.iframe.docShell.createAboutBlankContentViewer(null);
this.currentShare = null;
// share panel use is over, purge any history
if (this.iframe.sessionHistory) {
let purge = this.iframe.sessionHistory.count;
if (purge > 0)
this.iframe.sessionHistory.PurgeHistory(purge);
}
},
setErrorMessage: function() {
@ -702,6 +711,14 @@ SocialShare = {
iframe.contentDocument.documentElement.dispatchEvent(evt);
}, true);
}
// if the user switched between share providers we do not want that history
// available.
if (iframe.sessionHistory) {
let purge = iframe.sessionHistory.count;
if (purge > 0)
iframe.sessionHistory.PurgeHistory(purge);
}
// always ensure that origin belongs to the endpoint
let uri = Services.io.newURI(shareEndpoint, null, null);
iframe.setAttribute("origin", provider.origin);

View File

@ -81,7 +81,7 @@ function runSocialTestWithProvider(manifest, callback, finishcallback) {
SessionStore.setWindowValue(window, "socialSidebar", "");
for (let i = 0; i < manifests.length; i++) {
let m = manifests[i];
for (let what of ['sidebarURL', 'workerURL', 'iconURL']) {
for (let what of ['sidebarURL', 'workerURL', 'iconURL', 'shareURL', 'markURL']) {
if (m[what]) {
yield promiseSocialUrlNotRemembered(m[what]);
}

View File

@ -8,6 +8,7 @@
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "share-data-message", result: shareData});
// share windows self-close
history.back(); // bug 1042991, ensure history is available
window.close();
})
</script>

View File

@ -236,10 +236,17 @@ var gPermissionManager = {
}
this._tree.treeBoxObject.invalidate();
}
// No UI other than this window causes this method to be sent a "deleted"
// notification, so we don't need to implement it since Delete is handled
// directly by the Permission Removal handlers. If that ever changes, those
// implementations will have to move into here.
else if (aData == "deleted") {
for (var i = 0; i < this._permissions.length; i++) {
if (this._permissions[i].host == permission.host) {
this._permissions.splice(i, 1);
this._view._rowCount--;
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
this._tree.treeBoxObject.invalidate();
break;
}
}
}
}
},

View File

@ -114,6 +114,17 @@ function windowLoad(event, win, dialog) {
pm.remove("test.com", "popup");
},
},
{
test: function() {
url.value = "test.com";
btnAllow.doCommand();
pm.remove("test.com", "cookie");
is(tree.view.rowCount, 0, "display should update when cookie permission is deleted");
},
observances: [{ type: "cookie", host: "test.com", data: "added",
capability: allow },
{ type: "cookie", host: "test.com", data: "deleted" }]
},
];
let permObserver = {

View File

@ -66,7 +66,9 @@ this.WebappManager = {
}
} else if (aMessage.name == "Webapps:Install:Return:OK" &&
!data.isPackage) {
let manifest = new ManifestHelper(data.app.manifest, data.app.origin);
let manifest = new ManifestHelper(data.app.manifest,
data.app.origin,
data.app.manifestURL);
if (!manifest.appcache_path) {
this.installations[manifestURL].resolve();
}
@ -172,7 +174,8 @@ this.WebappManager = {
};
let requestingURI = chromeWin.makeURI(aData.from);
let manifest = new ManifestHelper(jsonManifest, aData.app.origin);
let app = aData.app;
let manifest = new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
let host;
try {

View File

@ -294,7 +294,10 @@ let Activities = {
let results = [];
aManifests.forEach((aManifest, i) => {
let manifestURL = aResults.options[i].manifest;
let helper = new ManifestHelper(aManifest.manifest, manifestURL);
// Not passing the origin is fine here since we only need
// helper.name which doesn't rely on url resolution.
let helper =
new ManifestHelper(aManifest.manifest, manifestURL, manifestURL);
results.push({
manifestURL: manifestURL,
iconURL: aResults.options[i].icon,

View File

@ -24,7 +24,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
// Shared code for AppsServiceChild.jsm, Webapps.jsm and Webapps.js
this.EXPORTED_SYMBOLS = ["AppsUtils", "ManifestHelper", "isAbsoluteURI", "mozIApplication"];
this.EXPORTED_SYMBOLS =
["AppsUtils", "ManifestHelper", "isAbsoluteURI", "mozIApplication"];
function debug(s) {
//dump("-*- AppsUtils.jsm: " + s + "\n");
@ -373,7 +374,8 @@ this.AppsUtils = {
// Nor through localized names
if ('locales' in aNewManifest) {
let defaultName = new ManifestHelper(aOldManifest, aApp.origin).name;
let defaultName =
new ManifestHelper(aOldManifest, aApp.origin, aApp.manifestURL).name;
for (let locale in aNewManifest.locales) {
let entry = aNewManifest.locales[locale];
if (!entry.name) {
@ -593,11 +595,25 @@ this.AppsUtils = {
/**
* Helper object to access manifest information with locale support
*/
this.ManifestHelper = function(aManifest, aOrigin) {
this._origin = Services.io.newURI(aOrigin, null, null);
this.ManifestHelper = function(aManifest, aOrigin, aManifestURL) {
// If the app is packaged, we resolve uris against the origin.
// If it's not, against the manifest url.
if (!aOrigin || !aManifestURL) {
throw Error("ManifestHelper needs both origin and manifestURL");
}
this._baseURI = Services.io.newURI(
aOrigin.startsWith("app://") ? aOrigin : aManifestURL, null, null);
// We keep the manifest url in all cases since we need it to
// resolve the package path for packaged apps.
this._manifestURL = Services.io.newURI(aManifestURL, null, null);
this._manifest = aManifest;
let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry)
.QueryInterface(Ci.nsIToolkitChromeRegistry);
let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"]
.getService(Ci.nsIXULChromeRegistry)
.QueryInterface(Ci.nsIToolkitChromeRegistry);
let locale = chrome.getSelectedLocale("global").toLowerCase();
this._localeRoot = this._manifest;
@ -686,7 +702,7 @@ ManifestHelper.prototype = {
iconSizes.sort((a, b) => a - b);
let biggestIconSize = iconSizes.pop();
let biggestIcon = icons[biggestIconSize];
let biggestIconURL = this._origin.resolve(biggestIcon);
let biggestIconURL = this._baseURI.resolve(biggestIcon);
return biggestIconURL;
},
@ -700,7 +716,7 @@ ManifestHelper.prototype = {
for (let size in icons) {
let iSize = parseInt(size);
if (Math.abs(iSize - aSize) < dist) {
icon = this._origin.resolve(icons[size]);
icon = this._baseURI.resolve(icons[size]);
dist = Math.abs(iSize - aSize);
}
}
@ -711,7 +727,7 @@ ManifestHelper.prototype = {
// If no start point is specified, we use the root launch path.
// In all error cases, we just return null.
if ((aStartPoint || "") === "") {
return this._origin.resolve(this._localeProp("launch_path") || "");
return this._baseURI.resolve(this._localeProp("launch_path") || "");
}
// Search for the l10n entry_points property.
@ -721,28 +737,28 @@ ManifestHelper.prototype = {
}
if (entryPoints[aStartPoint]) {
return this._origin.resolve(entryPoints[aStartPoint].launch_path || "");
return this._baseURI.resolve(entryPoints[aStartPoint].launch_path || "");
}
return null;
},
resolveFromOrigin: function(aURI) {
resolveURL: function(aURI) {
// This should be enforced higher up, but check it here just in case.
if (isAbsoluteURI(aURI)) {
throw new Error("Webapps.jsm: non-relative URI passed to resolveFromOrigin");
throw new Error("Webapps.jsm: non-relative URI passed to resolve");
}
return this._origin.resolve(aURI);
return this._baseURI.resolve(aURI);
},
fullAppcachePath: function() {
let appcachePath = this._localeProp("appcache_path");
return this._origin.resolve(appcachePath ? appcachePath : "");
return this._baseURI.resolve(appcachePath ? appcachePath : "");
},
fullPackagePath: function() {
let packagePath = this._localeProp("package_path");
return this._origin.resolve(packagePath ? packagePath : "");
return this._manifestURL.resolve(packagePath ? packagePath : "");
},
get role() {

View File

@ -59,7 +59,8 @@ this.PermissionsInstaller = {
installPermissions: function installPermissions(aApp, aIsReinstall, aOnError,
aIsSystemUpdate) {
try {
let newManifest = new ManifestHelper(aApp.manifest, aApp.origin);
let newManifest =
new ManifestHelper(aApp.manifest, aApp.origin, aApp.manifestURL);
if (!newManifest.permissions && !aIsReinstall) {
return;
}

View File

@ -389,7 +389,8 @@ this.DOMApplicationRegistry = {
updateOfflineCacheForApp: function(aId) {
let app = this.webapps[aId];
this._readManifests([{ id: aId }]).then((aResult) => {
let manifest = new ManifestHelper(aResult[0].manifest, app.origin);
let manifest =
new ManifestHelper(aResult[0].manifest, app.origin, app.manifestURL);
OfflineCacheInstaller.installCache({
cachePath: app.cachePath,
appId: aId,
@ -675,7 +676,7 @@ this.DOMApplicationRegistry = {
return;
}
let manifest = new ManifestHelper(aManifest, aApp.origin);
let manifest = new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
let launchPathURI = Services.io.newURI(manifest.fullLaunchPath(aEntryPoint), null, null);
let manifestURI = Services.io.newURI(aApp.manifestURL, null, null);
root.messages.forEach(function registerPages(aMessage) {
@ -689,7 +690,7 @@ this.DOMApplicationRegistry = {
let fullHandlerPath;
try {
if (handlerPath && handlerPath.trim()) {
fullHandlerPath = manifest.resolveFromOrigin(handlerPath);
fullHandlerPath = manifest.resolveURL(handlerPath);
} else {
throw new Error("Empty or blank handler path.");
}
@ -705,7 +706,7 @@ this.DOMApplicationRegistry = {
if (SystemMessagePermissionsChecker
.isSystemMessagePermittedToRegister(messageName,
aApp.origin,
aApp.manifestURL,
aManifest)) {
msgmgr.registerPage(messageName, handlerPageURI, manifestURI);
}
@ -733,7 +734,7 @@ this.DOMApplicationRegistry = {
return;
}
let manifest = new ManifestHelper(aManifest, aApp.origin);
let manifest = new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
let launchPathURI = Services.io.newURI(manifest.fullLaunchPath(aEntryPoint),
null, null);
let manifestURI = Services.io.newURI(aApp.manifestURL, null, null);
@ -747,7 +748,7 @@ this.DOMApplicationRegistry = {
let handlerPath = connection.handler_path;
if (handlerPath) {
try {
fullHandlerPath = manifest.resolveFromOrigin(handlerPath);
fullHandlerPath = manifest.resolveURL(handlerPath);
} catch(e) {
debug("Connection's handler path is invalid. Skipping: keyword: " +
keyword + " handler_path: " + handlerPath);
@ -760,7 +761,7 @@ this.DOMApplicationRegistry = {
if (SystemMessagePermissionsChecker
.isSystemMessagePermittedToRegister("connection",
aApp.origin,
aApp.manifestURL,
aManifest)) {
msgmgr.registerPage("connection", handlerPageURI, manifestURI);
}
@ -812,7 +813,7 @@ this.DOMApplicationRegistry = {
return activitiesToRegister;
}
let manifest = new ManifestHelper(aManifest, aApp.origin);
let manifest = new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
for (let activity in root.activities) {
let description = root.activities[activity];
let href = description.href;
@ -821,7 +822,7 @@ this.DOMApplicationRegistry = {
}
try {
href = manifest.resolveFromOrigin(href);
href = manifest.resolveURL(href);
} catch (e) {
debug("Activity href (" + href + ") is invalid, skipping. " +
"Error is: " + e);
@ -851,7 +852,7 @@ this.DOMApplicationRegistry = {
if (SystemMessagePermissionsChecker
.isSystemMessagePermittedToRegister("activity",
aApp.origin,
aApp.manifestURL,
aManifest)) {
msgmgr.registerPage("activity", launchPathURI, manifestURI);
}
@ -961,7 +962,8 @@ this.DOMApplicationRegistry = {
return;
}
let localeManifest = new ManifestHelper(manifest, app.origin);
let localeManifest =
new ManifestHelper(manifest, app.origin, app.manifestURL);
app.name = manifest.name;
app.csp = manifest.csp || "";
@ -1463,7 +1465,8 @@ this.DOMApplicationRegistry = {
let results = yield this._readManifests([{ id: id }]);
let jsonManifest = results[0].manifest;
let manifest = new ManifestHelper(jsonManifest, app.origin);
let manifest =
new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
if (manifest.appcache_path) {
debug("appcache found");
@ -1497,7 +1500,7 @@ this.DOMApplicationRegistry = {
throw new Error("MISSING_UPDATE_MANIFEST");
}
let manifest = new ManifestHelper(json, app.manifestURL);
let manifest = new ManifestHelper(json, app.origin, app.manifestURL);
let newApp = {
manifestURL: aManifestURL,
origin: app.origin,
@ -1808,7 +1811,8 @@ this.DOMApplicationRegistry = {
}
}
};
let helper = new ManifestHelper(manifest, aData.manifestURL);
let helper =
new ManifestHelper(manifest, aData.origin, aData.manifestURL);
debug("onlyCheckAppCache - launch updateSvc.checkForUpdate for " +
helper.fullAppcachePath());
updateSvc.checkForUpdate(Services.io.newURI(helper.fullAppcachePath(), null, null),
@ -1990,7 +1994,8 @@ this.DOMApplicationRegistry = {
let manFile = OS.Path.join(dir, "staged-update.webapp");
yield this._writeFile(manFile, JSON.stringify(aNewManifest));
let manifest = new ManifestHelper(aNewManifest, aApp.manifestURL);
let manifest =
new ManifestHelper(aNewManifest, aApp.origin, aApp.manifestURL);
// A package is available: set downloadAvailable to fire the matching
// event.
aApp.downloadAvailable = true;
@ -2036,7 +2041,8 @@ this.DOMApplicationRegistry = {
let manFile = OS.Path.join(dir, "manifest.webapp");
yield this._writeFile(manFile, JSON.stringify(aNewManifest));
manifest = new ManifestHelper(aNewManifest, aApp.origin);
manifest =
new ManifestHelper(aNewManifest, aApp.origin, aApp.manifestURL);
if (supportUseCurrentProfile()) {
// Update the permissions for this app.
@ -2055,7 +2061,8 @@ this.DOMApplicationRegistry = {
aApp.role = manifest.role || "";
aApp.updateTime = Date.now();
} else {
manifest = new ManifestHelper(aOldManifest, aApp.origin);
manifest =
new ManifestHelper(aOldManifest, aApp.origin, aApp.manifestURL);
}
// Update the registry.
@ -2564,7 +2571,8 @@ this.DOMApplicationRegistry = {
yield this._writeManifestFile(id, aData.isPackage, jsonManifest);
debug("app.origin: " + app.origin);
let manifest = new ManifestHelper(jsonManifest, app.origin);
let manifest =
new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
let appObject = this._cloneApp(aData, app, manifest, jsonManifest, id, localId);
@ -2618,7 +2626,7 @@ this.DOMApplicationRegistry = {
// origin for install apps is meaningless here, since it's app:// and this
// can't be used to resolve package paths.
manifest = new ManifestHelper(jsonManifest, app.manifestURL);
manifest = new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
this.queuedPackageDownload[app.manifestURL] = {
manifest: manifest,

View File

@ -170,18 +170,20 @@ this.SystemMessagePermissionsChecker = {
* app at start-up based on the permissions claimed in the app's manifest.
* @param string aSysMsgName
* The system messsage name.
* @param string aOrigin
* The app's origin.
* @param string aManifestURL
* The app's manifest URL.
* @param object aManifest
* The app's manifest.
* @returns bool
* Is permitted or not.
**/
isSystemMessagePermittedToRegister:
function isSystemMessagePermittedToRegister(aSysMsgName, aOrigin, aManifest) {
function isSystemMessagePermittedToRegister(aSysMsgName,
aManifestURL,
aManifest) {
debug("isSystemMessagePermittedToRegister(): " +
"aSysMsgName: " + aSysMsgName + ", " +
"aOrigin: " + aOrigin + ", " +
"aManifestURL: " + aManifestURL + ", " +
"aManifest: " + JSON.stringify(aManifest));
let permNames = this.getSystemMessagePermissions(aSysMsgName);
@ -207,20 +209,22 @@ this.SystemMessagePermissionsChecker = {
break;
}
let newManifest = new ManifestHelper(aManifest, aOrigin);
// It's ok here to not pass the origin to ManifestHelper since we only
// need the permission property and that doesn't depend on uri resolution.
let newManifest = new ManifestHelper(aManifest, aManifestURL, aManifestURL);
for (let permName in permNames) {
// The app doesn't claim valid permissions for this sytem message.
if (!newManifest.permissions || !newManifest.permissions[permName]) {
debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
"Please add the permission for app: '" + aOrigin + "'.");
"Please add the permission for app: '" + aManifestURL + "'.");
return false;
}
let permValue = PermissionsTable[permName][appStatus];
if (permValue != Ci.nsIPermissionManager.PROMPT_ACTION &&
permValue != Ci.nsIPermissionManager.ALLOW_ACTION) {
debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
"Please add the permission for app: '" + aOrigin + "'.");
"Please add the permission for app: '" + aManifestURL + "'.");
return false;
}

View File

@ -14,6 +14,7 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.menu.MenuItemActionBar;
import org.mozilla.gecko.menu.MenuItemDefault;
import org.mozilla.gecko.tests.UITestContext;
import org.mozilla.gecko.tests.helpers.DeviceHelper;
import org.mozilla.gecko.tests.helpers.WaitHelper;
import org.mozilla.gecko.util.HardwareUtils;
@ -27,9 +28,12 @@ import com.jayway.android.robotium.solo.Solo;
* A class representing any interactions that take place on the app menu.
*/
public class AppMenuComponent extends BaseComponent {
private Boolean hasLegacyMenu = null;
public enum MenuItem {
FORWARD(R.string.forward),
NEW_TAB(R.string.new_tab),
PAGE(R.string.page),
RELOAD(R.string.reload);
private final int resourceID;
@ -48,6 +52,27 @@ public class AppMenuComponent extends BaseComponent {
}
};
public enum PageMenuItem {
SAVE_AS_PDF(R.string.save_as_pdf);
private static final MenuItem PARENT_MENU = MenuItem.PAGE;
private final int resourceID;
private String stringResource;
PageMenuItem(final int resourceID) {
this.resourceID = resourceID;
}
public String getString(final Solo solo) {
if (stringResource == null) {
stringResource = solo.getString(resourceID);
}
return stringResource;
}
};
public AppMenuComponent(final UITestContext testContext) {
super(testContext);
}
@ -56,6 +81,54 @@ public class AppMenuComponent extends BaseComponent {
fAssertFalse("Menu is not open", isMenuOpen());
}
/**
* Legacy Android devices doesn't have hierarchical menus. Sub-menus, such as "Page", are missing in these devices.
* Try to determine if the menu item "Page" is present.
*
* TODO : This fragile way to determine legacy menus should be replaced with a check for 6-panel menu item.
*
* @return true if there is a legacy menu.
*/
private boolean hasLegacyMenu() {
if (hasLegacyMenu == null) {
hasLegacyMenu = findAppMenuItemView(MenuItem.PAGE.getString(mSolo)) == null;
}
return hasLegacyMenu;
}
public void assertMenuItemIsDisabledAndVisible(PageMenuItem pageMenuItem) {
openAppMenu();
if (!hasLegacyMenu()) {
// Non-legacy devices have hierarchical menu, check for parent menu item "page".
final View parentMenuItemView = findAppMenuItemView(MenuItem.PAGE.getString(mSolo));
if (parentMenuItemView.isEnabled()) {
fAssertTrue("The parent 'page' menu item is enabled", parentMenuItemView.isEnabled());
fAssertEquals("The parent 'page' menu item is visible", View.VISIBLE,
parentMenuItemView.getVisibility());
// Parent menu "page" is enabled, open page menu and check for menu item represented by pageMenuItem.
pressMenuItem(MenuItem.PAGE.getString(mSolo));
final View pageMenuItemView = findAppMenuItemView(pageMenuItem.getString(mSolo));
fAssertFalse("The page menu item is not enabled", pageMenuItemView.isEnabled());
fAssertEquals("The page menu item is visible", View.VISIBLE, pageMenuItemView.getVisibility());
} else {
fAssertFalse("The parent 'page' menu item is not enabled", parentMenuItemView.isEnabled());
fAssertEquals("The parent 'page' menu item is visible", View.VISIBLE, parentMenuItemView.getVisibility());
}
} else {
// Legacy devices don't have parent menu item "page", check for menu item represented by pageMenuItem.
final View pageMenuItemView = findAppMenuItemView(pageMenuItem.getString(mSolo));
fAssertFalse("The page menu item is not enabled", pageMenuItemView.isEnabled());
fAssertEquals("The page menu item is visible", View.VISIBLE, pageMenuItemView.getVisibility());
}
// Close the App Menu.
mSolo.goBack();
}
private View getOverflowMenuButtonView() {
return mSolo.getView(R.id.menu);
}
@ -87,32 +160,61 @@ public class AppMenuComponent extends BaseComponent {
return null;
}
public void pressMenuItem(MenuItem menuItem) {
openAppMenu();
/**
* Helper function to let Robotium locate and click menu item from legacy Android menu (devices with Android 2.x).
*
* Robotium will also try to open the menu if there are no open dialog.
*
* @param menuItemText, The title of menu item to open.
*/
private void pressLegacyMenuItem(final String menuItemTitle) {
mSolo.clickOnMenuItem(menuItemTitle, true);
}
final String text = menuItem.getString(mSolo);
final View menuItemView = findAppMenuItemView(text);
private void pressMenuItem(final String menuItemTitle) {
fAssertTrue("Menu is open", isMenuOpen(menuItemTitle));
if (menuItemView != null) {
fAssertTrue("The menu item is enabled", menuItemView.isEnabled());
fAssertEquals("The menu item is visible", View.VISIBLE, menuItemView.getVisibility());
if (!hasLegacyMenu()) {
final View menuItemView = findAppMenuItemView(menuItemTitle);
fAssertTrue(String.format("The menu item %s is enabled", menuItemTitle), menuItemView.isEnabled());
fAssertEquals(String.format("The menu item %s is visible", menuItemTitle), View.VISIBLE,
menuItemView.getVisibility());
mSolo.clickOnView(menuItemView);
} else {
// We could not find a view representing this menu item: Let's let Robotium try to
// locate and click it in the legacy Android menu (devices with Android 2.x).
//
// Even though we already opened the menu to see if we can locate the menu item,
// Robotium will also try to open the menu if it doesn't find an open dialog (Does
// not happen in this case).
mSolo.clickOnMenuItem(text, true);
pressLegacyMenuItem(menuItemTitle);
}
}
private void pressSubMenuItem(final String parentMenuItemTitle, final String childMenuItemTitle) {
openAppMenu();
if (!hasLegacyMenu()) {
pressMenuItem(parentMenuItemTitle);
// Child menu item is not pressed yet, Click on it.
pressMenuItem(childMenuItemTitle);
} else {
pressLegacyMenuItem(childMenuItemTitle);
}
}
public void pressMenuItem(MenuItem menuItem) {
openAppMenu();
pressMenuItem(menuItem.getString(mSolo));
}
public void pressMenuItem(final PageMenuItem pageMenuItem) {
pressSubMenuItem(PageMenuItem.PARENT_MENU.getString(mSolo), pageMenuItem.getString(mSolo));
}
private void openAppMenu() {
assertMenuIsNotOpen();
if (HardwareUtils.hasMenuButton()) {
// This is a hack needed for tablets where the OverflowMenuButton is always in the GONE state,
// so we press the menu key instead.
if (HardwareUtils.hasMenuButton() || DeviceHelper.isTablet()) {
mSolo.sendKey(Solo.MENU);
} else {
pressOverflowMenuButton();
@ -130,10 +232,24 @@ public class AppMenuComponent extends BaseComponent {
mSolo.clickOnView(overflowMenuButton, true);
}
/**
* Determines whether the app menu is open by searching for the text "New tab".
*
* @return true if app menu is open.
*/
private boolean isMenuOpen() {
// The presence of the "New tab" menu item is our best guess about whether
// the menu is open or not.
return mSolo.searchText(MenuItem.NEW_TAB.getString(mSolo));
return isMenuOpen(MenuItem.NEW_TAB.getString(mSolo));
}
/**
* Determines whether the app menu is open by searching for the text in menuItemTitle.
*
* @param menuItemTitle, The contentDescription of menu item to search.
*
* @return true if app menu is open.
*/
private boolean isMenuOpen(String menuItemTitle) {
return mSolo.searchText(menuItemTitle);
}
private void waitForMenuOpen() {

View File

@ -122,6 +122,7 @@ skip-if = android_version == "10"
[testAboutHomeVisibility]
# disabled on Android 2.3; bug 946656
skip-if = android_version == "10"
[testAppMenuPathways]
[testEventDispatcher]
[testInputConnection]
# disabled on Android 2.3; bug 1025968

View File

@ -0,0 +1,34 @@
package org.mozilla.gecko.tests;
import org.mozilla.gecko.tests.components.AppMenuComponent;
import org.mozilla.gecko.tests.helpers.GeckoHelper;
import org.mozilla.gecko.tests.helpers.NavigationHelper;
/**
* Set of tests to test UI App menu and submenus the user interact with.
*/
public class testAppMenuPathways extends UITest {
/**
* Robocop supports only a single test function per test class. Therefore, we
* have a single top-level test function that dispatches to sub-tests.
*/
public void testAppMenuPathways() {
GeckoHelper.blockForReady();
_testSaveAsPDFPathway();
}
public void _testSaveAsPDFPathway() {
// Page menu should be disabled in about:home.
mAppMenu.assertMenuItemIsDisabledAndVisible(AppMenuComponent.PageMenuItem.SAVE_AS_PDF);
// Navigate to a page to test save as pdf functionality.
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
// Test save as pdf functionality.
// The following call doesn't wait for the resulting pdf but checks that no exception are thrown.
mAppMenu.pressMenuItem(AppMenuComponent.PageMenuItem.SAVE_AS_PDF);
}
}

View File

@ -83,7 +83,7 @@ let WebappRT = {
let apps = request.result;
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
let manifest = new ManifestHelper(app.manifest, app.origin);
let manifest = new ManifestHelper(app.manifest, app.origin, app.manifestURL);
// if this is a path to the manifest, or the launch path, then we have a hit.
if (app.manifestURL == aUrl || manifest.fullLaunchPath() == aUrl) {

View File

@ -105,7 +105,7 @@ function updateList() {
function addApplication(aApp) {
let list = document.getElementById("appgrid");
let manifest = new ManifestHelper(aApp.manifest, aApp.origin);
let manifest = new ManifestHelper(aApp.manifest, aApp.origin, aApp.manifestURL);
let container = document.createElement("div");
container.className = "app list-item";

View File

@ -179,12 +179,13 @@ this.WebappManager = {
DOMApplicationRegistry.registryReady.then(() => {
DOMApplicationRegistry.confirmInstall(aData, file, (function(aApp, aManifest) {
this._postInstall(aData.profilePath, aManifest, aData.app.origin, aData.app.apkPackageName);
this._postInstall(aData.profilePath, aManifest, aData.app.origin,
aData.app.apkPackageName, aData.app.manifestURL);
}).bind(this));
});
},
_postInstall: function(aProfilePath, aNewManifest, aOrigin, aApkPackageName) {
_postInstall: function(aProfilePath, aNewManifest, aOrigin, aApkPackageName, aManifestURL) {
// aOrigin may now point to the app: url that hosts this app.
sendMessageToJava({
type: "Webapps:Postinstall",
@ -194,7 +195,7 @@ this.WebappManager = {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(aProfilePath);
let localeManifest = new ManifestHelper(aNewManifest, aOrigin);
let localeManifest = new ManifestHelper(aNewManifest, aOrigin, aManifestUrl);
this.writeDefaultPrefs(file, localeManifest);
},
@ -333,7 +334,7 @@ this.WebappManager = {
yield this._autoUpdatePackagedApp(aData, aOldApp);
}
this._postInstall(aData.profilePath, aData.manifest, aOldApp.origin, aOldApp.apkPackageName);
this._postInstall(aData.profilePath, aData.manifest, aOldApp.origin, aOldApp.apkPackageName, aOldApp.manifestURL);
}).bind(this)); },
_autoUpdatePackagedApp: Task.async(function*(aData, aOldApp) {

View File

@ -25,4 +25,6 @@ public class Constants {
public static final String INTENT_START_SEARCH = "org.mozilla.search.intent.START_SEARCH";
public static final String INTENT_START_SEARCH_QUERY_EXTRA = "org.mozilla.search.intent.START_SEARCH_QUERY_EXTRA";
public static final int SUGGESTION_MAX = 5;
}

View File

@ -8,6 +8,7 @@ import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
@ -21,6 +22,7 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserContract.SearchHistory;
import org.mozilla.search.AcceptsSearchQuery.SuggestionAnimation;
@ -34,7 +36,12 @@ public class PreSearchFragment extends Fragment {
private ListView listView;
private final String[] PROJECTION = new String[]{SearchHistory.QUERY, SearchHistory._ID};
private static final String[] PROJECTION = new String[]{ SearchHistory.QUERY, SearchHistory._ID };
// Limit search history query results to 5 items. This value matches the number of search
// suggestions we return in SearchFragment.
private static final Uri SEARCH_HISTORY_URI = SearchHistory.CONTENT_URI.buildUpon().
appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(Constants.SUGGESTION_MAX)).build();
private static final int LOADER_ID_SEARCH_HISTORY = 1;
@ -128,8 +135,8 @@ public class PreSearchFragment extends Fragment {
private class SearchHistoryLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), SearchHistory.CONTENT_URI,
PROJECTION, null, null, SearchHistory.DATE_LAST_VISITED + " DESC");
return new CursorLoader(getActivity(), SEARCH_HISTORY_URI, PROJECTION, null, null,
SearchHistory.DATE_LAST_VISITED + " DESC");
}
@Override

View File

@ -0,0 +1,124 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* 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.search;
import org.mozilla.gecko.AppConstants;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.widget.RemoteViews;
import android.util.Log;
/* Provides a really simple widget with two buttons, one to launch Fennec
* and one to launch the search activity. All intents are actually sent back
* here and then forwarded on to start the real activity. */
public class SearchWidget extends AppWidgetProvider {
final private static String LOGTAG = "GeckoSearchWidget";
final public static String ACTION_LAUNCH_BROWSER = "org.mozilla.widget.LAUNCH_BROWSER";
final public static String ACTION_LAUNCH_SEARCH = "org.mozilla.widget.LAUNCH_SEARCH";
@SuppressLint("NewApi")
@Override
public void onUpdate(final Context context, final AppWidgetManager manager, final int[] ids) {
for (int id : ids) {
final Bundle bundle;
if (AppConstants.Versions.feature16Plus) {
bundle = manager.getAppWidgetOptions(id);
} else {
bundle = null;
}
addView(manager, context, id, bundle);
}
super.onUpdate(context, manager, ids);
}
@SuppressLint("NewApi")
@Override
public void onAppWidgetOptionsChanged(final Context context,
final AppWidgetManager manager,
final int id,
final Bundle options) {
addView(manager, context, id, options);
if (AppConstants.Versions.feature16Plus) {
super.onAppWidgetOptionsChanged(context, manager, id, options);
}
}
@Override
public void onReceive(final Context context, final Intent intent) {
// This will hold the intent to redispatch
final Intent redirect;
Log.i(LOGTAG, "Got intent " + intent.getAction());
if (intent.getAction().equals(ACTION_LAUNCH_BROWSER)) {
redirect = buildRedirectIntent(Intent.ACTION_VIEW,
AppConstants.ANDROID_PACKAGE_NAME,
AppConstants.BROWSER_INTENT_CLASS_NAME,
intent);
} else if (intent.getAction().equals(ACTION_LAUNCH_SEARCH)) {
redirect = buildRedirectIntent(Intent.ACTION_VIEW,
AppConstants.SEARCH_PACKAGE_NAME,
AppConstants.SEARCH_INTENT_CLASS_NAME,
intent);
} else {
redirect = null;
}
if (redirect != null) {
try {
context.startActivity(redirect);
} catch(Exception ex) {
// When this is built stand alone, its hardcoded to try and launch nightly.
// If that fails, just fire a generic VIEW intent.
Intent redirect2 = buildRedirectIntent(Intent.ACTION_VIEW, null, null, intent);
context.startActivity(redirect2);
}
}
super.onReceive(context, intent);
}
// Utility to create the view for this widget and attach any event listeners to it
private void addView(final AppWidgetManager manager, final Context context, final int id, final Bundle options) {
final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.search_widget);
addClickIntent(context, views, R.id.search_button, ACTION_LAUNCH_SEARCH);
addClickIntent(context, views, R.id.new_tab_button, ACTION_LAUNCH_BROWSER);
// Clicking the logo also launches the browser
addClickIntent(context, views, R.id.logo_button, ACTION_LAUNCH_BROWSER);
manager.updateAppWidget(id, views);
}
// Utility for adding a pending intent to be fired when a View is clicked.
private void addClickIntent(final Context context, final RemoteViews views, final int viewId, final String action) {
final Intent intent = new Intent(context, SearchWidget.class);
intent.setAction(action);
intent.setData(Uri.parse("about:home"));
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
views.setOnClickPendingIntent(viewId, pendingIntent);
}
// Utility for building an intent to be redispatched (i.e. to launch the browser or the search intent).
private Intent buildRedirectIntent(final String action, final String pkg, final String className, final Intent source) {
final Intent activity = new Intent(action);
if (pkg != null && className != null) {
activity.setClassName(pkg, className);
}
activity.setData(source.getData());
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return activity;
}
}

View File

@ -22,6 +22,7 @@ import android.widget.ListView;
import org.mozilla.search.AcceptsSearchQuery;
import org.mozilla.search.AcceptsSearchQuery.SuggestionAnimation;
import org.mozilla.search.Constants;
import org.mozilla.search.R;
import java.util.ArrayList;
@ -41,9 +42,6 @@ public class SearchFragment extends Fragment implements AcceptsJumpTaps {
// Timeout for the suggestion client to respond
private static final int SUGGESTION_TIMEOUT = 3000;
// Maximum number of results returned by the suggestion client
private static final int SUGGESTION_MAX = 5;
// Color of search term match in search suggestion
private static final int SUGGESTION_HIGHLIGHT_COLOR = 0xFF999999;
@ -80,9 +78,9 @@ public class SearchFragment extends Fragment implements AcceptsJumpTaps {
// TODO: Don't hard-code this template string (bug 1039758)
final String template = "https://search.yahoo.com/sugg/ff?" +
"output=fxjson&appid=ffm&command=__searchTerms__&nresults=" + SUGGESTION_MAX;
"output=fxjson&appid=ffm&command=__searchTerms__&nresults=" + Constants.SUGGESTION_MAX;
suggestClient = new SuggestClient(activity, template, SUGGESTION_TIMEOUT, SUGGESTION_MAX);
suggestClient = new SuggestClient(activity, template, SUGGESTION_TIMEOUT, Constants.SUGGESTION_MAX);
suggestionLoaderCallbacks = new SuggestionLoaderCallbacks();
autoCompleteAdapter = new AutoCompleteAdapter(activity, this);

View File

@ -13,8 +13,28 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<!-- Basic launcher widget. -->
<receiver android:name="org.mozilla.search.SearchWidget"
android:label="@string/search_widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="org.mozilla.widget.LAUNCH_BROWSER"/>
</intent-filter>
<intent-filter>
<action android:name="org.mozilla.widget.LAUNCH_SEARCH"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/search_widget_info" />
</receiver>
<activity
android:name="org.mozilla.search.SearchPreferenceActivity"
android:label="@string/search_pref_title"

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -2,24 +2,9 @@
- 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/. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<padding
android:bottom="@dimen/card_background_padding_y"
android:top="@dimen/card_background_padding_y"
android:left="@dimen/card_background_padding_x"
android:right="@dimen/card_background_padding_x"/>
<solid android:color="@android:color/transparent"/>
</shape>
</item>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Background -->
<item>
<shape>
<solid android:color="@color/card_background"/>
<corners android:radius="5dp"/>
<stroke android:width="1dp" android:color="@color/card_border" />
</shape>
</item>
</layer-list>
<item android:state_pressed="true" android:drawable="@drawable/search_card_pressed" />
<item android:drawable="@drawable/search_card_default"/>
</selector>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<!-- Drawable used for buttons in the launch widget -->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true">
<item android:state_pressed="true"
android:drawable="@drawable/widget_active_bg"/>
<item android:drawable="@drawable/widget_bg"/>
</selector>

View File

@ -26,6 +26,7 @@
android:background="@color/global_background_color"
android:divider="@null"
android:dividerHeight="0dp"
android:listSelector="@android:color/transparent"
android:visibility="gone"/>
</LinearLayout>

View File

@ -12,7 +12,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null"
android:dividerHeight="0dp"/>
android:dividerHeight="0dp"
android:listSelector="@android:color/transparent"/>
<ImageButton
android:id="@+id/settings_button"

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<!-- A homescreen widget for launching Fennec or the search activity. We can't use styles in here
so make sure any changes you make are also made to launch_widget.xml which doesn't have
the search widget button. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/widget_header_height">
<!-- Wrap in a linear layout to center the text in a flexible space. We use a negative margin
to extend this into the firefox logo so that the button background appears to come from behind the logo, but
highlights correctly when tapped. -->
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@+id/logo_button"
android:layout_marginRight="@dimen/widget_button_offset"
android:paddingRight="@dimen/widget_button_padding"
android:gravity="center"
android:background="@drawable/search_widget_button"
android:orientation="horizontal"
android:id="@+id/search_button">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_url_bar_search"
android:drawablePadding="@dimen/widget_drawable_padding"
android:text="@string/search_widget_button_label"
android:contentDescription="@string/search_widget_button_label"
android:gravity="center"
android:textSize="@dimen/widget_text_size"
android:textColor="@color/text_color_primary"
android:id="@+id/search_button_label"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_toRightOf="@+id/logo_button"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/widget_button_offset"
android:paddingLeft="@dimen/widget_button_padding"
android:layout_centerVertical="true"
android:gravity="center"
android:background="@drawable/search_widget_button"
android:orientation="horizontal"
android:id="@+id/new_tab_button">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_widget_new_tab"
android:drawablePadding="@dimen/widget_drawable_padding"
android:gravity="center"
android:text="@string/new_tab"
android:contentDescription="@string/new_tab"
android:textSize="@dimen/widget_text_size"
android:textColor="@color/text_color_primary"
android:id="@+id/new_tab_button_label"/>
</LinearLayout>
<!-- The logo. adjustViewBounds is required for the buttons above to stretch underneath the logo. -->
<ImageView android:id="@+id/logo_button"
android:layout_centerInParent="true"
android:adjustViewBounds="true"
android:layout_width="@dimen/widget_header_height"
android:layout_height="match_parent"
android:src="@drawable/icon"/>
</RelativeLayout>

View File

@ -10,6 +10,7 @@
<!-- card colors -->
<color name="card_background">#ffffff</color>
<color name="card_background_pressed">#DCDCE1</color>
<color name="card_border">#BFBFBF</color>
<!-- Search suggestion highlight color is defined in SearchFragment.java -->

View File

@ -23,4 +23,11 @@
<dimen name="card_padding_x">38dp</dimen>
<dimen name="card_padding_y">23dp</dimen>
<!-- Widget Buttons -->
<dimen name="widget_header_height">70dp</dimen>
<dimen name="widget_button_offset">-50dp</dimen>
<dimen name="widget_button_padding">45dp</dimen>
<dimen name="widget_text_size">15sp</dimen>
<dimen name="widget_drawable_padding">2dp</dimen>
</resources>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="300dp"
android:minHeight="40dp"
android:label="@string/search_widget_name"
android:widgetCategory="home_screen"
android:previewImage="@drawable/launcher_widget"
android:initialLayout="@layout/search_widget"/>

View File

@ -16,4 +16,5 @@ search_activity_sources = [
'java/org/mozilla/search/PostSearchFragment.java',
'java/org/mozilla/search/PreSearchFragment.java',
'java/org/mozilla/search/SearchPreferenceActivity.java',
'java/org/mozilla/search/SearchWidget.java',
]

View File

@ -4,7 +4,7 @@
<!ENTITY search_jump_arrow '&#8598;'>
<!ENTITY search_app_name 'Firefox Search'>
<!ENTITY search_app_name '&brandShortName; Search'>
<!ENTITY search_for_something 'Search for something'>
<!ENTITY search_pref_title 'Settings'>
@ -12,3 +12,6 @@
<!ENTITY search_pref_clear_history_dialog_message 'Delete all search history from this device?'>
<!ENTITY search_pref_clear_history_title 'Clear search history'>
<!ENTITY search_pref_button_content_description 'Settings'>
<!ENTITY search_widget_button_label 'Search'>

View File

@ -8,3 +8,6 @@
<string name="search_pref_clear_history_dialog_message">&search_pref_clear_history_dialog_message;</string>
<string name="search_pref_clear_history_title">&search_pref_clear_history_title;</string>
<string name="search_pref_button_content_description">&search_pref_button_content_description;</string>
<string name="search_widget_name">&search_app_name;</string>
<string name="search_widget_button_label">&search_widget_button_label;</string>

View File

@ -4,9 +4,37 @@
"use strict";
const {Cc, Ci, Cu} = require("chrome");
const { Cc, Ci, Cu } = require("chrome");
let protocol = require("devtools/server/protocol");
let {method, RetVal} = protocol;
let { method, RetVal } = protocol;
const { reportException } = require("devtools/toolkit/DevToolsUtils");
/**
* A method decorator that ensures the actor is in the expected state before
* proceeding. If the actor is not in the expected state, the decorated method
* returns a rejected promise.
*
* @param String expectedState
* The expected state.
*
* @param Function method
* The actor method to proceed with when the actor is in the expected
* state.
*
* @returns Function
* The decorated method.
*/
function expectState(expectedState, method) {
return function(...args) {
if (this.state !== expectedState) {
const msg = "Wrong State: Expected '" + expectedState + "', but current "
+ "state is '" + this.state + "'";
return Promise.reject(new Error(msg));
}
return method.apply(this, args);
};
}
/**
* An actor that returns memory usage data for its parent actor's window.
@ -17,18 +45,59 @@ let {method, RetVal} = protocol;
let MemoryActor = protocol.ActorClass({
typeName: "memory",
initialize: function(conn, tabActor) {
get dbg() {
if (!this._dbg) {
this._dbg = this.parent.makeDebugger();
}
return this._dbg;
},
initialize: function(conn, parent) {
protocol.Actor.prototype.initialize.call(this, conn);
this.tabActor = tabActor;
this.parent = parent;
this._mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
.getService(Ci.nsIMemoryReporterManager);
this.state = "detached";
this._dbg = null;
},
destroy: function() {
this._mgr = null;
if (this.state === "attached") {
this.detach();
}
protocol.Actor.prototype.destroy.call(this);
},
/**
* Attach to this MemoryActor.
*/
attach: method(expectState("detached", function() {
this.dbg.addDebuggees();
this.dbg.enabled = true;
this.state = "attached";
}), {
request: {},
response: {
type: "attached"
}
}),
/**
* Detach from this MemoryActor.
*/
detach: method(expectState("attached", function() {
this.dbg.removeAllDebuggees();
this.dbg.enabled = false;
this._dbg = null;
this.state = "detached";
}), {
request: {},
response: {
type: "detached"
}
}),
/**
* A method that returns a detailed breakdown of the memory consumption of the
* associated window.
@ -49,7 +118,7 @@ let MemoryActor = protocol.ActorClass({
let nonJSMilliseconds = {};
try {
this._mgr.sizeOfTab(this.tabActor.window, jsObjectsSize, jsStringsSize, jsOtherSize,
this._mgr.sizeOfTab(this.parent.window, jsObjectsSize, jsStringsSize, jsOtherSize,
domSize, styleSize, otherSize, totalSize, jsMilliseconds, nonJSMilliseconds);
result.total = totalSize.value;
result.domSize = domSize.value;
@ -61,9 +130,7 @@ let MemoryActor = protocol.ActorClass({
result.jsMilliseconds = jsMilliseconds.value.toFixed(1);
result.nonJSMilliseconds = nonJSMilliseconds.value.toFixed(1);
} catch (e) {
console.error(e);
let url = this.tabActor.url;
console.error("Error getting size of " + url);
reportException("MemoryActor.prototype.measure", e);
}
return result;

View File

@ -283,7 +283,8 @@ WebappsActor.prototype = {
// We can't have appcache for packaged apps.
if (!aApp.origin.startsWith("app://")) {
reg.startOfflineCacheDownload(new ManifestHelper(manifest, aApp.origin));
reg.startOfflineCacheDownload(
new ManifestHelper(manifest, aApp.origin, aApp.manifestURL));
}
});
// Cleanup by removing the temporary directory.
@ -747,7 +748,7 @@ WebappsActor.prototype = {
let deferred = promise.defer();
this._findManifestByURL(manifestURL).then(jsonManifest => {
let manifest = new ManifestHelper(jsonManifest, app.origin);
let manifest = new ManifestHelper(jsonManifest, app.origin, manifestURL);
let iconURL = manifest.iconURLForSize(aRequest.size || 128);
if (!iconURL) {
deferred.resolve({

View File

@ -7,6 +7,7 @@ support-files =
nonchrome_unsafeDereference.html
inspector_getImageData.html
large-image.jpg
memory-helpers.js
small-image.gif
Debugger.Source.prototype.element.js
Debugger.Source.prototype.element-2.js
@ -66,6 +67,8 @@ skip-if = buildapp == 'mulet'
[test_inspector_getImageData.html]
skip-if = buildapp == 'mulet'
[test_memory.html]
[test_memory_attach_01.html]
[test_memory_attach_02.html]
[test_preference.html]
[test_connectToChild.html]
skip-if = buildapp == 'mulet'

View File

@ -0,0 +1,54 @@
let Cu = Components.utils;
let Cc = Components.classes;
let Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
// Always log packets when running tests.
Services.prefs.setBoolPref("devtools.debugger.log", true);
SimpleTest.registerCleanupFunction(function() {
Services.prefs.clearUserPref("devtools.debugger.log");
});
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/devtools/Loader.jsm");
let { require } = devtools;
let { MemoryFront } = require("devtools/server/actors/memory");
function startServerAndGetSelectedTabMemory() {
DebuggerServer.init(() => true);
DebuggerServer.addBrowserActors();
var client = new DebuggerClient(DebuggerServer.connectPipe());
return new Promise((resolve, reject) => {
client.connect(response => {
if (response.error) {
reject(new Error(response.error + ": " + response.message));
return;
}
client.listTabs(response => {
if (response.error) {
reject(new Error(response.error + ": " + response.message));
return;
}
var form = response.tabs[response.selected];
var memory = MemoryFront(client, form);
resolve({ memory, client });
});
});
});
}
function destroyServerAndFinish(client) {
client.close(() => {
DebuggerServer.destroy();
SimpleTest.finish()
});
}

View File

@ -11,59 +11,26 @@ Bug 923275 - Add a memory monitor widget to the developer toolbar
</head>
<body>
<pre id="test">
<script src="memory-helpers.js" type="application/javascript;version=1.8"></script>
<script>
window.onload = function() {
var Cu = Components.utils;
var Cc = Components.classes;
var Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
// Always log packets when running tests.
Services.prefs.setBoolPref("devtools.debugger.log", true);
SimpleTest.registerCleanupFunction(function() {
Services.prefs.clearUserPref("devtools.debugger.log");
});
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
SimpleTest.waitForExplicitFinish();
var {MemoryFront} = devtools.require("devtools/server/actors/memory");
DebuggerServer.init(function () { return true; });
DebuggerServer.addBrowserActors();
var client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(function onConnect() {
client.listTabs(function onListTabs(aResponse) {
var form = aResponse.tabs[aResponse.selected];
var front = MemoryFront(client, form);
front.measure().then(measurement => {
ok(measurement.total > 0, "total memory is valid");
ok(measurement.domSize > 0, "domSize is valid");
ok(measurement.styleSize > 0, "styleSize is valid");
ok(measurement.jsObjectsSize > 0, "jsObjectsSize is valid");
ok(measurement.jsStringsSize > 0, "jsStringsSize is valid");
ok(measurement.jsOtherSize > 0, "jsOtherSize is valid");
ok(measurement.otherSize > 0, "otherSize is valid");
ok(measurement.jsMilliseconds, "jsMilliseconds is valid");
ok(measurement.nonJSMilliseconds, "nonJSMilliseconds is valid");
client.close(() => {
DebuggerServer.destroy();
SimpleTest.finish()
});
});
});
Task.spawn(function* () {
var { memory, client } = yield startServerAndGetSelectedTabMemory();
var measurement = yield memory.measure();
ok(measurement.total > 0, "total memory is valid");
ok(measurement.domSize > 0, "domSize is valid");
ok(measurement.styleSize > 0, "styleSize is valid");
ok(measurement.jsObjectsSize > 0, "jsObjectsSize is valid");
ok(measurement.jsStringsSize > 0, "jsStringsSize is valid");
ok(measurement.jsOtherSize > 0, "jsOtherSize is valid");
ok(measurement.otherSize > 0, "otherSize is valid");
ok(measurement.jsMilliseconds, "jsMilliseconds is valid");
ok(measurement.nonJSMilliseconds, "nonJSMilliseconds is valid");
destroyServerAndFinish(client);
});
}
};
</script>
</pre>
</body>

View File

@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 960671 - Test attaching and detaching from a memory actor.
-->
<head>
<meta charset="utf-8">
<title>Memory monitoring actor test</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>
<pre id="test">
<script src="memory-helpers.js" type="application/javascript;version=1.8"></script>
<script>
window.onload = function() {
SimpleTest.waitForExplicitFinish();
Task.spawn(function* () {
var { memory, client } = yield startServerAndGetSelectedTabMemory();
yield memory.attach();
ok(true, "Shouldn't have gotten an error attaching.");
yield memory.detach();
ok(true, "Shouldn't have gotten an error detaching.");
destroyServerAndFinish(client);
});
};
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,49 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 960671 - Test attaching and detaching while in the wrong state.
-->
<head>
<meta charset="utf-8">
<title>Memory monitoring actor test</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>
<pre id="test">
<script src="memory-helpers.js" type="application/javascript;version=1.8"></script>
<script>
window.onload = function() {
SimpleTest.waitForExplicitFinish();
Task.spawn(function* () {
var { memory, client } = yield startServerAndGetSelectedTabMemory();
var e = null;
try {
yield memory.detach();
}
catch (ee) {
e = ee;
}
ok(e, "Should have hit the wrongState error");
yield memory.attach();
e = null;
try {
yield memory.attach();
}
catch (ee) {
e = ee;
}
ok(e, "Should have hit the wrongState error");
yield memory.detach();
destroyServerAndFinish(client);
});
};
</script>
</pre>
</body>
</html>

View File

@ -57,7 +57,8 @@ function CommonNativeApp(aApp, aManifest, aCategories, aRegistryDir) {
aApp.name = aManifest.name;
this.uniqueName = WebappOSUtils.getUniqueName(aApp);
let localeManifest = new ManifestHelper(aManifest, aApp.origin);
let localeManifest =
new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
this.appLocalizedName = localeManifest.name;
this.appNameAsFilename = stripStringForFilename(aApp.name);
@ -99,7 +100,7 @@ CommonNativeApp.prototype = {
*
*/
_setData: function(aApp, aManifest) {
let manifest = new ManifestHelper(aManifest, aApp.origin);
let manifest = new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
let origin = Services.io.newURI(aApp.origin, null, null);
this.iconURI = Services.io.newURI(manifest.biggestIconURL || DEFAULT_ICON_URL,

View File

@ -47,7 +47,8 @@ this.WebappManager = {
doInstall: function(data, window) {
let jsonManifest = data.isPackage ? data.app.updateManifest : data.app.manifest;
let manifest = new ManifestHelper(jsonManifest, data.app.origin);
let manifest =
new ManifestHelper(jsonManifest, data.app.origin, data.app.manifestURL);
let name = manifest.name;
let bundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties");

View File

@ -48,7 +48,8 @@ this.WebappRT = {
get localeManifest() {
return new ManifestHelper(this.config.app.manifest,
this.config.app.origin);
this.config.app.origin,
this.config.app.manifestURL);
},
get appID() {

View File

@ -1001,7 +1001,7 @@ AndroidBridge::InitCamera(const nsCString& contentType, uint32_t camera, uint32_
AutoLocalJNIFrame jniFrame(env, 1);
jintArray arr = mozilla::widget::android::GeckoAppShell::InitCameraWrapper
(NS_ConvertUTF8toUTF16(contentType), (int32_t) camera, (int32_t) width, (int32_t) height);
(NS_ConvertUTF8toUTF16(contentType), (int32_t) camera, (int32_t) *width, (int32_t) *height);
if (!arr)
return false;