mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge b2g-inbound to m-c. a=merge
This commit is contained in:
commit
94b920e24d
@ -89,10 +89,16 @@ let SystemAppProxy = {
|
||||
},
|
||||
|
||||
// Now deprecated, use sendCustomEvent with a custom event name
|
||||
dispatchEvent: function systemApp_sendChromeEvent(details, target) {
|
||||
dispatchEvent: function systemApp_dispatchEvent(details, target) {
|
||||
return this._sendCustomEvent('mozChromeEvent', details, false, target);
|
||||
},
|
||||
|
||||
dispatchKeyboardEvent: function systemApp_dispatchKeyboardEvent(type, details) {
|
||||
let content = this._frame ? this._frame.contentWindow : null;
|
||||
let e = new content.KeyboardEvent(type, details);
|
||||
content.dispatchEvent(e);
|
||||
},
|
||||
|
||||
// Listen for dom events on the system app
|
||||
addEventListener: function systemApp_addEventListener() {
|
||||
let content = this._frame ? this._frame.contentWindow : null;
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,11 +19,11 @@
|
||||
<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="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="a47dd04f8f66e42fd331711140f2c3e2fed0767d"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8b13bfc1d7d25cee4de55f332654fdba25b8460b"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -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="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,11 +19,11 @@
|
||||
<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="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="a47dd04f8f66e42fd331711140f2c3e2fed0767d"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8b13bfc1d7d25cee4de55f332654fdba25b8460b"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -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="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "769491b138582de92f5636413928de13268e1a63",
|
||||
"revision": "05f40074f94c58315e6c353089d31e8e20ea3c1a",
|
||||
"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="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
|
@ -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="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -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="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
|
@ -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="41b7be7c67167f367c3c4982ff08651d55455373"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="76e1789f081a811416c95e25213ca63976486af6"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -482,11 +482,11 @@ ResponsiveUI.prototype = {
|
||||
sleepButton.className = "devtools-responsiveui-sleep-button";
|
||||
sleepButton.setAttribute("top", 0);
|
||||
sleepButton.setAttribute("right", 0);
|
||||
sleepButton.addEventListener("mousedown", function() {
|
||||
SystemAppProxy.dispatchEvent({type: "sleep-button-press"});
|
||||
sleepButton.addEventListener("mousedown", () => {
|
||||
SystemAppProxy.dispatchKeyboardEvent("keydown", {key: "Power"});
|
||||
});
|
||||
sleepButton.addEventListener("mouseup", function() {
|
||||
SystemAppProxy.dispatchEvent({type: "sleep-button-release"});
|
||||
sleepButton.addEventListener("mouseup", () => {
|
||||
SystemAppProxy.dispatchKeyboardEvent("keyup", {key: "Power"});
|
||||
});
|
||||
this.stack.appendChild(sleepButton);
|
||||
|
||||
@ -497,20 +497,20 @@ ResponsiveUI.prototype = {
|
||||
|
||||
let volumeUp = this.chromeDoc.createElement("button");
|
||||
volumeUp.className = "devtools-responsiveui-volume-up-button";
|
||||
volumeUp.addEventListener("mousedown", function() {
|
||||
SystemAppProxy.dispatchEvent({type: "volume-up-button-press"});
|
||||
volumeUp.addEventListener("mousedown", () => {
|
||||
SystemAppProxy.dispatchKeyboardEvent("keydown", {key: "VolumeUp"});
|
||||
});
|
||||
volumeUp.addEventListener("mouseup", function() {
|
||||
SystemAppProxy.dispatchEvent({type: "volume-up-button-release"});
|
||||
volumeUp.addEventListener("mouseup", () => {
|
||||
SystemAppProxy.dispatchKeyboardEvent("keyup", {key: "VolumeUp"});
|
||||
});
|
||||
|
||||
let volumeDown = this.chromeDoc.createElement("button");
|
||||
volumeDown.className = "devtools-responsiveui-volume-down-button";
|
||||
volumeDown.addEventListener("mousedown", function() {
|
||||
SystemAppProxy.dispatchEvent({type: "volume-down-button-press"});
|
||||
volumeDown.addEventListener("mousedown", () => {
|
||||
SystemAppProxy.dispatchKeyboardEvent("keydown", {key: "VolumeDown"});
|
||||
});
|
||||
volumeDown.addEventListener("mouseup", function() {
|
||||
SystemAppProxy.dispatchEvent({type: "volume-down-button-release"});
|
||||
volumeDown.addEventListener("mouseup", () => {
|
||||
SystemAppProxy.dispatchKeyboardEvent("keyup", {key: "VolumeDown"});
|
||||
});
|
||||
|
||||
volumeButtons.appendChild(volumeUp);
|
||||
@ -524,11 +524,11 @@ ResponsiveUI.prototype = {
|
||||
|
||||
let homeButton = this.chromeDoc.createElement("toolbarbutton");
|
||||
homeButton.className = "devtools-responsiveui-toolbarbutton devtools-responsiveui-home-button";
|
||||
homeButton.addEventListener("mousedown", function() {
|
||||
SystemAppProxy.dispatchEvent({type: "home-button-press"});
|
||||
homeButton.addEventListener("mousedown", () => {
|
||||
SystemAppProxy.dispatchKeyboardEvent("keydown", {key: "Home"});
|
||||
});
|
||||
homeButton.addEventListener("mouseup", function() {
|
||||
SystemAppProxy.dispatchEvent({type: "home-button-release"});
|
||||
homeButton.addEventListener("mouseup", () => {
|
||||
SystemAppProxy.dispatchKeyboardEvent("keyup", {key: "Home"});
|
||||
});
|
||||
bottomToolbar.appendChild(homeButton);
|
||||
this.bottomToolbar = bottomToolbar;
|
||||
|
@ -19,6 +19,8 @@ using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static const TrackID TRACK_VIDEO = 2;
|
||||
|
||||
void
|
||||
FakeMediaStreamGraph::DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
|
||||
{
|
||||
@ -32,6 +34,7 @@ CameraPreviewMediaStream::CameraPreviewMediaStream(DOMMediaStream* aWrapper)
|
||||
, mInvalidatePending(0)
|
||||
, mDiscardedFrames(0)
|
||||
, mRateLimit(false)
|
||||
, mTrackCreated(false)
|
||||
{
|
||||
SetGraphImpl(MediaStreamGraph::GetInstance());
|
||||
mFakeMediaStreamGraph = new FakeMediaStreamGraph();
|
||||
@ -111,6 +114,24 @@ CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener)
|
||||
listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamListener::EVENT_REMOVED);
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::OnPreviewStateChange(bool aActive)
|
||||
{
|
||||
if (aActive) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mTrackCreated) {
|
||||
mTrackCreated = true;
|
||||
VideoSegment tmpSegment;
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
l->NotifyQueuedTrackChanges(mFakeMediaStreamGraph, TRACK_VIDEO, 0,
|
||||
MediaStreamListener::TRACK_EVENT_CREATED,
|
||||
tmpSegment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::Destroy()
|
||||
{
|
||||
|
@ -51,6 +51,7 @@ public:
|
||||
virtual void AddListener(MediaStreamListener* aListener) MOZ_OVERRIDE;
|
||||
virtual void RemoveListener(MediaStreamListener* aListener) MOZ_OVERRIDE;
|
||||
virtual void Destroy();
|
||||
void OnPreviewStateChange(bool aActive);
|
||||
|
||||
void Invalidate();
|
||||
|
||||
@ -67,6 +68,7 @@ protected:
|
||||
int32_t mInvalidatePending;
|
||||
uint32_t mDiscardedFrames;
|
||||
bool mRateLimit;
|
||||
bool mTrackCreated;
|
||||
nsRefPtr<FakeMediaStreamGraph> mFakeMediaStreamGraph;
|
||||
};
|
||||
|
||||
|
@ -291,7 +291,8 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
||||
mCurrentConfiguration = initialConfig.forget();
|
||||
|
||||
// Attach our DOM-facing media stream to our viewfinder stream.
|
||||
mStream = mInput;
|
||||
SetHintContents(HINT_CONTENTS_VIDEO);
|
||||
InitStreamCommon(mInput);
|
||||
MOZ_ASSERT(mWindow, "Shouldn't be created with a null window!");
|
||||
if (mWindow->GetExtantDoc()) {
|
||||
CombineWithPrincipal(mWindow->GetExtantDoc()->NodePrincipal());
|
||||
|
@ -140,6 +140,7 @@ DOMCameraControlListener::OnPreviewStateChange(PreviewState aState)
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid preview state");
|
||||
return;
|
||||
}
|
||||
mStream->OnPreviewStateChange(aState == kPreviewStarted);
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState));
|
||||
}
|
||||
|
||||
|
@ -109,18 +109,26 @@ var Camera = {
|
||||
var blob = e.data;
|
||||
var img = new Image();
|
||||
var test = this._currentTest;
|
||||
var onPreviewStateChange = function(e) {
|
||||
if (e.newState === 'started') {
|
||||
ok(true, "viewfinder is ready and playing after resume");
|
||||
Camera.cameraObj.removeEventListener('previewstatechange', onPreviewStateChange);
|
||||
Camera._testsCompleted++;
|
||||
if(Camera._testsCompleted == Camera._tests.length) {
|
||||
ok(true, "test finishing");
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
Camera.runTests();
|
||||
}
|
||||
}
|
||||
}
|
||||
Camera.cameraObj.addEventListener('previewstatechange', onPreviewStateChange);
|
||||
img.onload = function Imgsize() {
|
||||
ok(this.width == test.pictureSize.width, "The image taken has the width " +
|
||||
this.width + " pictureSize width = " + test.pictureSize.width);
|
||||
ok(this.height == test.pictureSize.height, "The image taken has the height " +
|
||||
this.height + " picturesize height = " + test.pictureSize.height);
|
||||
Camera._testsCompleted++;
|
||||
if(Camera._testsCompleted == Camera._tests.length) {
|
||||
ok(true, "test finishing");
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
Camera.runTests();
|
||||
}
|
||||
Camera.cameraObj.resumePreview();
|
||||
}
|
||||
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
|
||||
ok("image/" + test.fileFormat == blob.type, "Blob Type = " + blob.type);
|
||||
|
348
dom/icc/tests/marionette/head.js
Normal file
348
dom/icc/tests/marionette/head.js
Normal file
@ -0,0 +1,348 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
|
||||
|
||||
const PREF_KEY_RIL_DEBUGGING_ENABLED = "ril.debugging.enabled";
|
||||
|
||||
// The pin code hard coded in emulator is "0000".
|
||||
const DEFAULT_PIN = "0000";
|
||||
// The puk code hard coded in emulator is "12345678".
|
||||
const DEFAULT_PUK = "12345678";
|
||||
|
||||
// Emulate Promise.jsm semantics.
|
||||
Promise.defer = function() { return new Deferred(); }
|
||||
function Deferred() {
|
||||
this.promise = new Promise(function(resolve, reject) {
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
}.bind(this));
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
let _pendingEmulatorCmdCount = 0;
|
||||
|
||||
/**
|
||||
* Send emulator command with safe guard.
|
||||
*
|
||||
* We should only call |finish()| after all emulator command transactions
|
||||
* end, so here comes with the pending counter. Resolve when the emulator
|
||||
* gives positive response, and reject otherwise.
|
||||
*
|
||||
* Fulfill params:
|
||||
* result -- an array of emulator response lines.
|
||||
* Reject params:
|
||||
* result -- an array of emulator response lines.
|
||||
*
|
||||
* @param aCommand
|
||||
* A string command to be passed to emulator through its telnet console.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function runEmulatorCmdSafe(aCommand) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
++_pendingEmulatorCmdCount;
|
||||
runEmulatorCmd(aCommand, function(aResult) {
|
||||
--_pendingEmulatorCmdCount;
|
||||
|
||||
ok(true, "Emulator response: " + JSON.stringify(aResult));
|
||||
if (Array.isArray(aResult) &&
|
||||
aResult[aResult.length - 1] === "OK") {
|
||||
deferred.resolve(aResult);
|
||||
} else {
|
||||
deferred.reject(aResult);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
let workingFrame;
|
||||
let iccManager;
|
||||
|
||||
/**
|
||||
* Push required permissions and test if
|
||||
* |navigator.mozIccManager| exists. Resolve if it does,
|
||||
* reject otherwise.
|
||||
*
|
||||
* Fulfill params:
|
||||
* iccManager -- an reference to navigator.mozIccManager.
|
||||
*
|
||||
* Reject params: (none)
|
||||
*
|
||||
* @param aAdditonalPermissions [optional]
|
||||
* An array of permission strings other than "mobileconnection" to be
|
||||
* pushed. Default: empty string.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function ensureIccManager(aAdditionalPermissions) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
aAdditionalPermissions = aAdditionalPermissions || [];
|
||||
|
||||
if (aAdditionalPermissions.indexOf("mobileconnection") < 0) {
|
||||
aAdditionalPermissions.push("mobileconnection");
|
||||
}
|
||||
let permissions = [];
|
||||
for (let perm of aAdditionalPermissions) {
|
||||
permissions.push({ "type": perm, "allow": 1, "context": document });
|
||||
}
|
||||
|
||||
SpecialPowers.pushPermissions(permissions, function() {
|
||||
ok(true, "permissions pushed: " + JSON.stringify(permissions));
|
||||
|
||||
// Permission changes can't change existing Navigator.prototype
|
||||
// objects, so grab our objects from a new Navigator.
|
||||
workingFrame = document.createElement("iframe");
|
||||
workingFrame.addEventListener("load", function load() {
|
||||
workingFrame.removeEventListener("load", load);
|
||||
|
||||
iccManager = workingFrame.contentWindow.navigator.mozIccManager;
|
||||
|
||||
if (iccManager) {
|
||||
ok(true, "navigator.mozIccManager is instance of " + iccManager.constructor);
|
||||
} else {
|
||||
ok(true, "navigator.mozIccManager is undefined");
|
||||
}
|
||||
|
||||
if (iccManager instanceof MozIccManager) {
|
||||
deferred.resolve(iccManager);
|
||||
} else {
|
||||
deferred.reject();
|
||||
}
|
||||
});
|
||||
|
||||
document.body.appendChild(workingFrame);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MozIcc by IccId
|
||||
*
|
||||
* @param aIccId [optional]
|
||||
* Default: The first item of |aIccManager.iccIds|.
|
||||
*
|
||||
* @return A MozIcc.
|
||||
*/
|
||||
function getMozIcc(aIccId) {
|
||||
aIccId = aIccId || iccManager.iccIds[0];
|
||||
|
||||
if (!aIccId) {
|
||||
ok(true, "iccManager.iccIds[0] is undefined");
|
||||
return null;
|
||||
}
|
||||
|
||||
return iccManager.getIccById(aIccId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MozMobileConnection by ServiceId
|
||||
*
|
||||
* @param aServiceId [optional]
|
||||
* A numeric DSDS service id. Default: 0 if not indicated.
|
||||
*
|
||||
* @return A MozMobileConnection.
|
||||
*/
|
||||
function getMozMobileConnectionByServiceId(aServiceId) {
|
||||
aServiceId = aServiceId || 0;
|
||||
return workingFrame.contentWindow.navigator.mozMobileConnections[aServiceId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set radio enabling state.
|
||||
*
|
||||
* Resolve no matter the request succeeds or fails. Never reject.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
*
|
||||
* @param aEnabled
|
||||
* A boolean state.
|
||||
* @param aServiceId [optional]
|
||||
* A numeric DSDS service id.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function setRadioEnabled(aEnabled, aServiceId) {
|
||||
return getMozMobileConnectionByServiceId(aServiceId).setRadioEnabled(aEnabled)
|
||||
.then(() => {
|
||||
ok(true, "setRadioEnabled " + aEnabled + " on " + aServiceId + " success.");
|
||||
}, (aError) => {
|
||||
ok(false, "setRadioEnabled " + aEnabled + " on " + aServiceId + " " +
|
||||
aError.name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for one named event.
|
||||
*
|
||||
* Resolve if that named event occurs. Never reject.
|
||||
*
|
||||
* Fulfill params: the DOMEvent passed.
|
||||
*
|
||||
* @param aEventTarget
|
||||
* An EventTarget object.
|
||||
* @param aEventName
|
||||
* A string event name.
|
||||
* @param aMatchFun [optional]
|
||||
* A matching function returns true or false to filter the event.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForTargetEvent(aEventTarget, aEventName, aMatchFun) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
aEventTarget.addEventListener(aEventName, function onevent(aEvent) {
|
||||
if (!aMatchFun || aMatchFun(aEvent)) {
|
||||
aEventTarget.removeEventListener(aEventName, onevent);
|
||||
ok(true, "Event '" + aEventName + "' got.");
|
||||
deferred.resolve(aEvent);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set radio enabling state and wait for "radiostatechange" event.
|
||||
*
|
||||
* Resolve if radio state changed to the expected one. Never reject.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
*
|
||||
* @param aEnabled
|
||||
* A boolean state.
|
||||
* @param aServiceId [optional]
|
||||
* A numeric DSDS service id. Default: the one indicated in
|
||||
* start*TestCommon() or 0 if not indicated.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function setRadioEnabledAndWait(aEnabled, aServiceId) {
|
||||
let mobileConn = getMozMobileConnectionByServiceId(aServiceId);
|
||||
let promises = [];
|
||||
|
||||
promises.push(waitForTargetEvent(mobileConn, "radiostatechange", function() {
|
||||
// To ignore some transient states, we only resolve that deferred promise
|
||||
// when |radioState| equals to the expected one.
|
||||
return mobileConn.radioState === (aEnabled ? "enabled" : "disabled");
|
||||
}));
|
||||
promises.push(setRadioEnabled(aEnabled, aServiceId));
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart radio and wait card state changes to expected one.
|
||||
*
|
||||
* Resolve if card state changed to the expected one. Never reject.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
*
|
||||
* @param aCardState
|
||||
* Expected card state.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function restartRadioAndWait(aCardState) {
|
||||
return setRadioEnabledAndWait(false).then(() => {
|
||||
let promises = [];
|
||||
|
||||
promises.push(waitForTargetEvent(iccManager, "iccdetected")
|
||||
.then((aEvent) => {
|
||||
let icc = getMozIcc(aEvent.iccId);
|
||||
if (icc.cardState !== aCardState) {
|
||||
return waitForTargetEvent(icc, "cardstatechange", function() {
|
||||
return icc.cardState === aCardState;
|
||||
});
|
||||
}
|
||||
}));
|
||||
promises.push(setRadioEnabledAndWait(true));
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable PIN-lock.
|
||||
*
|
||||
* Fulfill params: (none)
|
||||
* Reject params:
|
||||
* An object contains error name and remaining retry count.
|
||||
* @see IccCardLockError
|
||||
*
|
||||
* @param aIcc
|
||||
* A MozIcc object.
|
||||
* @param aEnabled
|
||||
* A boolean state.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function setPinLockEnabled(aIcc, aEnabled) {
|
||||
let options = {
|
||||
lockType: "pin",
|
||||
enabled: aEnabled,
|
||||
pin: DEFAULT_PIN
|
||||
};
|
||||
|
||||
return aIcc.setCardLock(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for pending emulator transactions and call |finish()|.
|
||||
*/
|
||||
function cleanUp() {
|
||||
// Use ok here so that we have at least one test run.
|
||||
ok(true, ":: CLEANING UP ::");
|
||||
|
||||
waitFor(finish, function() {
|
||||
return _pendingEmulatorCmdCount === 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic test routine helper for icc tests.
|
||||
*
|
||||
* This helper does nothing but clean-ups.
|
||||
*
|
||||
* @param aTestCaseMain
|
||||
* A function that takes no parameter.
|
||||
*/
|
||||
function startTestBase(aTestCaseMain) {
|
||||
// Turn on debugging pref.
|
||||
let debugPref = SpecialPowers.getBoolPref(PREF_KEY_RIL_DEBUGGING_ENABLED);
|
||||
SpecialPowers.setBoolPref(PREF_KEY_RIL_DEBUGGING_ENABLED, true);
|
||||
|
||||
Promise.resolve()
|
||||
.then(aTestCaseMain)
|
||||
.catch((aError) => {
|
||||
ok(false, "promise rejects during test: " + aError);
|
||||
})
|
||||
.then(() => {
|
||||
// Restore debugging pref.
|
||||
SpecialPowers.setBoolPref(PREF_KEY_RIL_DEBUGGING_ENABLED, debugPref);
|
||||
cleanUp();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Common test routine helper for icc tests.
|
||||
*
|
||||
* This function ensures global variable |iccManager| and |icc| is available
|
||||
* during the process and performs clean-ups as well.
|
||||
*
|
||||
* @param aTestCaseMain
|
||||
* A function that takes one parameter -- icc.
|
||||
* @param aAdditonalPermissions [optional]
|
||||
* An array of permission strings other than "mobileconnection" to be
|
||||
* pushed. Default: empty string..
|
||||
*/
|
||||
function startTestCommon(aTestCaseMain, aAdditionalPermissions) {
|
||||
startTestBase(function() {
|
||||
return ensureIccManager(aAdditionalPermissions)
|
||||
.then(aTestCaseMain);
|
||||
});
|
||||
}
|
@ -5,7 +5,11 @@ qemu = true
|
||||
|
||||
[test_stk_proactive_command.js]
|
||||
[test_icc_contact.js]
|
||||
[test_icc_card_lock.js]
|
||||
[test_icc_card_lock_get_retry_count.js]
|
||||
[test_icc_card_lock_change_pin.js]
|
||||
[test_icc_card_lock_enable_pin.js]
|
||||
[test_icc_card_lock_unlock_pin.js]
|
||||
[test_icc_card_lock_unlock_puk.js]
|
||||
[test_icc_card_state.js]
|
||||
[test_icc_info.js]
|
||||
[test_stk_refresh.js]
|
||||
|
@ -1,126 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
MARIONETTE_HEAD_JS = "icc_header.js";
|
||||
|
||||
/* Test PIN code changes fail */
|
||||
taskHelper.push(function testPinChangeFailed() {
|
||||
// The default pin is '0000' in emulator
|
||||
let request = icc.setCardLock(
|
||||
{lockType: "pin",
|
||||
pin: "1111",
|
||||
newPin: "0000"});
|
||||
|
||||
ok(request instanceof DOMRequest,
|
||||
"request instanceof " + request.constructor);
|
||||
|
||||
request.onerror = function onerror() {
|
||||
is(request.error.name, "IncorrectPassword");
|
||||
// The default pin retries is 3, failed once becomes to 2
|
||||
is(request.error.retryCount, 2);
|
||||
|
||||
// Reset pin retries by passing correct pin code.
|
||||
let resetRequest = icc.setCardLock(
|
||||
{lockType: "pin",
|
||||
pin: "0000",
|
||||
newPin: "0000"});
|
||||
|
||||
resetRequest.onsuccess = function onsuccess() {
|
||||
taskHelper.runNext();
|
||||
};
|
||||
|
||||
resetRequest.onerror = function onerror() {
|
||||
ok(false, "Reset pin retries got error: " + request.error.name);
|
||||
taskHelper.runNext();
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
/* Test PIN code changes success */
|
||||
taskHelper.push(function testPinChangeSuccess() {
|
||||
// The default pin is '0000' in emulator
|
||||
let request = icc.setCardLock(
|
||||
{lockType: "pin",
|
||||
pin: "0000",
|
||||
newPin: "0000"});
|
||||
|
||||
ok(request instanceof DOMRequest,
|
||||
"request instanceof " + request.constructor);
|
||||
|
||||
request.onerror = function onerror() {
|
||||
ok(false, "Should not fail, got error: " + request.error.name);
|
||||
|
||||
taskHelper.runNext();
|
||||
};
|
||||
|
||||
request.onsuccess = function onsuccess() {
|
||||
taskHelper.runNext();
|
||||
};
|
||||
});
|
||||
|
||||
/* Read PIN-lock retry count */
|
||||
taskHelper.push(function testPinCardLockRetryCount() {
|
||||
let request = icc.getCardLockRetryCount('pin');
|
||||
|
||||
ok(request instanceof DOMRequest,
|
||||
'request instanceof ' + request.constructor);
|
||||
|
||||
request.onsuccess = function onsuccess() {
|
||||
ok(request.result.retryCount >= 0,
|
||||
'retryCount is ' + request.result.retryCount);
|
||||
taskHelper.runNext();
|
||||
};
|
||||
request.onerror = function onerror() {
|
||||
// The operation is optional any might not be supported for all
|
||||
// all locks. In this case, we generate 'NotSupportedError' for
|
||||
// the valid lock types.
|
||||
is(request.error.name, 'RequestNotSupported',
|
||||
'error name is ' + request.error.name);
|
||||
taskHelper.runNext();
|
||||
};
|
||||
});
|
||||
|
||||
/* Read PUK-lock retry count */
|
||||
taskHelper.push(function testPukCardLockRetryCount() {
|
||||
let request = icc.getCardLockRetryCount('puk');
|
||||
|
||||
ok(request instanceof DOMRequest,
|
||||
'request instanceof ' + request.constructor);
|
||||
|
||||
request.onsuccess = function onsuccess() {
|
||||
ok(request.result.retryCount >= 0,
|
||||
'retryCount is ' + request.result.retryCount);
|
||||
taskHelper.runNext();
|
||||
};
|
||||
request.onerror = function onerror() {
|
||||
// The operation is optional any might not be supported for all
|
||||
// all locks. In this case, we generate 'NotSupportedError' for
|
||||
// the valid lock types.
|
||||
is(request.error.name, 'RequestNotSupported',
|
||||
'error name is ' + request.error.name);
|
||||
taskHelper.runNext();
|
||||
};
|
||||
});
|
||||
|
||||
/* Read lock retry count for an invalid entries */
|
||||
taskHelper.push(function testInvalidCardLockRetryCount() {
|
||||
let request = icc.getCardLockRetryCount('invalid-lock-type');
|
||||
|
||||
ok(request instanceof DOMRequest,
|
||||
'request instanceof ' + request.constructor);
|
||||
|
||||
request.onsuccess = function onsuccess() {
|
||||
ok(false,
|
||||
'request should never return success for an invalid lock type');
|
||||
taskHelper.runNext();
|
||||
};
|
||||
request.onerror = function onerror() {
|
||||
is(request.error.name, 'GenericFailure',
|
||||
'error name is ' + request.error.name);
|
||||
taskHelper.runNext();
|
||||
};
|
||||
});
|
||||
|
||||
// Start test
|
||||
taskHelper.runNext();
|
45
dom/icc/tests/marionette/test_icc_card_lock_change_pin.js
Normal file
45
dom/icc/tests/marionette/test_icc_card_lock_change_pin.js
Normal file
@ -0,0 +1,45 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
const LOCK_TYPE = "pin";
|
||||
|
||||
function testChangePin(aIcc, aPin, aNewPin, aErrorName, aRetryCount) {
|
||||
log("testChangePin for pin=" + aPin + " and newPin=" + aNewPin);
|
||||
return aIcc.setCardLock({ lockType: LOCK_TYPE, pin: aPin, newPin: aNewPin })
|
||||
.then((aResult) => {
|
||||
if (aErrorName) {
|
||||
ok(false, "changing pin should not success");
|
||||
}
|
||||
}, (aError) => {
|
||||
if (!aErrorName) {
|
||||
ok(false, "changing pin should not fail");
|
||||
return;
|
||||
}
|
||||
|
||||
// check the request error.
|
||||
is(aError.name, aErrorName, "error.name");
|
||||
is(aError.retryCount, aRetryCount, "error.retryCount");
|
||||
});
|
||||
}
|
||||
|
||||
// Start tests
|
||||
startTestCommon(function() {
|
||||
let icc = getMozIcc();
|
||||
let retryCount;
|
||||
|
||||
return icc.getCardLockRetryCount(LOCK_TYPE)
|
||||
// Get current PIN-lock retry count.
|
||||
.then((aResult) => {
|
||||
retryCount = aResult.retryCount;
|
||||
ok(true, LOCK_TYPE + " retryCount is " + retryCount);
|
||||
})
|
||||
// Test PIN code changes fail.
|
||||
// The retry count should be decreased by 1.
|
||||
.then(() => testChangePin(icc, "1111", DEFAULT_PIN, "IncorrectPassword",
|
||||
retryCount - 1))
|
||||
// Test PIN code changes success. This will reset the retry count.
|
||||
.then(() => testChangePin(icc, DEFAULT_PIN, DEFAULT_PIN));
|
||||
});
|
59
dom/icc/tests/marionette/test_icc_card_lock_enable_pin.js
Normal file
59
dom/icc/tests/marionette/test_icc_card_lock_enable_pin.js
Normal file
@ -0,0 +1,59 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
function setCardLockAndCheck(aIcc, aLockType, aPin, aEnabled, aErrorName,
|
||||
aRetryCount) {
|
||||
let options = {
|
||||
lockType: aLockType,
|
||||
enabled: aEnabled,
|
||||
pin: aPin
|
||||
};
|
||||
|
||||
return aIcc.setCardLock(options)
|
||||
.then((aResult) => {
|
||||
if (aErrorName) {
|
||||
ok(false, "setting pin should not success");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check lock state.
|
||||
return aIcc.getCardLock(aLockType)
|
||||
.then((aResult) => {
|
||||
is(aResult.enabled, aEnabled, "result.enabled");
|
||||
});
|
||||
}, (aError) => {
|
||||
if (!aErrorName) {
|
||||
ok(false, "setting pin should not fail");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the request error.
|
||||
is(aError.name, aErrorName, "error.name");
|
||||
is(aError.retryCount, aRetryCount, "error.retryCount");
|
||||
});
|
||||
}
|
||||
|
||||
// Start tests
|
||||
startTestCommon(function() {
|
||||
let icc = getMozIcc();
|
||||
let lockType = "pin";
|
||||
let retryCount;
|
||||
|
||||
return icc.getCardLockRetryCount(lockType)
|
||||
// Get current PIN-lock retry count.
|
||||
.then((aResult) => {
|
||||
retryCount = aResult.retryCount;
|
||||
ok(true, lockType + " retryCount is " + retryCount);
|
||||
})
|
||||
// Test fail to enable PIN-lock by passing wrong pin.
|
||||
// The retry count should be decreased by 1.
|
||||
.then(() => setCardLockAndCheck(icc, lockType, "1111", true,
|
||||
"IncorrectPassword", retryCount -1))
|
||||
// Test enabling PIN-lock.
|
||||
.then(() => setCardLockAndCheck(icc, lockType, DEFAULT_PIN, true))
|
||||
// Restore pin state.
|
||||
.then(() => setCardLockAndCheck(icc, lockType, DEFAULT_PIN, false));
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
function testGetCardLockRetryCount(aIcc, aLockType, aRetryCount) {
|
||||
log("testGetCardLockRetryCount for " + aLockType);
|
||||
return aIcc.getCardLockRetryCount(aLockType)
|
||||
.then((aResult) => {
|
||||
if (!aRetryCount) {
|
||||
ok(false, "getCardLockRetryCount(" + aLockType + ") should not success");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the request result.
|
||||
is(aResult.retryCount, aRetryCount, "result.retryCount");
|
||||
}, (aError) => {
|
||||
if (aRetryCount) {
|
||||
ok(false, "getCardLockRetryCount(" + aLockType + ") should not fail");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the error.
|
||||
is(aError.name, "GenericFailure", "error.name");
|
||||
});
|
||||
}
|
||||
|
||||
// Start tests
|
||||
startTestCommon(function() {
|
||||
let icc = getMozIcc();
|
||||
|
||||
// Read PIN-lock retry count.
|
||||
// The default PIN-lock retry count hard coded in emulator is 3.
|
||||
return testGetCardLockRetryCount(icc, "pin", 3)
|
||||
// Read PUK-lock retry count.
|
||||
// The default PUK-lock retry count hard coded in emulator is 6.
|
||||
.then(() => testGetCardLockRetryCount(icc, "puk", 6))
|
||||
// Read lock retry count for an invalid entries.
|
||||
.then(() => testGetCardLockRetryCount(icc, "invalid-lock-type"));
|
||||
});
|
68
dom/icc/tests/marionette/test_icc_card_lock_unlock_pin.js
Normal file
68
dom/icc/tests/marionette/test_icc_card_lock_unlock_pin.js
Normal file
@ -0,0 +1,68 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
function testUnlockPin(aIcc, aPin, aErrorName, aRetryCount) {
|
||||
log("testUnlockPin with pin=" + aPin);
|
||||
|
||||
return aIcc.unlockCardLock({ lockType: "pin", pin: aPin })
|
||||
.then((aResult) => {
|
||||
if (aErrorName) {
|
||||
ok(false, "unlocking pin should not success");
|
||||
}
|
||||
}, (aError) => {
|
||||
if (!aErrorName) {
|
||||
ok(false, "unlocking pin should not fail");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the request error.
|
||||
is(aError.name, aErrorName, "error.name");
|
||||
is(aError.retryCount, aRetryCount, "error.retryCount");
|
||||
});
|
||||
}
|
||||
|
||||
function testUnlockPinAndWait(aIcc, aPin, aCardState) {
|
||||
log("testUnlockPin with pin=" + aPin + ", and wait cardState changes to '" +
|
||||
aCardState + "'");
|
||||
|
||||
let promises = [];
|
||||
|
||||
promises.push(waitForTargetEvent(aIcc, "cardstatechange", function() {
|
||||
return aIcc.cardState === aCardState;
|
||||
}));
|
||||
promises.push(testUnlockPin(aIcc, aPin));
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
// Start tests
|
||||
startTestCommon(function() {
|
||||
let icc = getMozIcc();
|
||||
let retryCount;
|
||||
|
||||
// Enable PIN-lock.
|
||||
return setPinLockEnabled(icc, true)
|
||||
// Reset card state to "pinRequired" by restarting radio
|
||||
.then(() => restartRadioAndWait("pinRequired"))
|
||||
.then(() => { icc = getMozIcc(); })
|
||||
|
||||
// Get current PIN-lock retry count.
|
||||
.then(() => icc.getCardLockRetryCount("pin"))
|
||||
.then((aResult) => {
|
||||
retryCount = aResult.retryCount;
|
||||
ok(true, "pin retryCount is " + retryCount);
|
||||
})
|
||||
|
||||
// Test fail to unlock PIN-lock.
|
||||
// The retry count should be decreased by 1.
|
||||
.then(() => testUnlockPin(icc, "1111", "IncorrectPassword", retryCount - 1))
|
||||
|
||||
// Test success to unlock PIN-lock.
|
||||
.then(() => testUnlockPinAndWait(icc, DEFAULT_PIN, "ready"))
|
||||
|
||||
// Restore pin state.
|
||||
.then(() => setPinLockEnabled(icc, false));
|
||||
});
|
97
dom/icc/tests/marionette/test_icc_card_lock_unlock_puk.js
Normal file
97
dom/icc/tests/marionette/test_icc_card_lock_unlock_puk.js
Normal file
@ -0,0 +1,97 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
function passingWrongPinAndWait(aIcc) {
|
||||
return aIcc.getCardLockRetryCount("pin").then((aResult) => {
|
||||
let promises = [];
|
||||
let retryCount = aResult.retryCount;
|
||||
|
||||
ok(true, "pin retryCount is " + retryCount);
|
||||
|
||||
promises.push(waitForTargetEvent(aIcc, "cardstatechange", function() {
|
||||
return aIcc.cardState === "pukRequired";
|
||||
}));
|
||||
|
||||
for (let i = 0; i < retryCount; i++) {
|
||||
promises.push(aIcc.unlockCardLock({ lockType: "pin", pin: "1111" })
|
||||
.then(() => {
|
||||
ok(false, "unlocking pin should not success");
|
||||
}, function reject(aRetryCount, aError) {
|
||||
// check the request error.
|
||||
is(aError.name, "IncorrectPassword", "error.name");
|
||||
is(aError.retryCount, aRetryCount, "error.retryCount");
|
||||
}.bind(null, retryCount - i - 1)));
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
function testUnlockPuk(aIcc, aPuk, aNewPin, aErrorName, aRetryCount) {
|
||||
log("testUnlockPuk with puk=" + aPuk + " and newPin=" + aNewPin);
|
||||
|
||||
return aIcc.unlockCardLock({ lockType: "puk", puk: aPuk, newPin: aNewPin })
|
||||
.then((aResult) => {
|
||||
if (aErrorName) {
|
||||
ok(false, "unlocking puk should not success");
|
||||
}
|
||||
}, (aError) => {
|
||||
if (!aErrorName) {
|
||||
ok(false, "unlocking puk should not fail");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the request error.
|
||||
is(aError.name, aErrorName, "error.name");
|
||||
is(aError.retryCount, aRetryCount, "error.retryCount");
|
||||
});
|
||||
}
|
||||
|
||||
function testUnlockPukAndWait(aIcc, aPuk, aNewPin, aCardState) {
|
||||
log("testUnlockPuk with puk=" + aPuk + "/newPin=" + aNewPin +
|
||||
", and wait card state changes to '" + aCardState + "'");
|
||||
|
||||
let promises = [];
|
||||
|
||||
promises.push(waitForTargetEvent(aIcc, "cardstatechange", function() {
|
||||
return aIcc.cardState === aCardState;
|
||||
}));
|
||||
promises.push(testUnlockPuk(aIcc, aPuk, aNewPin));
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
// Start tests
|
||||
startTestCommon(function() {
|
||||
let icc = getMozIcc();
|
||||
let retryCount;
|
||||
|
||||
// Enable pin lock.
|
||||
return setPinLockEnabled(icc, true)
|
||||
// Reset card state to "pinRequired" by restarting radio
|
||||
.then(() => restartRadioAndWait("pinRequired"))
|
||||
.then(() => { icc = getMozIcc() })
|
||||
// Switch cardState to "pukRequired" by entering wrong pin code.
|
||||
.then(() => passingWrongPinAndWait(icc))
|
||||
|
||||
// Get current PUK-lock retry count.
|
||||
.then(() => icc.getCardLockRetryCount("puk"))
|
||||
.then((aResult) => {
|
||||
retryCount = aResult.retryCount;
|
||||
ok(true, "puk retryCount is " + retryCount);
|
||||
})
|
||||
|
||||
// Test unlock PUK code fail.
|
||||
// The retry count should be decreased by 1.
|
||||
.then(() => testUnlockPuk(icc, "11111111", DEFAULT_PIN, "IncorrectPassword",
|
||||
retryCount - 1))
|
||||
|
||||
// Test unlock PUK code success.
|
||||
.then(() => testUnlockPukAndWait(icc, DEFAULT_PUK, DEFAULT_PIN, "ready"))
|
||||
|
||||
// Restore pin state.
|
||||
.then(() => setPinLockEnabled(icc, false));
|
||||
});
|
@ -28,8 +28,6 @@ GetNetUtilsLibHandle()
|
||||
return sNetUtilsLib;
|
||||
}
|
||||
|
||||
mozilla::Mutex NetUtils::sIfcMutex("NetUtils::sIfcMutex");
|
||||
|
||||
// static
|
||||
void*
|
||||
NetUtils::GetSharedLibrary()
|
||||
@ -70,14 +68,12 @@ NetUtils::NetUtils()
|
||||
int32_t NetUtils::do_ifc_enable(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_enable)
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_enable(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_disable(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_disable)
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_disable(ifname);
|
||||
}
|
||||
|
||||
@ -89,7 +85,6 @@ int32_t NetUtils::do_ifc_configure(const char *ifname,
|
||||
in_addr_t dns2)
|
||||
{
|
||||
USE_DLFUNC(ifc_configure)
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
int32_t ret = ifc_configure(ifname, address, prefixLength, gateway, dns1, dns2);
|
||||
return ret;
|
||||
}
|
||||
@ -98,7 +93,6 @@ int32_t NetUtils::do_ifc_reset_connections(const char *ifname,
|
||||
const int32_t resetMask)
|
||||
{
|
||||
USE_DLFUNC(ifc_reset_connections)
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_reset_connections(ifname, resetMask);
|
||||
}
|
||||
|
||||
@ -106,7 +100,6 @@ int32_t NetUtils::do_ifc_set_default_route(const char *ifname,
|
||||
in_addr_t gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_set_default_route)
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_set_default_route(ifname, gateway);
|
||||
}
|
||||
|
||||
@ -116,7 +109,6 @@ int32_t NetUtils::do_ifc_add_route(const char *ifname,
|
||||
const char *gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_add_route)
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_add_route(ifname, dst, prefixLength, gateway);
|
||||
}
|
||||
|
||||
@ -126,21 +118,18 @@ int32_t NetUtils::do_ifc_remove_route(const char *ifname,
|
||||
const char *gateway)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_route)
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_remove_route(ifname, dst, prefixLength, gateway);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_remove_host_routes(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_host_routes)
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_remove_host_routes(ifname);
|
||||
}
|
||||
|
||||
int32_t NetUtils::do_ifc_remove_default_route(const char *ifname)
|
||||
{
|
||||
USE_DLFUNC(ifc_remove_default_route)
|
||||
mozilla::MutexAutoLock lock(sIfcMutex);
|
||||
return ifc_remove_default_route(ifname);
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
#define NetUtils_h
|
||||
|
||||
#include "arpa/inet.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
// Copied from ifc.h
|
||||
#define RESET_IPV4_ADDRESSES 0x01
|
||||
@ -59,9 +58,6 @@ public:
|
||||
char* vendorinfo);
|
||||
|
||||
static int32_t SdkVersion();
|
||||
|
||||
private:
|
||||
static mozilla::Mutex sIfcMutex;
|
||||
};
|
||||
|
||||
// Defines a function type with the right arguments and return type.
|
||||
|
@ -54,6 +54,11 @@ ChromeNotifications.prototype = {
|
||||
} catch(e) {
|
||||
behavior = undefined;
|
||||
}
|
||||
|
||||
if (behavior && behavior.showOnlyOnce === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
appNotifier.showAppNotification(
|
||||
notification.icon,
|
||||
notification.title,
|
||||
|
@ -86,6 +86,35 @@
|
||||
});
|
||||
},
|
||||
|
||||
function (done) {
|
||||
info("Sending one non-resendable notification");
|
||||
var behavior = {
|
||||
showOnlyOnce: true
|
||||
};
|
||||
var notif = new Notification("title", { mozbehavior: behavior });
|
||||
ok(notif, "Notification object is valid");
|
||||
notifications.push(notif);
|
||||
|
||||
var promise = Notification.get();
|
||||
promise.then(function (notifications) {
|
||||
is(notifications.length, 1, "one notification has been sent");
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
function (done) {
|
||||
info("Trying to resend non-resendable notification");
|
||||
var notif = notifications.pop();
|
||||
notif.onclose = function() {
|
||||
done();
|
||||
};
|
||||
|
||||
navigator.mozChromeNotifications.mozResendAllNotifications(function(number) {
|
||||
is(number, 0, "One notification not resent");
|
||||
notif.close();
|
||||
});
|
||||
},
|
||||
|
||||
function (done) {
|
||||
info("Sending two notifications, closing one");
|
||||
var notif1 = new Notification("title1");
|
||||
|
@ -72,6 +72,7 @@ dictionary GetNotificationOptions {
|
||||
dictionary NotificationBehavior {
|
||||
boolean noscreen = false;
|
||||
boolean noclear = false;
|
||||
boolean showOnlyOnce = false;
|
||||
DOMString soundFile = "";
|
||||
sequence<unsigned long> vibrationPattern;
|
||||
};
|
||||
|
@ -246,6 +246,9 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
|
||||
if (!aMetrics.IsScrollable()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (gfxPrefs::LayoutEventRegionsEnabled() && aLayer.IsScrollInfoLayer()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
|
||||
if (!(state && state->mController.get())) {
|
||||
|
@ -143,10 +143,11 @@ public:
|
||||
* The following values may be returned by this function:
|
||||
* nsEventStatus_eConsumeNoDefault is returned to indicate the
|
||||
* caller should discard the event with extreme prejudice.
|
||||
* Currently this is only returned if the APZ determines that
|
||||
* something is in overscroll and the event should be ignored entirely.
|
||||
* There may be other scenarios where this return code might be used in
|
||||
* the future.
|
||||
* Currently this is only returned if the APZ determines that something is
|
||||
* in overscroll and the event should be ignored entirely, or if the input
|
||||
* event is part of a extended gesture like flywheel scrolling, and gets
|
||||
* consumed within the APZ code. There may be other scenarios where this
|
||||
* return code might be used in the future.
|
||||
* nsEventStatus_eIgnore is returned to indicate that the APZ code didn't
|
||||
* use this event. This might be because it was directed at a point on
|
||||
* the screen where there was no APZ, or because the thing the user was
|
||||
|
@ -84,7 +84,7 @@ TouchBlockState::TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTarget
|
||||
, mPreventDefault(false)
|
||||
, mContentResponded(false)
|
||||
, mContentResponseTimerExpired(false)
|
||||
, mSingleTapDisallowed(false)
|
||||
, mDuringFastMotion(false)
|
||||
, mSingleTapOccurred(false)
|
||||
{
|
||||
TBS_LOG("Creating %p\n", this);
|
||||
@ -164,17 +164,24 @@ TouchBlockState::IsDefaultPrevented() const
|
||||
}
|
||||
|
||||
void
|
||||
TouchBlockState::DisallowSingleTap()
|
||||
TouchBlockState::SetDuringFastMotion()
|
||||
{
|
||||
TBS_LOG("%p disallowing single-tap\n", this);
|
||||
mSingleTapDisallowed = true;
|
||||
TBS_LOG("%p setting fast-motion flag\n", this);
|
||||
mDuringFastMotion = true;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::IsDuringFastMotion() const
|
||||
{
|
||||
return mDuringFastMotion;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::SetSingleTapOccurred()
|
||||
{
|
||||
TBS_LOG("%p attempting to set single-tap occurred; disallowed=%d\n", this, mSingleTapDisallowed);
|
||||
if (!mSingleTapDisallowed) {
|
||||
TBS_LOG("%p attempting to set single-tap occurred; disallowed=%d\n",
|
||||
this, mDuringFastMotion);
|
||||
if (!mDuringFastMotion) {
|
||||
mSingleTapOccurred = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -116,16 +116,24 @@ public:
|
||||
bool IsDefaultPrevented() const;
|
||||
|
||||
/**
|
||||
* Set a flag that disables setting the single-tap flag on this block.
|
||||
* Sets a flag that indicates this input block occurred while the APZ was
|
||||
* in a state of fast motion. This affects gestures that may be produced
|
||||
* from input events in this block.
|
||||
*/
|
||||
void DisallowSingleTap();
|
||||
void SetDuringFastMotion();
|
||||
/**
|
||||
* Set a flag that indicates that this touch block triggered a single tap event.
|
||||
* @return true iff DisallowSingleTap was not previously called.
|
||||
* @return true iff SetDuringFastMotion was called on this block.
|
||||
*/
|
||||
bool IsDuringFastMotion() const;
|
||||
/**
|
||||
* Set the single-tap-occurred flag that indicates that this touch block
|
||||
* triggered a single tap event.
|
||||
* @return true if the flag was set. This may not happen if, for example,
|
||||
* SetDuringFastMotion was previously called.
|
||||
*/
|
||||
bool SetSingleTapOccurred();
|
||||
/**
|
||||
* @return true iff SetSingleTapOccurred was previously called on this block.
|
||||
* @return true iff the single-tap-occurred flag is set on this block.
|
||||
*/
|
||||
bool SingleTapOccurred() const;
|
||||
|
||||
@ -171,7 +179,7 @@ private:
|
||||
bool mPreventDefault;
|
||||
bool mContentResponded;
|
||||
bool mContentResponseTimerExpired;
|
||||
bool mSingleTapDisallowed;
|
||||
bool mDuringFastMotion;
|
||||
bool mSingleTapOccurred;
|
||||
nsTArray<MultiTouchInput> mEvents;
|
||||
};
|
||||
|
@ -59,7 +59,7 @@ InputQueue::ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget,
|
||||
if (block->GetOverscrollHandoffChain()->HasFastMovingApzc()) {
|
||||
// If we're already in a fast fling, then we want the touch event to stop the fling
|
||||
// and to disallow the touch event from being used as part of a fling.
|
||||
block->DisallowSingleTap();
|
||||
block->SetDuringFastMotion();
|
||||
}
|
||||
block->GetOverscrollHandoffChain()->CancelAnimations();
|
||||
}
|
||||
@ -68,6 +68,9 @@ InputQueue::ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget,
|
||||
if (!gfxPrefs::LayoutEventRegionsEnabled()) {
|
||||
waitForMainThread |= aTarget->NeedToWaitForContent();
|
||||
}
|
||||
if (block->IsDuringFastMotion()) {
|
||||
waitForMainThread = false;
|
||||
}
|
||||
if (waitForMainThread) {
|
||||
// We either don't know for sure if aTarget is the right APZC, or we may
|
||||
// need to wait to give content the opportunity to prevent-default the
|
||||
@ -106,7 +109,9 @@ InputQueue::ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget,
|
||||
// XXX calling ArePointerEventsConsumable on |target| may be wrong here if
|
||||
// the target isn't confirmed and the real target turns out to be something
|
||||
// else. For now assume this is rare enough that it's not an issue.
|
||||
if (target && target->ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())) {
|
||||
if (block->IsDuringFastMotion()) {
|
||||
result = nsEventStatus_eConsumeNoDefault;
|
||||
} else if (target && target->ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())) {
|
||||
result = nsEventStatus_eConsumeDoDefault;
|
||||
}
|
||||
|
||||
|
@ -275,9 +275,9 @@ public:
|
||||
*/
|
||||
nsAutoCString mLog;
|
||||
|
||||
#define FLB_LOG_PAINTED_LAYER_DECISION(tld, ...) \
|
||||
tld->mLog.AppendPrintf("\t\t\t\t"); \
|
||||
tld->mLog.AppendPrintf(__VA_ARGS__);
|
||||
#define FLB_LOG_PAINTED_LAYER_DECISION(pld, ...) \
|
||||
pld->mLog.AppendPrintf("\t\t\t\t"); \
|
||||
pld->mLog.AppendPrintf(__VA_ARGS__);
|
||||
#else
|
||||
#define FLB_LOG_PAINTED_LAYER_DECISION(...)
|
||||
#endif
|
||||
@ -309,7 +309,7 @@ public:
|
||||
*/
|
||||
void AccumulateEventRegions(nsDisplayLayerEventRegions* aEventRegions)
|
||||
{
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating event regions %p against tld=%p\n", aEventRegions, this);
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating event regions %p against pld=%p\n", aEventRegions, this);
|
||||
|
||||
mHitRegion.Or(mHitRegion, aEventRegions->HitRegion());
|
||||
mMaybeHitRegion.Or(mMaybeHitRegion, aEventRegions->MaybeHitRegion());
|
||||
@ -2106,7 +2106,7 @@ ContainerState::PopPaintedLayerData()
|
||||
nsRefPtr<Layer> layer;
|
||||
nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer(mBuilder);
|
||||
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(data, "Selecting layer for tld=%p\n", data);
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(data, "Selecting layer for pld=%p\n", data);
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(data, " Solid=%i, hasImage=%i, canOptimizeAwayPaintedLayer=%i\n",
|
||||
data->mIsSolidColorInVisibleRegion, !!imageContainer,
|
||||
CanOptimizeAwayPaintedLayer(data, mLayerBuilder));
|
||||
@ -2347,7 +2347,7 @@ PaintedLayerData::Accumulate(ContainerState* aState,
|
||||
const nsIntRect& aDrawRect,
|
||||
const DisplayItemClip& aClip)
|
||||
{
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating dp=%s(%p), f=%p against tld=%p\n", aItem->Name(), aItem, aItem->Frame(), this);
|
||||
FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating dp=%s(%p), f=%p against pld=%p\n", aItem->Name(), aItem, aItem->Frame(), this);
|
||||
|
||||
bool snap;
|
||||
nsRect itemBounds = aItem->GetBounds(aState->mBuilder, &snap);
|
||||
|
Loading…
Reference in New Issue
Block a user