mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
ecd3864bc0
@ -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="3e7409120827303329b00a8c6f2f26ccc5a4f59b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80af23f8c74d9d2e9388d8ed3c204040b5c528ec"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
|
@ -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="3e7409120827303329b00a8c6f2f26ccc5a4f59b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80af23f8c74d9d2e9388d8ed3c204040b5c528ec"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3e7409120827303329b00a8c6f2f26ccc5a4f59b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80af23f8c74d9d2e9388d8ed3c204040b5c528ec"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
|
@ -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="3e7409120827303329b00a8c6f2f26ccc5a4f59b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80af23f8c74d9d2e9388d8ed3c204040b5c528ec"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"branch": "",
|
||||
"revision": ""
|
||||
},
|
||||
"revision": "e7950c4d17aa917001d257fc9b0288c0c0ea3c21",
|
||||
"revision": "215bb3fd100d59db61cb9a710d814380e5f054b1",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -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="3e7409120827303329b00a8c6f2f26ccc5a4f59b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80af23f8c74d9d2e9388d8ed3c204040b5c528ec"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -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="3e7409120827303329b00a8c6f2f26ccc5a4f59b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80af23f8c74d9d2e9388d8ed3c204040b5c528ec"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -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="3e7409120827303329b00a8c6f2f26ccc5a4f59b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80af23f8c74d9d2e9388d8ed3c204040b5c528ec"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -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="3e7409120827303329b00a8c6f2f26ccc5a4f59b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80af23f8c74d9d2e9388d8ed3c204040b5c528ec"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -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="3e7409120827303329b00a8c6f2f26ccc5a4f59b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80af23f8c74d9d2e9388d8ed3c204040b5c528ec"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
|
@ -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="3e7409120827303329b00a8c6f2f26ccc5a4f59b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80af23f8c74d9d2e9388d8ed3c204040b5c528ec"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -140,16 +140,19 @@ let RemoteTabViewer = {
|
||||
list.removeItemAt(i);
|
||||
}
|
||||
|
||||
let seenURLs = new Set();
|
||||
let localURLs = engine.getOpenURLs();
|
||||
|
||||
for (let [guid, client] in Iterator(engine.getAllClients())) {
|
||||
// Create the client node, but don't add it in-case we don't show any tabs
|
||||
let appendClient = true;
|
||||
let seenURLs = {};
|
||||
|
||||
client.tabs.forEach(function({title, urlHistory, icon}) {
|
||||
let url = urlHistory[0];
|
||||
if (engine.locallyOpenTabMatchesURL(url) || url in seenURLs)
|
||||
if (!url || localURLs.has(url) || seenURLs.has(url)) {
|
||||
return;
|
||||
|
||||
seenURLs[url] = null;
|
||||
}
|
||||
seenURLs.add(url);
|
||||
|
||||
if (appendClient) {
|
||||
let attrs = {
|
||||
|
@ -161,6 +161,11 @@ function test() {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "text/html",
|
||||
method: "GET",
|
||||
template: "https://www.google.com/",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
@ -24,6 +24,7 @@ function test() {
|
||||
yield changeManifestValueBad("name", "the worst app");
|
||||
yield addNewManifestProperty("developer", "foo", "bar");
|
||||
yield addNewManifestPropertyBad("developer", "blob", "bob");
|
||||
yield removeManifestProperty("developer", "foo");
|
||||
gManifestWindow = null;
|
||||
gManifestEditor = null;
|
||||
|
||||
@ -162,3 +163,26 @@ function addNewManifestPropertyBad(parent, key, value) {
|
||||
"Manifest contains key, but it should not");
|
||||
});
|
||||
}
|
||||
|
||||
function removeManifestProperty(parent, key) {
|
||||
info("*** Remove property test ***");
|
||||
|
||||
return Task.spawn(function() {
|
||||
let parentElem = gManifestWindow.document
|
||||
.querySelector("[id ^= '" + parent + "']");
|
||||
ok(parentElem, "Found parent element");
|
||||
|
||||
let keyExists = key in gManifestEditor.manifest[parent];
|
||||
ok(keyExists,
|
||||
"The manifest contains the key under the expected parent");
|
||||
|
||||
let newElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']");
|
||||
let removePropertyButton = newElem.querySelector(".variables-view-delete");
|
||||
ok(removePropertyButton, "The remove property button was found");
|
||||
removePropertyButton.click();
|
||||
|
||||
yield waitForUpdate();
|
||||
|
||||
ok(!(key in gManifestEditor.manifest[parent]), "Property was successfully removed");
|
||||
});
|
||||
}
|
||||
|
@ -273,6 +273,8 @@ let DebuggerView = {
|
||||
|
||||
DebuggerController.Breakpoints.destroy().then(() => {
|
||||
window.emit(EVENTS.EDITOR_UNLOADED, this.editor);
|
||||
this.editor.destroy();
|
||||
this.editor = null;
|
||||
aCallback();
|
||||
});
|
||||
},
|
||||
|
@ -5,6 +5,9 @@
|
||||
|
||||
function test()
|
||||
{
|
||||
// Test is slow on Linux EC2 instances - Bug 962931
|
||||
requestLongerTimeout(2);
|
||||
|
||||
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
let Toolbox = devtools.Toolbox;
|
||||
|
@ -39,6 +39,11 @@ XPCOMUtils.defineLazyGetter(this, "CertUtils",
|
||||
return mod;
|
||||
});
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
|
||||
"@mozilla.org/xre/app-info;1",
|
||||
"nsICrashReporter");
|
||||
#endif
|
||||
|
||||
const FILE_CACHE = "experiments.json";
|
||||
const OBSERVER_TOPIC = "experiments-changed";
|
||||
@ -673,11 +678,6 @@ Experiments.Experiments.prototype = {
|
||||
return this._pendingTasks.loadFromCache;
|
||||
}
|
||||
|
||||
if (this._pendingTasks.updateManifest) {
|
||||
// We're already updating the manifest, no need to load the cached version.
|
||||
return this._pendingTasks.updateManifest;
|
||||
}
|
||||
|
||||
let path = this._cacheFilePath;
|
||||
this._pendingTasks.loadFromCache = Task.spawn(function () {
|
||||
try {
|
||||
@ -888,6 +888,7 @@ Experiments.Experiments.prototype = {
|
||||
yield activeExperiment.stop();
|
||||
yield activeExperiment.start();
|
||||
} catch (e) {
|
||||
gLogger.error(e);
|
||||
// On failure try the next experiment.
|
||||
activeExperiment = null;
|
||||
}
|
||||
@ -921,6 +922,7 @@ Experiments.Experiments.prototype = {
|
||||
try {
|
||||
yield experiment.start();
|
||||
activeChanged = true;
|
||||
activeExperiment = experiment;
|
||||
break;
|
||||
} catch (e) {
|
||||
// On failure try the next experiment.
|
||||
@ -933,6 +935,12 @@ Experiments.Experiments.prototype = {
|
||||
Services.obs.notifyObservers(null, OBSERVER_TOPIC, null);
|
||||
}
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
if (activeExperiment) {
|
||||
gCrashReporter.annotateCrashReport("ActiveExperiment", activeExperiment.id);
|
||||
}
|
||||
#endif
|
||||
|
||||
throw new Task.Result(activeChanged);
|
||||
}.bind(this));
|
||||
},
|
||||
|
@ -9,7 +9,7 @@ EXTRA_COMPONENTS += [
|
||||
|
||||
JS_MODULES_PATH = 'modules/experiments'
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'Experiments.jsm',
|
||||
]
|
||||
|
||||
|
@ -35,7 +35,7 @@ passwordsCount.label = #1 password;#1 passwords
|
||||
# LOCALIZATION NOTE (addonsCount.label): Semicolon-separated list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# #1 is the number of add-ons, see the link above for forms
|
||||
addonsCount.label = #1 addon;#1 addons
|
||||
addonsCount.label = #1 add-on;#1 add-ons
|
||||
|
||||
save.recoverykey.title = Save Recovery Key
|
||||
save.recoverykey.defaultfilename = Firefox Recovery Key.html
|
||||
|
@ -29,5 +29,5 @@
|
||||
<MozParam name="channel" condition="purpose" purpose="homepage" value="np"/>
|
||||
<MozParam name="source" condition="purpose" purpose="homepage" value="hp"/>
|
||||
</Url>
|
||||
<SearchForm>https://www.google.com/</SearchForm>
|
||||
<Url type="text/html" method="GET" template="https://www.google.com/" rel="searchform"/>
|
||||
</SearchPlugin>
|
||||
|
@ -68,6 +68,7 @@ RemoteTabsView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
let tabsEngine = Weave.Service.engineManager.get("tabs");
|
||||
let list = this._set;
|
||||
let seenURLs = new Set();
|
||||
let localURLs = tabsEngine.getOpenURLs();
|
||||
|
||||
// Clear grid, We don't know what has happened to tabs since last sync
|
||||
// Also can result in duplicate tabs(bug 864614)
|
||||
@ -76,7 +77,7 @@ RemoteTabsView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
for (let [guid, client] in Iterator(tabsEngine.getAllClients())) {
|
||||
client.tabs.forEach(function({title, urlHistory, icon}) {
|
||||
let url = urlHistory[0];
|
||||
if (tabsEngine.locallyOpenTabMatchesURL(url) || seenURLs.has(url)) {
|
||||
if (!url || getOpenURLs.has(url) || seenURLs.has(url)) {
|
||||
return;
|
||||
}
|
||||
seenURLs.add(url);
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -178,6 +178,10 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#main-window[tabsintitlebar]:not([inFullscreen]) .tab-close-button:not(:-moz-any(:hover,:-moz-lwtheme,[selected="true"])) {
|
||||
-moz-image-region: rect(0, 64px, 16px, 48px);
|
||||
}
|
||||
|
||||
#main-window[tabsintitlebar][sizemode="normal"] #titlebar-content:-moz-lwtheme {
|
||||
/* Render a window top border: */
|
||||
background-image: linear-gradient(to bottom,
|
||||
@ -888,6 +892,7 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
#back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon,
|
||||
#back-button[open="true"] > .toolbarbutton-icon {
|
||||
background-color: hsla(210,4%,10%,.12) !important;
|
||||
box-shadow: 0 1px 0 0 hsla(210,80%,20%,.1) inset !important;
|
||||
|
@ -1164,36 +1164,37 @@ nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext,
|
||||
NS_LossyConvertUTF16toASCII(aError).get());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsRefPtr<CameraErrorCallback>* errorCb;
|
||||
nsRefPtr<CameraErrorCallback> errorCb;
|
||||
|
||||
switch (aContext) {
|
||||
case CameraControlListener::kInStartCamera:
|
||||
mGetCameraOnSuccessCb = nullptr;
|
||||
errorCb = &mGetCameraOnErrorCb;
|
||||
errorCb = mGetCameraOnErrorCb.forget();
|
||||
break;
|
||||
|
||||
case CameraControlListener::kInStopCamera:
|
||||
mReleaseOnSuccessCb = nullptr;
|
||||
errorCb = &mReleaseOnErrorCb;
|
||||
errorCb = mReleaseOnErrorCb.forget();
|
||||
break;
|
||||
|
||||
case CameraControlListener::kInSetConfiguration:
|
||||
mSetConfigurationOnSuccessCb = nullptr;
|
||||
errorCb = &mSetConfigurationOnErrorCb;
|
||||
errorCb = mSetConfigurationOnErrorCb.forget();
|
||||
break;
|
||||
|
||||
case CameraControlListener::kInAutoFocus:
|
||||
mAutoFocusOnSuccessCb = nullptr;
|
||||
errorCb = &mAutoFocusOnErrorCb;
|
||||
errorCb = mAutoFocusOnErrorCb.forget();
|
||||
break;
|
||||
|
||||
case CameraControlListener::kInTakePicture:
|
||||
mTakePictureOnSuccessCb = nullptr;
|
||||
errorCb = &mTakePictureOnErrorCb;
|
||||
errorCb = mTakePictureOnErrorCb.forget();
|
||||
break;
|
||||
|
||||
case CameraControlListener::kInStartRecording:
|
||||
mStartRecordingOnSuccessCb = nullptr;
|
||||
errorCb = &mStartRecordingOnErrorCb;
|
||||
errorCb = mStartRecordingOnErrorCb.forget();
|
||||
break;
|
||||
|
||||
case CameraControlListener::kInStopRecording:
|
||||
@ -1231,17 +1232,13 @@ nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext,
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(errorCb);
|
||||
|
||||
if (!*errorCb) {
|
||||
if (!errorCb) {
|
||||
DOM_CAMERA_LOGW("DOM No error handler for error '%s' in context=%d\n",
|
||||
NS_LossyConvertUTF16toASCII(aError).get(), aContext);
|
||||
return;
|
||||
}
|
||||
|
||||
// kung-fu death grip
|
||||
nsRefPtr<CameraErrorCallback> cb = (*errorCb).forget();
|
||||
ErrorResult ignored;
|
||||
cb->Call(aError, ignored);
|
||||
errorCb->Call(aError, ignored);
|
||||
}
|
||||
|
||||
|
@ -245,17 +245,7 @@ MozInputMethodManager.prototype = {
|
||||
|
||||
classID: Components.ID("{7e9d7280-ef86-11e2-b778-0800200c9a66}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIInputMethodManager
|
||||
]),
|
||||
|
||||
classInfo: XPCOMUtils.generateCI({
|
||||
"classID": Components.ID("{7e9d7280-ef86-11e2-b778-0800200c9a66}"),
|
||||
"contractID": "@mozilla.org/b2g-imm;1",
|
||||
"interfaces": [Ci.nsIInputMethodManager],
|
||||
"flags": Ci.nsIClassInfo.DOM_OBJECT,
|
||||
"classDescription": "B2G Input Method Manager"
|
||||
}),
|
||||
QueryInterface: XPCOMUtils.generateQI([]),
|
||||
|
||||
showAll: function() {
|
||||
if (!WindowMap.isActive(this._window)) {
|
||||
@ -301,19 +291,10 @@ MozInputMethod.prototype = {
|
||||
classID: Components.ID("{4607330d-e7d2-40a4-9eb8-43967eae0142}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIInputMethod,
|
||||
Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsIObserver
|
||||
]),
|
||||
|
||||
classInfo: XPCOMUtils.generateCI({
|
||||
"classID": Components.ID("{4607330d-e7d2-40a4-9eb8-43967eae0142}"),
|
||||
"contractID": "@mozilla.org/b2g-inputmethod;1",
|
||||
"interfaces": [Ci.nsIInputMethod],
|
||||
"flags": Ci.nsIClassInfo.DOM_OBJECT,
|
||||
"classDescription": "B2G Input Method"
|
||||
}),
|
||||
|
||||
init: function mozInputMethodInit(win) {
|
||||
this._window = win;
|
||||
this._mgmt = new MozInputMethodManager(win);
|
||||
@ -473,19 +454,10 @@ MozInputContext.prototype = {
|
||||
classID: Components.ID("{1e38633d-d08b-4867-9944-afa5c648adb6}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIB2GInputContext,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference
|
||||
]),
|
||||
|
||||
classInfo: XPCOMUtils.generateCI({
|
||||
"classID": Components.ID("{1e38633d-d08b-4867-9944-afa5c648adb6}"),
|
||||
"contractID": "@mozilla.org/b2g-inputcontext;1",
|
||||
"interfaces": [Ci.nsIB2GInputContext],
|
||||
"flags": Ci.nsIClassInfo.DOM_OBJECT,
|
||||
"classDescription": "B2G Input Context"
|
||||
}),
|
||||
|
||||
init: function ic_init(win) {
|
||||
this._window = win;
|
||||
this._utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
|
@ -45,6 +45,7 @@ import org.mozilla.gecko.home.SearchEngine;
|
||||
import org.mozilla.gecko.menu.GeckoMenu;
|
||||
import org.mozilla.gecko.preferences.GeckoPreferences;
|
||||
import org.mozilla.gecko.prompts.Prompt;
|
||||
import org.mozilla.gecko.prompts.PromptListItem;
|
||||
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
||||
import org.mozilla.gecko.toolbar.AutocompleteHandler;
|
||||
import org.mozilla.gecko.toolbar.BrowserToolbar;
|
||||
@ -225,15 +226,10 @@ abstract public class BrowserApp extends GeckoApp
|
||||
final TabsPanel.Panel panel = tab.isPrivate()
|
||||
? TabsPanel.Panel.PRIVATE_TABS
|
||||
: TabsPanel.Panel.NORMAL_TABS;
|
||||
// Delay calling showTabs so that it does not modify the mTabsChangedListeners
|
||||
// array while we are still iterating through the array.
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (areTabsShown() && mTabsPanel.getCurrentPanel() != panel)
|
||||
showTabs(panel);
|
||||
}
|
||||
});
|
||||
|
||||
if (areTabsShown() && mTabsPanel.getCurrentPanel() != panel) {
|
||||
showTabs(panel);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case START:
|
||||
@ -641,6 +637,42 @@ abstract public class BrowserApp extends GeckoApp
|
||||
registerEventListener("Prompt:ShowTop");
|
||||
}
|
||||
|
||||
private void showBookmarkDialog() {
|
||||
final Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
final Prompt ps = new Prompt(this, new Prompt.PromptCallback() {
|
||||
@Override
|
||||
public void onPromptFinished(String result) {
|
||||
int itemId = -1;
|
||||
try {
|
||||
itemId = new JSONObject(result).getInt("button");
|
||||
} catch(JSONException ex) {
|
||||
Log.e(LOGTAG, "Exception reading bookmark prompt result", ex);
|
||||
}
|
||||
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
if (itemId == 0) {
|
||||
new EditBookmarkDialog(BrowserApp.this).show(tab.getURL());
|
||||
} else if (itemId == 1) {
|
||||
String url = tab.getURL();
|
||||
String title = tab.getDisplayTitle();
|
||||
Bitmap favicon = tab.getFavicon();
|
||||
if (url != null && title != null) {
|
||||
GeckoAppShell.createShortcut(title, url, url, favicon, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final PromptListItem[] items = new PromptListItem[2];
|
||||
Resources res = getResources();
|
||||
items[0] = new PromptListItem(res.getString(R.string.contextmenu_edit_bookmark));
|
||||
items[1] = new PromptListItem(res.getString(R.string.contextmenu_add_to_launcher));
|
||||
|
||||
ps.show("", "", items, ListView.CHOICE_MODE_NONE);
|
||||
}
|
||||
|
||||
private void setDynamicToolbarEnabled(boolean enabled) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
|
||||
@ -705,7 +737,16 @@ abstract public class BrowserApp extends GeckoApp
|
||||
}
|
||||
|
||||
if (itemId == R.id.subscribe) {
|
||||
subscribeToFeeds(Tabs.getInstance().getSelectedTab());
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null && tab.hasFeeds()) {
|
||||
JSONObject args = new JSONObject();
|
||||
try {
|
||||
args.put("tabId", tab.getId());
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "error building json arguments");
|
||||
}
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Feeds:Subscribe", args.toString()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -735,6 +776,27 @@ abstract public class BrowserApp extends GeckoApp
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.add_to_launcher) {
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final String url = tab.getURL();
|
||||
final String title = tab.getDisplayTitle();
|
||||
if (url == null || title == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final OnFaviconLoadedListener listener = new GeckoAppShell.CreateShortcutFaviconLoadedListener(url, title);
|
||||
Favicons.getSizedFavicon(url,
|
||||
tab.getFaviconURL(),
|
||||
Integer.MAX_VALUE,
|
||||
LoadFaviconTask.FLAG_PERSIST,
|
||||
listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2089,9 +2151,6 @@ abstract public class BrowserApp extends GeckoApp
|
||||
MenuItem desktopMode = aMenu.findItem(R.id.desktop_mode);
|
||||
MenuItem enterGuestMode = aMenu.findItem(R.id.new_guest_session);
|
||||
MenuItem exitGuestMode = aMenu.findItem(R.id.exit_guest_session);
|
||||
MenuItem subscribe = aMenu.findItem(R.id.save_subscribe);
|
||||
MenuItem addToReadingList = aMenu.findItem(R.id.reading_list_add);
|
||||
MenuItem save = aMenu.findItem(R.id.save);
|
||||
|
||||
// Only show the "Quit" menu item on pre-ICS or television devices.
|
||||
// In ICS+, it's easy to kill an app through the task switcher.
|
||||
@ -2116,15 +2175,11 @@ abstract public class BrowserApp extends GeckoApp
|
||||
return true;
|
||||
}
|
||||
|
||||
save.setVisible(!GeckoProfile.get(this).inGuestMode());
|
||||
if (tab.isBookmark() || tab.isReadingListItem()) {
|
||||
save.setIcon(R.drawable.ic_menu_bookmark_remove);
|
||||
} else {
|
||||
save.setIcon(R.drawable.ic_menu_bookmark_add);
|
||||
}
|
||||
|
||||
bookmark.setEnabled(!AboutPages.isAboutReader(tab.getURL()));
|
||||
bookmark.setVisible(!GeckoProfile.get(this).inGuestMode());
|
||||
bookmark.setCheckable(true);
|
||||
bookmark.setChecked(tab.isBookmark());
|
||||
bookmark.setIcon(tab.isBookmark() ? R.drawable.ic_menu_bookmark_remove : R.drawable.ic_menu_bookmark_add);
|
||||
|
||||
back.setEnabled(tab.canDoBack());
|
||||
forward.setEnabled(tab.canDoForward());
|
||||
@ -2214,22 +2269,18 @@ abstract public class BrowserApp extends GeckoApp
|
||||
else
|
||||
enterGuestMode.setVisible(true);
|
||||
|
||||
addToReadingList.setChecked(tab.isReadingListItem());
|
||||
addToReadingList.setEnabled(tab.getReaderEnabled());
|
||||
|
||||
subscribe.setEnabled(tab.hasFeeds());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
final Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
Tab tab = null;
|
||||
Intent intent = null;
|
||||
|
||||
final int itemId = item.getItemId();
|
||||
|
||||
if (itemId == R.id.bookmark) {
|
||||
tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null) {
|
||||
if (item.isChecked()) {
|
||||
tab.removeBookmark();
|
||||
@ -2239,12 +2290,12 @@ abstract public class BrowserApp extends GeckoApp
|
||||
tab.addBookmark();
|
||||
getButtonToast().show(false,
|
||||
getResources().getString(R.string.bookmark_added),
|
||||
getResources().getString(R.string.contextmenu_edit_bookmark),
|
||||
getResources().getString(R.string.bookmark_options),
|
||||
null,
|
||||
new ButtonToast.ToastListener() {
|
||||
@Override
|
||||
public void onButtonClicked() {
|
||||
new EditBookmarkDialog(BrowserApp.this).show(tab.getURL());
|
||||
showBookmarkDialog();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2262,18 +2313,21 @@ abstract public class BrowserApp extends GeckoApp
|
||||
}
|
||||
|
||||
if (itemId == R.id.reload) {
|
||||
tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null)
|
||||
tab.doReload();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.back) {
|
||||
tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null)
|
||||
tab.doBack();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.forward) {
|
||||
tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null)
|
||||
tab.doForward();
|
||||
return true;
|
||||
@ -2316,12 +2370,13 @@ abstract public class BrowserApp extends GeckoApp
|
||||
}
|
||||
|
||||
if (itemId == R.id.desktop_mode) {
|
||||
if (tab == null)
|
||||
Tab selectedTab = Tabs.getInstance().getSelectedTab();
|
||||
if (selectedTab == null)
|
||||
return true;
|
||||
JSONObject args = new JSONObject();
|
||||
try {
|
||||
args.put("desktopMode", !item.isChecked());
|
||||
args.put("tabId", tab.getId());
|
||||
args.put("tabId", selectedTab.getId());
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "error building json arguments");
|
||||
}
|
||||
@ -2356,25 +2411,6 @@ abstract public class BrowserApp extends GeckoApp
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.launcher_add) {
|
||||
addToLauncher(tab.getURL(), tab.getTitle(), tab.getFaviconURL());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.reading_list_add) {
|
||||
if (item.isChecked()) {
|
||||
ReaderModeUtils.removeFromReadingList(tab.getURL());
|
||||
} else {
|
||||
ReaderModeUtils.addToReadingList(tab);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.subscribe || itemId == R.id.save_subscribe) {
|
||||
subscribeToFeeds(tab);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@ -2419,33 +2455,6 @@ abstract public class BrowserApp extends GeckoApp
|
||||
ps.show(res.getString(titleString), res.getString(msgString), null, ListView.CHOICE_MODE_NONE);
|
||||
}
|
||||
|
||||
public void subscribeToFeeds(Tab tab) {
|
||||
if (!tab.hasFeeds()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject args = new JSONObject();
|
||||
try {
|
||||
args.put("tabId", tab.getId());
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "JSON error", e);
|
||||
}
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Feeds:Subscribe", args.toString()));
|
||||
}
|
||||
|
||||
private void addToLauncher(String url, String title, String faviconUrl) {
|
||||
if (url == null || title == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final OnFaviconLoadedListener listener = new GeckoAppShell.CreateShortcutFaviconLoadedListener(url, title);
|
||||
Favicons.getSizedFavicon(url,
|
||||
faviconUrl,
|
||||
Integer.MAX_VALUE,
|
||||
LoadFaviconTask.FLAG_PERSIST,
|
||||
listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will detect if the key pressed is back. If so, will show the history.
|
||||
*/
|
||||
|
@ -244,14 +244,9 @@ class FilePickerResultHandler implements ActivityResultHandler {
|
||||
}
|
||||
});
|
||||
|
||||
// We're already on the UIThread, but we have to post this back to the uithread to avoid
|
||||
// modifying the listener array while its being iterated through.
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Tabs.unregisterOnTabsChangedListener(FileLoaderCallbacks.this);
|
||||
}
|
||||
});
|
||||
// Tabs' listener array is safe to modify during use: its
|
||||
// iteration pattern is based on snapshots.
|
||||
Tabs.unregisterOnTabsChangedListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,15 +20,16 @@ import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Queue;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
|
||||
import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
|
||||
@ -114,48 +115,28 @@ import android.widget.Toast;
|
||||
public class GeckoAppShell
|
||||
{
|
||||
private static final String LOGTAG = "GeckoAppShell";
|
||||
private static final boolean LOGGING = false;
|
||||
|
||||
// static members only
|
||||
// We have static members only.
|
||||
private GeckoAppShell() { }
|
||||
|
||||
static private LinkedList<GeckoEvent> gPendingEvents =
|
||||
new LinkedList<GeckoEvent>();
|
||||
private static boolean restartScheduled = false;
|
||||
private static GeckoEditableListener editableListener = null;
|
||||
|
||||
static private boolean gRestartScheduled = false;
|
||||
private static final Queue<GeckoEvent> PENDING_EVENTS = new ConcurrentLinkedQueue<GeckoEvent>();
|
||||
private static final Map<String, String> ALERT_COOKIES = new ConcurrentHashMap<String, String>();
|
||||
|
||||
static private GeckoEditableListener mEditableListener = null;
|
||||
private static volatile boolean locationHighAccuracyEnabled;
|
||||
|
||||
static private final HashMap<String, String>
|
||||
mAlertCookies = new HashMap<String, String>();
|
||||
// Accessed by NotificationHelper. This should be encapsulated.
|
||||
/* package */ static NotificationClient notificationClient;
|
||||
|
||||
// See also HardwareUtils.LOW_MEMORY_THRESHOLD_MB.
|
||||
static private final int HIGH_MEMORY_DEVICE_THRESHOLD_MB = 768;
|
||||
|
||||
/* Keep in sync with constants found here:
|
||||
http://mxr.mozilla.org/mozilla-central/source/uriloader/base/nsIWebProgressListener.idl
|
||||
*/
|
||||
static public final int WPL_STATE_START = 0x00000001;
|
||||
static public final int WPL_STATE_STOP = 0x00000010;
|
||||
static public final int WPL_STATE_IS_DOCUMENT = 0x00020000;
|
||||
static public final int WPL_STATE_IS_NETWORK = 0x00040000;
|
||||
|
||||
/* Keep in sync with constants found here:
|
||||
http://mxr.mozilla.org/mozilla-central/source/netwerk/base/public/nsINetworkLinkService.idl
|
||||
*/
|
||||
static public final int LINK_TYPE_UNKNOWN = 0;
|
||||
static public final int LINK_TYPE_ETHERNET = 1;
|
||||
static public final int LINK_TYPE_USB = 2;
|
||||
static public final int LINK_TYPE_WIFI = 3;
|
||||
static public final int LINK_TYPE_WIMAX = 4;
|
||||
static public final int LINK_TYPE_2G = 5;
|
||||
static public final int LINK_TYPE_3G = 6;
|
||||
static public final int LINK_TYPE_4G = 7;
|
||||
private static final int HIGH_MEMORY_DEVICE_THRESHOLD_MB = 768;
|
||||
|
||||
public static final String SHORTCUT_TYPE_WEBAPP = "webapp";
|
||||
public static final String SHORTCUT_TYPE_BOOKMARK = "bookmark";
|
||||
|
||||
static private final boolean LOGGING = false;
|
||||
|
||||
static private int sDensityDpi = 0;
|
||||
static private int sScreenDepth = 0;
|
||||
|
||||
@ -182,9 +163,26 @@ public class GeckoAppShell
|
||||
private static Sensor gProximitySensor = null;
|
||||
private static Sensor gLightSensor = null;
|
||||
|
||||
private static volatile boolean mLocationHighAccuracy;
|
||||
/*
|
||||
* Keep in sync with constants found here:
|
||||
* http://mxr.mozilla.org/mozilla-central/source/uriloader/base/nsIWebProgressListener.idl
|
||||
*/
|
||||
static public final int WPL_STATE_START = 0x00000001;
|
||||
static public final int WPL_STATE_STOP = 0x00000010;
|
||||
static public final int WPL_STATE_IS_DOCUMENT = 0x00020000;
|
||||
static public final int WPL_STATE_IS_NETWORK = 0x00040000;
|
||||
|
||||
static NotificationClient sNotificationClient;
|
||||
/* Keep in sync with constants found here:
|
||||
http://mxr.mozilla.org/mozilla-central/source/netwerk/base/public/nsINetworkLinkService.idl
|
||||
*/
|
||||
static public final int LINK_TYPE_UNKNOWN = 0;
|
||||
static public final int LINK_TYPE_ETHERNET = 1;
|
||||
static public final int LINK_TYPE_USB = 2;
|
||||
static public final int LINK_TYPE_WIFI = 3;
|
||||
static public final int LINK_TYPE_WIMAX = 4;
|
||||
static public final int LINK_TYPE_2G = 5;
|
||||
static public final int LINK_TYPE_3G = 6;
|
||||
static public final int LINK_TYPE_4G = 7;
|
||||
|
||||
/* The Android-side API: API methods that Android calls */
|
||||
|
||||
@ -354,27 +352,48 @@ public class GeckoAppShell
|
||||
private static void geckoLoaded() {
|
||||
GeckoEditable editable = new GeckoEditable();
|
||||
// install the gecko => editable listener
|
||||
mEditableListener = editable;
|
||||
editableListener = editable;
|
||||
}
|
||||
|
||||
static void sendPendingEventsToGecko() {
|
||||
try {
|
||||
while (!gPendingEvents.isEmpty()) {
|
||||
GeckoEvent e = gPendingEvents.removeFirst();
|
||||
while (!PENDING_EVENTS.isEmpty()) {
|
||||
final GeckoEvent e = PENDING_EVENTS.poll();
|
||||
notifyGeckoOfEvent(e);
|
||||
}
|
||||
} catch (NoSuchElementException e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the Gecko thread is running, immediately dispatches the event to
|
||||
* Gecko.
|
||||
*
|
||||
* If the Gecko thread is not running, queues the event. If the queue is
|
||||
* full, throws {@link IllegalStateException}.
|
||||
*
|
||||
* Queued events will be dispatched in order of arrival when the Gecko
|
||||
* thread becomes live.
|
||||
*
|
||||
* This method can be called from any thread.
|
||||
*
|
||||
* @param e
|
||||
* the event to dispatch. Cannot be null.
|
||||
*/
|
||||
@RobocopTarget
|
||||
public static void sendEventToGecko(GeckoEvent e) {
|
||||
if (e == null) {
|
||||
throw new IllegalArgumentException("e cannot be null.");
|
||||
}
|
||||
|
||||
if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
|
||||
notifyGeckoOfEvent(e);
|
||||
// Gecko will copy the event data into a normal C++ object. We can recycle the evet now.
|
||||
// Gecko will copy the event data into a normal C++ object. We can recycle the event now.
|
||||
e.recycle();
|
||||
} else {
|
||||
gPendingEvents.addLast(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Throws if unable to add the event due to capacity restrictions.
|
||||
PENDING_EVENTS.add(e);
|
||||
}
|
||||
|
||||
// Tell the Gecko event loop that an event is available.
|
||||
@ -427,16 +446,16 @@ public class GeckoAppShell
|
||||
|
||||
@WrapElementForJNI(generateStatic = true)
|
||||
public static void notifyIME(int type) {
|
||||
if (mEditableListener != null) {
|
||||
mEditableListener.notifyIME(type);
|
||||
if (editableListener != null) {
|
||||
editableListener.notifyIME(type);
|
||||
}
|
||||
}
|
||||
|
||||
@WrapElementForJNI(generateStatic = true)
|
||||
public static void notifyIMEContext(int state, String typeHint,
|
||||
String modeHint, String actionHint) {
|
||||
if (mEditableListener != null) {
|
||||
mEditableListener.notifyIMEContext(state, typeHint,
|
||||
if (editableListener != null) {
|
||||
editableListener.notifyIMEContext(state, typeHint,
|
||||
modeHint, actionHint);
|
||||
}
|
||||
}
|
||||
@ -444,9 +463,9 @@ public class GeckoAppShell
|
||||
@WrapElementForJNI(generateStatic = true)
|
||||
public static void notifyIMEChange(String text, int start, int end, int newEnd) {
|
||||
if (newEnd < 0) { // Selection change
|
||||
mEditableListener.onSelectionChange(start, end);
|
||||
editableListener.onSelectionChange(start, end);
|
||||
} else { // Text change
|
||||
mEditableListener.onTextChange(text, start, end, newEnd);
|
||||
editableListener.onTextChange(text, start, end, newEnd);
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,7 +563,7 @@ public class GeckoAppShell
|
||||
criteria.setSpeedRequired(false);
|
||||
criteria.setBearingRequired(false);
|
||||
criteria.setAltitudeRequired(false);
|
||||
if (mLocationHighAccuracy) {
|
||||
if (locationHighAccuracyEnabled) {
|
||||
criteria.setAccuracy(Criteria.ACCURACY_FINE);
|
||||
criteria.setCostAllowed(true);
|
||||
criteria.setPowerRequirement(Criteria.POWER_HIGH);
|
||||
@ -582,7 +601,7 @@ public class GeckoAppShell
|
||||
|
||||
@WrapElementForJNI
|
||||
public static void enableLocationHighAccuracy(final boolean enable) {
|
||||
mLocationHighAccuracy = enable;
|
||||
locationHighAccuracyEnabled = enable;
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
@ -700,7 +719,7 @@ public class GeckoAppShell
|
||||
// The launch state can only be Launched or GeckoRunning at this point
|
||||
GeckoThread.setLaunchState(GeckoThread.LaunchState.GeckoExiting);
|
||||
if (getGeckoInterface() != null) {
|
||||
if (gRestartScheduled) {
|
||||
if (restartScheduled) {
|
||||
getGeckoInterface().doRestart();
|
||||
} else {
|
||||
getGeckoInterface().getActivity().finish();
|
||||
@ -718,7 +737,7 @@ public class GeckoAppShell
|
||||
|
||||
@WrapElementForJNI
|
||||
static void scheduleRestart() {
|
||||
gRestartScheduled = true;
|
||||
restartScheduled = true;
|
||||
}
|
||||
|
||||
public static Intent getWebappIntent(String aURI, String aOrigin, String aTitle, Bitmap aIcon) {
|
||||
@ -1316,9 +1335,12 @@ public class GeckoAppShell
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only called from GeckoApp.
|
||||
*/
|
||||
public static void setNotificationClient(NotificationClient client) {
|
||||
if (sNotificationClient == null) {
|
||||
sNotificationClient = client;
|
||||
if (notificationClient == null) {
|
||||
notificationClient = client;
|
||||
} else {
|
||||
Log.d(LOGTAG, "Notification client already set");
|
||||
}
|
||||
@ -1345,30 +1367,30 @@ public class GeckoAppShell
|
||||
PendingIntent contentIntent = PendingIntent.getActivity(
|
||||
getContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
mAlertCookies.put(aAlertName, aAlertCookie);
|
||||
ALERT_COOKIES.put(aAlertName, aAlertCookie);
|
||||
callObserver(aAlertName, "alertshow", aAlertCookie);
|
||||
|
||||
sNotificationClient.add(notificationID, aImageUrl, aAlertTitle, aAlertText, contentIntent);
|
||||
notificationClient.add(notificationID, aImageUrl, aAlertTitle, aAlertText, contentIntent);
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
public static void alertsProgressListener_OnProgress(String aAlertName, long aProgress, long aProgressMax, String aAlertText) {
|
||||
int notificationID = aAlertName.hashCode();
|
||||
sNotificationClient.update(notificationID, aProgress, aProgressMax, aAlertText);
|
||||
notificationClient.update(notificationID, aProgress, aProgressMax, aAlertText);
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
public static void closeNotification(String aAlertName) {
|
||||
String alertCookie = mAlertCookies.get(aAlertName);
|
||||
String alertCookie = ALERT_COOKIES.get(aAlertName);
|
||||
if (alertCookie != null) {
|
||||
callObserver(aAlertName, "alertfinished", alertCookie);
|
||||
mAlertCookies.remove(aAlertName);
|
||||
ALERT_COOKIES.remove(aAlertName);
|
||||
}
|
||||
|
||||
removeObserver(aAlertName);
|
||||
|
||||
int notificationID = aAlertName.hashCode();
|
||||
sNotificationClient.remove(notificationID);
|
||||
notificationClient.remove(notificationID);
|
||||
}
|
||||
|
||||
public static void handleNotification(String aAction, String aAlertName, String aAlertCookie) {
|
||||
@ -1377,7 +1399,7 @@ public class GeckoAppShell
|
||||
if (GeckoApp.ACTION_ALERT_CALLBACK.equals(aAction)) {
|
||||
callObserver(aAlertName, "alertclickcallback", aAlertCookie);
|
||||
|
||||
if (sNotificationClient.isOngoing(notificationID)) {
|
||||
if (notificationClient.isOngoing(notificationID)) {
|
||||
// When clicked, keep the notification if it displays progress
|
||||
return;
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||
PendingIntent deletePendingIntent = buildNotificationPendingIntent(message, CLEARED_EVENT);
|
||||
builder.setDeleteIntent(deletePendingIntent);
|
||||
|
||||
GeckoAppShell.sNotificationClient.add(id.hashCode(), builder.build());
|
||||
GeckoAppShell.notificationClient.add(id.hashCode(), builder.build());
|
||||
|
||||
boolean persistent = message.optBoolean(PERSISTENT_ATTR);
|
||||
// We add only not persistent notifications to the list since we want to purge only
|
||||
@ -325,7 +325,7 @@ public final class NotificationHelper implements GeckoEventListener {
|
||||
}
|
||||
|
||||
private void closeNotification(String id) {
|
||||
GeckoAppShell.sNotificationClient.remove(id.hashCode());
|
||||
GeckoAppShell.notificationClient.remove(id.hashCode());
|
||||
sendNotificationWasClosed(id);
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,9 @@
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.util.StringUtils;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
public class ReaderModeUtils {
|
||||
private static final String LOGTAG = "ReaderModeUtils";
|
||||
@ -48,30 +45,4 @@ public class ReaderModeUtils {
|
||||
|
||||
return aboutReaderUrl;
|
||||
}
|
||||
|
||||
public static void addToReadingList(Tab tab) {
|
||||
if (!tab.getReaderEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject json = new JSONObject();
|
||||
try {
|
||||
json.put("tabID", String.valueOf(tab.getId()));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "JSON error - failing to add to reading list", e);
|
||||
return;
|
||||
}
|
||||
|
||||
GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Add", json.toString());
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
|
||||
public static void removeFromReadingList(String url) {
|
||||
if (url == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Remove", url);
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
}
|
||||
|
@ -457,6 +457,22 @@ public class Tab {
|
||||
});
|
||||
}
|
||||
|
||||
public void addToReadingList() {
|
||||
if (!mReaderEnabled)
|
||||
return;
|
||||
|
||||
JSONObject json = new JSONObject();
|
||||
try {
|
||||
json.put("tabID", String.valueOf(getId()));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "JSON error - failing to add to reading list", e);
|
||||
return;
|
||||
}
|
||||
|
||||
GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Add", json.toString());
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
|
||||
public void toggleReaderMode() {
|
||||
if (AboutPages.isAboutReader(mUrl)) {
|
||||
Tabs.getInstance().loadUrl(ReaderModeUtils.getUrlFromAboutReader(mUrl));
|
||||
|
@ -5,8 +5,6 @@
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -528,15 +526,14 @@ public class Tabs implements GeckoEventListener {
|
||||
public void onTabChanged(Tab tab, TabEvents msg, Object data);
|
||||
}
|
||||
|
||||
private static List<OnTabsChangedListener> mTabsChangedListeners =
|
||||
Collections.synchronizedList(new ArrayList<OnTabsChangedListener>());
|
||||
private static final List<OnTabsChangedListener> TABS_CHANGED_LISTENERS = new CopyOnWriteArrayList<OnTabsChangedListener>();
|
||||
|
||||
public static void registerOnTabsChangedListener(OnTabsChangedListener listener) {
|
||||
mTabsChangedListeners.add(listener);
|
||||
TABS_CHANGED_LISTENERS.add(listener);
|
||||
}
|
||||
|
||||
public static void unregisterOnTabsChangedListener(OnTabsChangedListener listener) {
|
||||
mTabsChangedListeners.remove(listener);
|
||||
TABS_CHANGED_LISTENERS.remove(listener);
|
||||
}
|
||||
|
||||
public enum TabEvents {
|
||||
@ -578,15 +575,13 @@ public class Tabs implements GeckoEventListener {
|
||||
public void run() {
|
||||
onTabChanged(tab, msg, data);
|
||||
|
||||
synchronized (mTabsChangedListeners) {
|
||||
if (mTabsChangedListeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (TABS_CHANGED_LISTENERS.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<OnTabsChangedListener> items = mTabsChangedListeners.iterator();
|
||||
while (items.hasNext()) {
|
||||
items.next().onTabChanged(tab, msg, data);
|
||||
}
|
||||
Iterator<OnTabsChangedListener> items = TABS_CHANGED_LISTENERS.iterator();
|
||||
while (items.hasNext()) {
|
||||
items.next().onTabChanged(tab, msg, data);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -93,9 +93,15 @@ public class FaviconDecoder {
|
||||
result.length = length;
|
||||
result.isICO = false;
|
||||
|
||||
Bitmap decodedImage = BitmapUtils.decodeByteArray(buffer, offset, length);
|
||||
if (decodedImage == null) {
|
||||
// What we got wasn't decodable after all. Probably corrupted image, or we got a muffled OOM.
|
||||
return null;
|
||||
}
|
||||
|
||||
// We assume here that decodeByteArray doesn't hold on to the entire supplied
|
||||
// buffer -- worst case, each of our buffers will be twice the necessary size.
|
||||
result.bitmapsDecoded = new SingleBitmapIterator(BitmapUtils.decodeByteArray(buffer, offset, length));
|
||||
result.bitmapsDecoded = new SingleBitmapIterator(decodedImage);
|
||||
result.faviconBytes = buffer;
|
||||
|
||||
return result;
|
||||
|
@ -102,14 +102,9 @@ public class TwoLinePageRow extends LinearLayout
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
// Delay removing the listener to avoid modifying mTabsChangedListeners
|
||||
// while notifyListeners is iterating through the array.
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Tabs.unregisterOnTabsChangedListener(TwoLinePageRow.this);
|
||||
}
|
||||
});
|
||||
// Tabs' listener array is safe to modify during use: its
|
||||
// iteration pattern is based on snapshots.
|
||||
Tabs.unregisterOnTabsChangedListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,7 +54,6 @@
|
||||
<!ENTITY back "Back">
|
||||
<!ENTITY stop "Stop">
|
||||
<!ENTITY site_security "Site Security">
|
||||
<!ENTITY save "Save">
|
||||
|
||||
<!ENTITY close_tab "Close Tab">
|
||||
<!ENTITY one_tab "1 tab">
|
||||
@ -290,7 +289,6 @@ size. -->
|
||||
<!ENTITY site_settings_clear "Clear">
|
||||
<!ENTITY site_settings_no_settings "There are no settings to clear.">
|
||||
|
||||
<!ENTITY reading_list_add "Add to Reading List">
|
||||
<!ENTITY reading_list_added "Page added to your Reading List">
|
||||
<!ENTITY reading_list_removed "Page removed from your Reading List">
|
||||
<!ENTITY reading_list_failed "Failed to add page to your Reading List">
|
||||
|
@ -20,30 +20,10 @@
|
||||
android:title="@string/forward"
|
||||
android:visible="false"/>
|
||||
|
||||
<item android:id="@+id/save"
|
||||
<item android:id="@+id/bookmark"
|
||||
android:icon="@drawable/ic_menu_bookmark_add"
|
||||
android:title="@string/save"
|
||||
android:showAsAction="ifRoom">
|
||||
|
||||
<menu>
|
||||
|
||||
<item android:id="@+id/bookmark"
|
||||
android:title="@string/bookmark"
|
||||
android:checkable="true" />
|
||||
|
||||
<item android:id="@+id/reading_list_add"
|
||||
android:title="@string/reading_list_add"
|
||||
android:checkable="true" />
|
||||
|
||||
<item android:id="@+id/launcher_add"
|
||||
android:title="@string/contextmenu_add_to_launcher" />
|
||||
|
||||
<item android:id="@+id/save_subscribe"
|
||||
android:title="@string/contextmenu_subscribe" />
|
||||
|
||||
</menu>
|
||||
|
||||
</item>
|
||||
android:title="@string/bookmark"
|
||||
android:showAsAction="ifRoom"/>
|
||||
|
||||
<item android:id="@+id/share"
|
||||
android:icon="@drawable/ic_menu_share"
|
||||
|
@ -20,30 +20,10 @@
|
||||
android:title="@string/reload"
|
||||
android:showAsAction="always"/>
|
||||
|
||||
<item android:id="@+id/save"
|
||||
<item android:id="@+id/bookmark"
|
||||
android:icon="@drawable/ic_menu_bookmark_add"
|
||||
android:title="@string/save"
|
||||
android:showAsAction="ifRoom">
|
||||
|
||||
<menu>
|
||||
|
||||
<item android:id="@+id/bookmark"
|
||||
android:title="@string/bookmark"
|
||||
android:checkable="true" />
|
||||
|
||||
<item android:id="@+id/reading_list_add"
|
||||
android:title="@string/reading_list_add"
|
||||
android:checkable="true" />
|
||||
|
||||
<item android:id="@+id/launcher_add"
|
||||
android:title="@string/contextmenu_add_to_launcher" />
|
||||
|
||||
<item android:id="@+id/save_subscribe"
|
||||
android:title="@string/contextmenu_subscribe" />
|
||||
|
||||
</menu>
|
||||
|
||||
</item>
|
||||
android:title="@string/bookmark"
|
||||
android:showAsAction="ifRoom"/>
|
||||
|
||||
<item android:id="@+id/share"
|
||||
android:icon="@drawable/ic_menu_share"
|
||||
|
@ -20,30 +20,10 @@
|
||||
android:title="@string/forward"
|
||||
android:visible="false"/>
|
||||
|
||||
<item android:id="@+id/save"
|
||||
<item android:id="@+id/bookmark"
|
||||
android:icon="@drawable/ic_menu_bookmark_add"
|
||||
android:title="@string/save"
|
||||
android:showAsAction="always">
|
||||
|
||||
<menu>
|
||||
|
||||
<item android:id="@+id/bookmark"
|
||||
android:title="@string/bookmark"
|
||||
android:checkable="true" />
|
||||
|
||||
<item android:id="@+id/reading_list_add"
|
||||
android:title="@string/reading_list_add"
|
||||
android:checkable="true" />
|
||||
|
||||
<item android:id="@+id/launcher_add"
|
||||
android:title="@string/contextmenu_add_to_launcher" />
|
||||
|
||||
<item android:id="@+id/save_subscribe"
|
||||
android:title="@string/contextmenu_subscribe" />
|
||||
|
||||
</menu>
|
||||
|
||||
</item>
|
||||
android:title="@string/bookmark"
|
||||
android:showAsAction="always"/>
|
||||
|
||||
<item android:id="@+id/share"
|
||||
android:icon="@drawable/ic_menu_share"
|
||||
|
@ -18,29 +18,9 @@
|
||||
android:icon="@drawable/ic_menu_forward"
|
||||
android:title="@string/forward"/>
|
||||
|
||||
<item android:id="@+id/save"
|
||||
<item android:id="@+id/bookmark"
|
||||
android:icon="@drawable/ic_menu_bookmark_add"
|
||||
android:title="@string/save">
|
||||
|
||||
<menu>
|
||||
|
||||
<item android:id="@+id/bookmark"
|
||||
android:title="@string/bookmark"
|
||||
android:checkable="true" />
|
||||
|
||||
<item android:id="@+id/reading_list_add"
|
||||
android:title="@string/reading_list_add"
|
||||
android:checkable="true" />
|
||||
|
||||
<item android:id="@+id/launcher_add"
|
||||
android:title="@string/contextmenu_add_to_launcher" />
|
||||
|
||||
<item android:id="@+id/save_subscribe"
|
||||
android:title="@string/contextmenu_subscribe" />
|
||||
|
||||
</menu>
|
||||
|
||||
</item>
|
||||
android:title="@string/bookmark"/>
|
||||
|
||||
<item android:id="@+id/new_tab"
|
||||
android:icon="@drawable/ic_menu_new_tab"
|
||||
|
@ -233,14 +233,12 @@
|
||||
<string name="tabs_normal">&tabs_normal;</string>
|
||||
<string name="tabs_private">&tabs_private;</string>
|
||||
<string name="tabs_synced">&tabs_synced;</string>
|
||||
<string name="save">&save;</string>
|
||||
|
||||
<string name="site_settings_title">&site_settings_title3;</string>
|
||||
<string name="site_settings_cancel">&site_settings_cancel;</string>
|
||||
<string name="site_settings_clear">&site_settings_clear;</string>
|
||||
<string name="site_settings_no_settings">&site_settings_no_settings;</string>
|
||||
|
||||
<string name="reading_list_add">&reading_list_add;</string>
|
||||
<string name="reading_list_added">&reading_list_added;</string>
|
||||
<string name="reading_list_removed">&reading_list_removed;</string>
|
||||
<string name="reading_list_failed">&reading_list_failed;</string>
|
||||
|
@ -91,9 +91,9 @@ skip-if = android_version == "10" || processor == "x86"
|
||||
[testSessionOOMSave]
|
||||
# disabled on x86 and 2.3; bug 945395
|
||||
skip-if = android_version == "10" || processor == "x86"
|
||||
[testSessionOOMRestore]
|
||||
#[testSessionOOMRestore] # see bug 946957
|
||||
# disabled on Android 2.3; bug 979600
|
||||
skip-if = android_version == "10"
|
||||
#skip-if = android_version == "10"
|
||||
[testSettingsMenuItems]
|
||||
# disabled on Android 2.3; bug 979552
|
||||
skip-if = android_version == "10"
|
||||
|
@ -17,7 +17,6 @@ import org.mozilla.gecko.GeckoApplication;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.LightweightTheme;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.ReaderModeUtils;
|
||||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator;
|
||||
@ -1352,7 +1351,10 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
tab.toggleReaderMode();
|
||||
}
|
||||
} else if (event.equals("Reader:LongClick")) {
|
||||
ReaderModeUtils.addToReadingList(Tabs.getInstance().getSelectedTab());
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null) {
|
||||
tab.addToReadingList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,19 +73,15 @@ TabEngine.prototype = {
|
||||
this.service.resource(url).delete();
|
||||
},
|
||||
|
||||
/* The intent is not to show tabs in the menu if they're already
|
||||
* open locally. There are a couple ways to interpret this: for
|
||||
* instance, we could do it by removing a tab from the list when
|
||||
* you open it -- but then if you close it, you can't get back to
|
||||
* it. So the way I'm doing it here is to not show a tab in the menu
|
||||
* if you have a tab open to the same URL, even though this means
|
||||
* that as soon as you navigate anywhere, the original tab will
|
||||
* reappear in the menu.
|
||||
/**
|
||||
* Return a Set of open URLs.
|
||||
*/
|
||||
locallyOpenTabMatchesURL: function TabEngine_localTabMatches(url) {
|
||||
return this._store.getAllTabs().some(function (tab) {
|
||||
return tab.urlHistory[0] == url;
|
||||
});
|
||||
getOpenURLs: function () {
|
||||
let urls = new Set();
|
||||
for (let entry of this._store.getAllTabs()) {
|
||||
urls.add(entry.urlHistory[0]);
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
};
|
||||
|
||||
@ -100,39 +96,60 @@ TabStore.prototype = {
|
||||
return id == this.engine.service.clientsEngine.localID;
|
||||
},
|
||||
|
||||
getAllTabs: function getAllTabs(filter) {
|
||||
getWindowEnumerator: function () {
|
||||
return Services.wm.getEnumerator("navigator:browser");
|
||||
},
|
||||
|
||||
shouldSkipWindow: function (win) {
|
||||
return win.closed ||
|
||||
PrivateBrowsingUtils.isWindowPrivate(win);
|
||||
},
|
||||
|
||||
getTabState: function (tab) {
|
||||
return JSON.parse(Svc.Session.getTabState(tab));
|
||||
},
|
||||
|
||||
getAllTabs: function (filter) {
|
||||
let filteredUrls = new RegExp(Svc.Prefs.get("engine.tabs.filteredUrls"), "i");
|
||||
|
||||
let allTabs = [];
|
||||
|
||||
let currentState = JSON.parse(Svc.Session.getBrowserState());
|
||||
currentState.windows.forEach(function (window) {
|
||||
if (window.isPrivate) {
|
||||
return;
|
||||
let winEnum = this.getWindowEnumerator();
|
||||
while (winEnum.hasMoreElements()) {
|
||||
let win = winEnum.getNext();
|
||||
if (this.shouldSkipWindow(win)) {
|
||||
continue;
|
||||
}
|
||||
window.tabs.forEach(function (tab) {
|
||||
|
||||
dump("WIN IS " + JSON.stringify(win) + "\n");
|
||||
for (let tab of win.gBrowser.tabs) {
|
||||
tabState = this.getTabState(tab);
|
||||
|
||||
// Make sure there are history entries to look at.
|
||||
if (!tab.entries.length)
|
||||
return;
|
||||
if (!tabState || !tabState.entries.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Until we store full or partial history, just grab the current entry.
|
||||
// index is 1 based, so make sure we adjust.
|
||||
let entry = tab.entries[tab.index - 1];
|
||||
let entry = tabState.entries[tabState.index - 1];
|
||||
|
||||
// Filter out some urls if necessary. SessionStore can return empty
|
||||
// tabs in some cases - easiest thing is to just ignore them for now.
|
||||
if (!entry.url || filter && filteredUrls.test(entry.url))
|
||||
return;
|
||||
if (!entry.url || filter && filteredUrls.test(entry.url)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// I think it's also possible that attributes[.image] might not be set
|
||||
// so handle that as well.
|
||||
allTabs.push({
|
||||
title: entry.title || "",
|
||||
urlHistory: [entry.url],
|
||||
icon: tab.attributes && tab.attributes.image || "",
|
||||
lastUsed: Math.floor((tab.lastAccessed || 0) / 1000)
|
||||
icon: tabState.attributes && tabState.attributes.image || "",
|
||||
lastUsed: Math.floor((tabState.lastAccessed || 0) / 1000)
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return allTabs;
|
||||
},
|
||||
|
@ -2,6 +2,7 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://services-common/async.js");
|
||||
Cu.import("resource://testing-common/services-common/utils.js");
|
||||
|
||||
let provider = {
|
||||
getFile: function(prop, persistent) {
|
||||
@ -124,3 +125,70 @@ function generateNewKeys(collectionKeys, collections=null) {
|
||||
collectionKeys.setContents(wbo.cleartext, modified);
|
||||
}
|
||||
|
||||
// Helpers for testing open tabs.
|
||||
// These reflect part of the internal structure of TabEngine,
|
||||
// and stub part of Service.wm.
|
||||
|
||||
function mockShouldSkipWindow (win) {
|
||||
return win.closed ||
|
||||
win.mockIsPrivate;
|
||||
}
|
||||
|
||||
function mockGetTabState (tab) {
|
||||
return tab;
|
||||
}
|
||||
|
||||
function mockGetWindowEnumerator(url, numWindows, numTabs) {
|
||||
let elements = [];
|
||||
for (let w = 0; w < numWindows; ++w) {
|
||||
let tabs = [];
|
||||
let win = {
|
||||
closed: false,
|
||||
mockIsPrivate: false,
|
||||
gBrowser: {
|
||||
tabs: tabs,
|
||||
},
|
||||
};
|
||||
elements.push(win);
|
||||
|
||||
for (let t = 0; t < numTabs; ++t) {
|
||||
tabs.push(TestingUtils.deepCopy({
|
||||
index: 1,
|
||||
entries: [{
|
||||
url: ((typeof url == "string") ? url : url()),
|
||||
title: "title"
|
||||
}],
|
||||
attributes: {
|
||||
image: "image"
|
||||
},
|
||||
lastAccessed: 1499
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Always include a closed window and a private window.
|
||||
elements.push({
|
||||
closed: true,
|
||||
mockIsPrivate: false,
|
||||
gBrowser: {
|
||||
tabs: [],
|
||||
},
|
||||
});
|
||||
|
||||
elements.push({
|
||||
closed: false,
|
||||
mockIsPrivate: true,
|
||||
gBrowser: {
|
||||
tabs: [],
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
hasMoreElements: function () {
|
||||
return elements.length;
|
||||
},
|
||||
getNext: function () {
|
||||
return elements.shift();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -5,44 +5,32 @@ Cu.import("resource://services-sync/engines/tabs.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
function fakeSessionSvc() {
|
||||
let tabs = [];
|
||||
for(let i = 0; i < arguments.length; i++) {
|
||||
tabs.push({
|
||||
index: 1,
|
||||
entries: [{
|
||||
url: arguments[i],
|
||||
title: "title"
|
||||
}],
|
||||
attributes: {
|
||||
image: "image"
|
||||
}
|
||||
});
|
||||
}
|
||||
let obj = {windows: [{tabs: tabs}]};
|
||||
|
||||
// delete the getter, or the previously created fake Session
|
||||
delete Svc.Session;
|
||||
Svc.Session = {
|
||||
getBrowserState: function() JSON.stringify(obj)
|
||||
};
|
||||
function getMocks() {
|
||||
let engine = new TabEngine(Service);
|
||||
let store = engine._store;
|
||||
store.getTabState = mockGetTabState;
|
||||
store.shouldSkipWindow = mockShouldSkipWindow;
|
||||
return [engine, store];
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
_("Test getOpenURLs.");
|
||||
let [engine, store] = getMocks();
|
||||
|
||||
_("test locallyOpenTabMatchesURL");
|
||||
let engine = new TabEngine(Service);
|
||||
|
||||
// 3 tabs
|
||||
fakeSessionSvc("http://bar.com", "http://foo.com", "http://foobar.com");
|
||||
let urls = ["http://bar.com", "http://foo.com", "http://foobar.com"];
|
||||
function threeURLs() {
|
||||
return urls.pop();
|
||||
}
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, threeURLs, 1, 3);
|
||||
|
||||
let matches;
|
||||
|
||||
_(" test matching works (true)");
|
||||
matches = engine.locallyOpenTabMatchesURL("http://foo.com");
|
||||
let openurlsset = engine.getOpenURLs();
|
||||
matches = openurlsset.has("http://foo.com");
|
||||
do_check_true(matches);
|
||||
|
||||
_(" test matching works (false)");
|
||||
matches = engine.locallyOpenTabMatchesURL("http://barfoo.com");
|
||||
matches = openurlsset.has("http://barfoo.com");
|
||||
do_check_false(matches);
|
||||
}
|
||||
|
@ -6,6 +6,14 @@ Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services-common/utils.js");
|
||||
|
||||
function getMockStore() {
|
||||
let engine = new TabEngine(Service);
|
||||
let store = engine._store;
|
||||
store.getTabState = mockGetTabState;
|
||||
store.shouldSkipWindow = mockShouldSkipWindow;
|
||||
return store;
|
||||
}
|
||||
|
||||
function test_create() {
|
||||
let store = new TabEngine(Service)._store;
|
||||
|
||||
@ -40,43 +48,15 @@ function test_create() {
|
||||
Svc.Prefs.reset("notifyTabState");
|
||||
}
|
||||
|
||||
function fakeSessionSvc(url, numtabs) {
|
||||
// first delete the getter, or the previously
|
||||
// created fake Session
|
||||
delete Svc.Session;
|
||||
Svc.Session = {
|
||||
getBrowserState: function() {
|
||||
let obj = {
|
||||
windows: [{
|
||||
tabs: [{
|
||||
index: 1,
|
||||
entries: [{
|
||||
url: url,
|
||||
title: "title"
|
||||
}],
|
||||
attributes: {
|
||||
image: "image"
|
||||
},
|
||||
lastAccessed: 1499
|
||||
}]
|
||||
}]
|
||||
};
|
||||
if (numtabs) {
|
||||
let tabs = obj.windows[0].tabs;
|
||||
for (let i = 0; i < numtabs-1; i++)
|
||||
tabs.push(TestingUtils.deepCopy(tabs[0]));
|
||||
}
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function test_getAllTabs() {
|
||||
let store = new TabEngine(Service)._store, tabs;
|
||||
let store = getMockStore();
|
||||
let tabs;
|
||||
|
||||
_("get all tabs");
|
||||
fakeSessionSvc("http://foo.com");
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://foo.com", 1, 1);
|
||||
|
||||
_("Get all tabs.");
|
||||
tabs = store.getAllTabs();
|
||||
_("Tabs: " + JSON.stringify(tabs));
|
||||
do_check_eq(tabs.length, 1);
|
||||
do_check_eq(tabs[0].title, "title");
|
||||
do_check_eq(tabs[0].urlHistory.length, 1);
|
||||
@ -84,31 +64,32 @@ function test_getAllTabs() {
|
||||
do_check_eq(tabs[0].icon, "image");
|
||||
do_check_eq(tabs[0].lastUsed, 1);
|
||||
|
||||
_("get all tabs, and check that filtering works");
|
||||
// we don't bother testing every URL type here, the
|
||||
// filteredUrls regex really should have it own tests
|
||||
fakeSessionSvc("about:foo");
|
||||
_("Get all tabs, and check that filtering works.");
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "about:foo", 1, 1);
|
||||
tabs = store.getAllTabs(true);
|
||||
_("Filtered: " + JSON.stringify(tabs));
|
||||
do_check_eq(tabs.length, 0);
|
||||
}
|
||||
|
||||
function test_createRecord() {
|
||||
let store = new TabEngine(Service)._store, record;
|
||||
let store = getMockStore();
|
||||
let record;
|
||||
|
||||
store.getTabState = mockGetTabState;
|
||||
store.shouldSkipWindow = mockShouldSkipWindow;
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://foo.com", 1, 1);
|
||||
|
||||
// get some values before testing
|
||||
fakeSessionSvc("http://foo.com");
|
||||
let tabs = store.getAllTabs();
|
||||
let tabsize = JSON.stringify(tabs[0]).length;
|
||||
let numtabs = Math.ceil(20000./77.);
|
||||
|
||||
_("create a record");
|
||||
fakeSessionSvc("http://foo.com");
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://foo.com", 1, 1);
|
||||
record = store.createRecord("fake-guid");
|
||||
do_check_true(record instanceof TabSetRecord);
|
||||
do_check_eq(record.tabs.length, 1);
|
||||
|
||||
_("create a big record");
|
||||
fakeSessionSvc("http://foo.com", numtabs);
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://foo.com", 1, numtabs);
|
||||
record = store.createRecord("fake-guid");
|
||||
do_check_true(record instanceof TabSetRecord);
|
||||
do_check_eq(record.tabs.length, 256);
|
||||
|
@ -1163,7 +1163,7 @@ Engine.prototype = {
|
||||
_type: null,
|
||||
// The name of the charset used to submit the search terms.
|
||||
_queryCharset: null,
|
||||
// A URL string pointing to the engine's search form.
|
||||
// The engine's raw SearchForm value (URL string pointing to a search form).
|
||||
__searchForm: null,
|
||||
get _searchForm() {
|
||||
return this.__searchForm;
|
||||
@ -1364,10 +1364,11 @@ Engine.prototype = {
|
||||
* if no matching URL is found.
|
||||
*
|
||||
* @param aType string to match the EngineURL's type attribute
|
||||
* @param aRel [optional] only return URLs that with this rel value
|
||||
*/
|
||||
_getURLOfType: function SRCH_ENG__getURLOfType(aType) {
|
||||
_getURLOfType: function SRCH_ENG__getURLOfType(aType, aRel) {
|
||||
for (var i = 0; i < this._urls.length; ++i) {
|
||||
if (this._urls[i].type == aType)
|
||||
if (this._urls[i].type == aType && (!aRel || this._urls[i]._hasRelation(aRel)))
|
||||
return this._urls[i];
|
||||
}
|
||||
|
||||
@ -1535,11 +1536,11 @@ Engine.prototype = {
|
||||
if (engineToUpdate._isInAppDir) {
|
||||
let oldUpdateURL = engineToUpdate._updateURL;
|
||||
let newUpdateURL = aEngine._updateURL;
|
||||
let oldSelfURL = engineToUpdate._getURLOfType(URLTYPE_OPENSEARCH);
|
||||
if (oldSelfURL && oldSelfURL._hasRelation("self")) {
|
||||
let oldSelfURL = engineToUpdate._getURLOfType(URLTYPE_OPENSEARCH, "self");
|
||||
if (oldSelfURL) {
|
||||
oldUpdateURL = oldSelfURL.template;
|
||||
let newSelfURL = aEngine._getURLOfType(URLTYPE_OPENSEARCH);
|
||||
if (!newSelfURL || !newSelfURL._hasRelation("self")) {
|
||||
let newSelfURL = aEngine._getURLOfType(URLTYPE_OPENSEARCH, "self");
|
||||
if (!newSelfURL) {
|
||||
LOG("_onLoad: updateURL missing in updated engine for " +
|
||||
aEngine.name + " aborted");
|
||||
onError();
|
||||
@ -1758,7 +1759,7 @@ Engine.prototype = {
|
||||
"Can't call _initFromMetaData on a readonly engine!",
|
||||
Cr.NS_ERROR_FAILURE);
|
||||
|
||||
this._urls.push(new EngineURL("text/html", aMethod, aTemplate));
|
||||
this._urls.push(new EngineURL(URLTYPE_SEARCH_HTML, aMethod, aTemplate));
|
||||
|
||||
this._name = aName;
|
||||
this.alias = aAlias;
|
||||
@ -2240,11 +2241,11 @@ Engine.prototype = {
|
||||
} else if (name != "")
|
||||
template += "&" + name + "=" + value;
|
||||
}
|
||||
url = new EngineURL("text/html", method, template);
|
||||
url = new EngineURL(URLTYPE_SEARCH_HTML, method, template);
|
||||
|
||||
} else if (method == "POST") {
|
||||
// Create the URL object and just add the parameters directly
|
||||
url = new EngineURL("text/html", method, template);
|
||||
url = new EngineURL(URLTYPE_SEARCH_HTML, method, template);
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
var name = inputs[i][0];
|
||||
var value = inputs[i][1];
|
||||
@ -2623,9 +2624,8 @@ Engine.prototype = {
|
||||
|
||||
get _hasUpdates() {
|
||||
// Whether or not the engine has an update URL
|
||||
let selfURL = this._getURLOfType(URLTYPE_OPENSEARCH);
|
||||
return !!(this._updateURL || this._iconUpdateURL || (selfURL &&
|
||||
selfURL._hasRelation("self")));
|
||||
let selfURL = this._getURLOfType(URLTYPE_OPENSEARCH, "self");
|
||||
return !!(this._updateURL || this._iconUpdateURL || selfURL);
|
||||
},
|
||||
|
||||
get name() {
|
||||
@ -2637,8 +2637,19 @@ Engine.prototype = {
|
||||
},
|
||||
|
||||
get searchForm() {
|
||||
// First look for a <Url rel="searchform">
|
||||
var searchFormURL = this._getURLOfType(URLTYPE_SEARCH_HTML, "searchform");
|
||||
if (searchFormURL) {
|
||||
let submission = searchFormURL.getSubmission("", this);
|
||||
|
||||
// If the rel=searchform URL is not type="get" (i.e. has postData),
|
||||
// ignore it, since we can only return a URL.
|
||||
if (!submission.postData)
|
||||
return submission.uri.spec;
|
||||
}
|
||||
|
||||
if (!this._searchForm) {
|
||||
// No searchForm specified in the engine definition file, use the prePath
|
||||
// No SearchForm specified in the engine definition file, use the prePath
|
||||
// (e.g. https://foo.com for https://foo.com/search.php?q=bar).
|
||||
var htmlUrl = this._getURLOfType(URLTYPE_SEARCH_HTML);
|
||||
ENSURE_WARN(htmlUrl, "Engine has no HTML URL!", Cr.NS_ERROR_UNEXPECTED);
|
||||
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
|
||||
<ShortName>engine-rel-searchform-post.xml</ShortName>
|
||||
<Url type="text/html" method="POST" template="http://engine-rel-searchform-post.xml/POST" rel="searchform"/>
|
||||
<SearchForm>http://engine-rel-searchform-post.xml/?search</SearchForm>
|
||||
</SearchPlugin>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
|
||||
<ShortName>engine-rel-searchform.xml</ShortName>
|
||||
<Url type="text/html" method="GET" template="http://engine-rel-searchform.xml/?search" rel="searchform"/>
|
||||
</SearchPlugin>
|
@ -0,0 +1,66 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* Tests that <Url rel="searchform"/> is properly recognized as a searchForm.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Components.utils.import("resource://testing-common/httpd.js");
|
||||
|
||||
let engines = [
|
||||
"engine-rel-searchform.xml",
|
||||
"engine-rel-searchform-post.xml",
|
||||
];
|
||||
|
||||
function search_observer(aSubject, aTopic, aData) {
|
||||
let engine = aSubject.QueryInterface(Ci.nsISearchEngine);
|
||||
do_print("Observer: " + aData + " for " + engine.name);
|
||||
|
||||
if (aData != "engine-added")
|
||||
return;
|
||||
|
||||
let idx = engines.indexOf(engine.name);
|
||||
if (idx < 0)
|
||||
// The engine is some other engine unrelated to the test, so ignore it.
|
||||
return;
|
||||
|
||||
// The final searchForm of the engine should be a URL whose domain is the
|
||||
// <ShortName> in the engine's XML and that has a ?search parameter. The
|
||||
// point of the ?search parameter is to avoid accidentally matching the value
|
||||
// returned as a last resort by Engine's searchForm getter, which is simply
|
||||
// the prePath of the engine's first HTML <Url>.
|
||||
do_check_eq(engine.searchForm, "http://" + engine.name + "/?search");
|
||||
|
||||
engines.splice(idx, 1);
|
||||
if (!engines.length)
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
removeMetadata();
|
||||
updateAppInfo();
|
||||
|
||||
let httpServer = new HttpServer();
|
||||
httpServer.start(-1);
|
||||
httpServer.registerDirectory("/", do_get_cwd());
|
||||
|
||||
do_register_cleanup(function cleanup() {
|
||||
httpServer.stop(function() {});
|
||||
Services.obs.removeObserver(search_observer, "browser-search-engine-modified");
|
||||
});
|
||||
|
||||
do_test_pending();
|
||||
Services.obs.addObserver(search_observer, "browser-search-engine-modified", false);
|
||||
|
||||
for (let basename of engines) {
|
||||
Services.search.addEngine("http://localhost:" +
|
||||
httpServer.identity.primaryPort +
|
||||
"/data/" + basename,
|
||||
Ci.nsISearchEngine.DATA_XML,
|
||||
null, false);
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@ support-files =
|
||||
data/engine.src
|
||||
data/engine.xml
|
||||
data/engine2.xml
|
||||
data/engine-rel-searchform.xml
|
||||
data/engine-rel-searchform-post.xml
|
||||
data/engineImages.xml
|
||||
data/ico-size-16x16-png.ico
|
||||
data/invalid-engine.xml
|
||||
@ -37,3 +39,4 @@ support-files =
|
||||
[test_sync.js]
|
||||
[test_sync_fallback.js]
|
||||
[test_sync_delay_fallback.js]
|
||||
[test_rel_searchform.js]
|
||||
|
@ -2936,8 +2936,12 @@ ObjectActor.prototype = {
|
||||
let previewers = DebuggerServer.ObjectActorPreviewers[this.obj.class] ||
|
||||
DebuggerServer.ObjectActorPreviewers.Object;
|
||||
for (let fn of previewers) {
|
||||
if (fn(this, g, raw)) {
|
||||
break;
|
||||
try {
|
||||
if (fn(this, g, raw)) {
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
DevToolsUtils.reportException("ObjectActor.prototype.grip previewer function", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user