mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound
This commit is contained in:
commit
e6779db575
@ -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="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
|
||||
|
@ -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="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="65fba428f8d76336b33ddd9e15900357953600ba">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
@ -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="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<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="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
@ -120,7 +120,7 @@
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
|
||||
<project name="device/qcom/common" path="device/qcom/common" revision="34ed8345250bb97262d70a052217a92e83444ede"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="1b3322cfd1179ea11fa5083d600717b65e5923e6"/>
|
||||
<project name="kernel/msm" path="kernel" revision="7158567fc83e7475f08db3adedc5df1ad6f54abd"/>
|
||||
<project name="kernel/msm" path="kernel" revision="2051bfa495412f5077065e555ec662ba3d30e6e6"/>
|
||||
<project name="platform/bootable/recovery" path="bootable/recovery" revision="f2914eacee9120680a41463708bb6ee8291749fc"/>
|
||||
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="4b7ae991637a216d745e154cd49b4db6ca55a19e"/>
|
||||
<project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="f0689ac1914cdbc59e53bdc9edd9013dc157c299"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "7e5b2c297555985ac76b069361ff252fe176a018",
|
||||
"revision": "896e4c28c82da3f07a1910be2187201e2045ffd7",
|
||||
"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="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -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="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -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="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -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="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<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="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8a4baf82a131a7853cf7e7f9cf74253927b2f355"/>
|
||||
|
@ -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="17cc405ab6163528efe321e831bf0e72d1166a28"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="101c500903a2477f9de1ea5ce523b9e0be4d45d0"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3b2aeeb5af3083c2f6f4b44beae0b4802566d482"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -5,7 +5,7 @@
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://webide/locale/webide.dtd" >
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://webide/content/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
|
@ -10,3 +10,9 @@ webide.jar:
|
||||
content/newapp.js (newapp.js)
|
||||
content/details.xhtml (details.xhtml)
|
||||
content/details.js (details.js)
|
||||
|
||||
# Temporarily include locales in content, until we're ready
|
||||
# to localize webide
|
||||
|
||||
content/webide.dtd (../locales/en-US/webide.dtd)
|
||||
content/webide.properties (../locales/en-US/webide.properties)
|
||||
|
@ -5,7 +5,7 @@
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://webide/locale/webide.dtd" >
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://webide/content/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
|
@ -15,7 +15,7 @@ const {AppProjects} = require("devtools/app-manager/app-projects");
|
||||
const {Connection} = require("devtools/client/connection-manager");
|
||||
const {AppManager} = require("devtools/app-manager");
|
||||
|
||||
const Strings = Services.strings.createBundle("chrome://webide/locale/webide.properties");
|
||||
const Strings = Services.strings.createBundle("chrome://webide/content/webide.properties");
|
||||
|
||||
const HTML = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://webide/locale/webide.dtd" >
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://webide/content/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DEFINES += -DAB_CD=$(AB_CD)
|
@ -1,10 +0,0 @@
|
||||
#filter substitution
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
webide.jar:
|
||||
% locale webide @AB_CD@ %locale/
|
||||
locale/webide.dtd (%webide.dtd)
|
||||
locale/webide.properties (%webide.properties)
|
@ -1,7 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
@ -6,7 +6,6 @@
|
||||
|
||||
PARALLEL_DIRS += [
|
||||
'content',
|
||||
'locales',
|
||||
'themes',
|
||||
]
|
||||
|
||||
|
@ -422,7 +422,11 @@
|
||||
background-image: url("chrome://browser/skin/privatebrowsing-mask-tabstrip-XPVista7.png");
|
||||
}
|
||||
|
||||
#private-browsing-indicator-titlebar > .private-browsing-indicator {
|
||||
/* We're intentionally using the titlebar asset here for fullscreen mode.
|
||||
* See bug 1008183.
|
||||
*/
|
||||
#private-browsing-indicator-titlebar > .private-browsing-indicator,
|
||||
#main-window[inFullscreen] #TabsToolbar > .private-browsing-indicator {
|
||||
background-image: url("chrome://browser/skin/privatebrowsing-mask-titlebar-XPVista7.png");
|
||||
}
|
||||
}
|
||||
|
@ -2855,7 +2855,7 @@ chatbox {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#main-window[privatebrowsingmode=temporary]:not([tabsintitlebar]) #TabsToolbar > .private-browsing-indicator {
|
||||
#main-window[privatebrowsingmode=temporary]:-moz-any([inFullscreen],:not([tabsintitlebar])) #TabsToolbar > .private-browsing-indicator {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
@ -2865,7 +2865,12 @@ chatbox {
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
#private-browsing-indicator-titlebar > .private-browsing-indicator {
|
||||
/* Bug 1008183: We're intentionally using the titlebar asset here for fullscreen
|
||||
* mode, since the tabstrip "mimics" the titlebar in that case with its own
|
||||
* min/max/close window buttons.
|
||||
*/
|
||||
#private-browsing-indicator-titlebar > .private-browsing-indicator,
|
||||
#main-window[inFullscreen] #TabsToolbar > .private-browsing-indicator {
|
||||
background: url("chrome://browser/skin/privatebrowsing-mask-titlebar.png") no-repeat center 0px;
|
||||
-moz-margin-end: 4px;
|
||||
width: 40px;
|
||||
@ -2883,6 +2888,14 @@ chatbox {
|
||||
background-image: url("chrome://browser/skin/privatebrowsing-mask-titlebar-XPVista7-tall.png");
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
/* We're intentionally using the titlebar asset here for fullscreen mode.
|
||||
* See bug 1008183.
|
||||
*/
|
||||
#main-window[inFullscreen] #TabsToolbar > .private-browsing-indicator {
|
||||
background-image: url("chrome://browser/skin/privatebrowsing-mask-titlebar-XPVista7.png");
|
||||
}
|
||||
|
||||
#main-window[sizemode="maximized"] > #titlebar > #titlebar-content > #titlebar-buttonbox-container > #private-browsing-indicator-titlebar > .private-browsing-indicator {
|
||||
top: -5px;
|
||||
}
|
||||
@ -2893,7 +2906,11 @@ chatbox {
|
||||
%endif
|
||||
|
||||
@media (-moz-windows-classic) {
|
||||
#private-browsing-indicator-titlebar > .private-browsing-indicator {
|
||||
/* We're intentionally using the titlebar asset here for fullscreen mode.
|
||||
* See bug 1008183.
|
||||
*/
|
||||
#private-browsing-indicator-titlebar > .private-browsing-indicator,
|
||||
#main-window[inFullscreen] #TabsToolbar > .private-browsing-indicator {
|
||||
background-image: url("chrome://browser/skin/privatebrowsing-mask-titlebar-XPVista7.png");
|
||||
}
|
||||
/**
|
||||
|
@ -144,7 +144,10 @@ this.Address = {
|
||||
if (((result = str.match(this.REGEXP_DECODE_PLMN)) != null)
|
||||
|| ((result = str.match(this.REGEXP_DECODE_IPV4)) != null)
|
||||
|| ((result = str.match(this.REGEXP_DECODE_IPV6)) != null)
|
||||
|| ((result = str.match(this.REGEXP_DECODE_CUSTOM)) != null)) {
|
||||
|| (((result = str.match(this.REGEXP_DECODE_CUSTOM)) != null)
|
||||
&& (result[2] != "PLMN")
|
||||
&& (result[2] != "IPv4")
|
||||
&& (result[2] != "IPv6"))) {
|
||||
return {address: result[1], type: result[2]};
|
||||
}
|
||||
|
||||
@ -153,10 +156,7 @@ this.Address = {
|
||||
type = "num";
|
||||
} else if (str.match(this.REGEXP_ALPHANUM)) {
|
||||
type = "alphanum";
|
||||
} else if (str.indexOf("@") > 0) {
|
||||
// E-mail should match the definition of `mailbox` as described in section
|
||||
// 3.4 of RFC2822, but excluding the obsolete definitions as indicated by
|
||||
// the "obs-" prefix. Here we match only a `@` character.
|
||||
} else if (str.match(this.REGEXP_EMAIL)) {
|
||||
type = "email";
|
||||
} else {
|
||||
throw new WSP.CodeError("Address: invalid address");
|
||||
@ -179,7 +179,7 @@ this.Address = {
|
||||
let str;
|
||||
switch (value.type) {
|
||||
case "email":
|
||||
if (value.address.indexOf("@") > 0) {
|
||||
if (value.address.match(this.REGEXP_EMAIL)) {
|
||||
str = value.address;
|
||||
}
|
||||
break;
|
||||
@ -212,7 +212,7 @@ this.Address = {
|
||||
if (value.type.match(this.REGEXP_ENCODE_CUSTOM_TYPE)
|
||||
&& value.address.match(this.REGEXP_ENCODE_CUSTOM_ADDR)) {
|
||||
str = value.address + "/TYPE=" + value.type;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -234,11 +234,11 @@ this.Address = {
|
||||
return "email";
|
||||
}
|
||||
|
||||
if (address.match(this.REGEXP_IPV4)) {
|
||||
if (address.match(this.REGEXP_ENCODE_IPV4)) {
|
||||
return "IPv4";
|
||||
}
|
||||
|
||||
if (address.match(this.REGEXP_IPV6)) {
|
||||
if (address.match(this.REGEXP_ENCODE_IPV6)) {
|
||||
return "IPv6";
|
||||
}
|
||||
|
||||
@ -252,20 +252,53 @@ this.Address = {
|
||||
};
|
||||
|
||||
defineLazyRegExp(Address, "REGEXP_DECODE_PLMN", "^(\\+?[\\d.-]+)\\/TYPE=(PLMN)$");
|
||||
defineLazyRegExp(Address, "REGEXP_DECODE_IPV4", "^(\\d{1,3}(?:\\.\\d{1,3}){3})\\/TYPE=(IPv4)$");
|
||||
defineLazyRegExp(Address, "REGEXP_DECODE_IPV6", "^([\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7})\\/TYPE=(IPv6)$");
|
||||
defineLazyRegExp(Address, "REGEXP_DECODE_IPV4", "^((?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9]){0,1}[0-9])\\.){3,3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9]){0,1}[0-9]))\\/TYPE=(IPv4)$");
|
||||
defineLazyRegExp(Address, "REGEXP_DECODE_IPV6", "^(" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,7}:|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|" +
|
||||
"[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|" +
|
||||
":(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9]){0,1}[0-9])\\.){3,3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9]){0,1}[0-9])" +
|
||||
")\\/TYPE=(IPv6)$");
|
||||
defineLazyRegExp(Address, "REGEXP_DECODE_CUSTOM", "^([\\w\\+\\-.%]+)\\/TYPE=(\\w+)$");
|
||||
defineLazyRegExp(Address, "REGEXP_ENCODE_PLMN", "^\\+?[\\d.-]+$");
|
||||
defineLazyRegExp(Address, "REGEXP_ENCODE_IPV4", "^\\d{1,3}(?:\\.\\d{1,3}){3}$");
|
||||
defineLazyRegExp(Address, "REGEXP_ENCODE_IPV6", "^[\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7}$");
|
||||
defineLazyRegExp(Address, "REGEXP_ENCODE_IPV4", "^(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9]){0,1}[0-9])\\.){3,3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9]){0,1}[0-9])$");
|
||||
defineLazyRegExp(Address, "REGEXP_ENCODE_IPV6", "^(?:" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,7}:|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|" +
|
||||
"(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|" +
|
||||
"[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|" +
|
||||
":(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)" +
|
||||
")$");
|
||||
defineLazyRegExp(Address, "REGEXP_ENCODE_CUSTOM_TYPE", "^\\w+$");
|
||||
defineLazyRegExp(Address, "REGEXP_ENCODE_CUSTOM_ADDR", "^[\\w\\+\\-.%]+$");
|
||||
defineLazyRegExp(Address, "REGEXP_NUM", "^[\\+*#]\\d+$");
|
||||
defineLazyRegExp(Address, "REGEXP_NUM", "^[\\+*#]?\\d+$");
|
||||
defineLazyRegExp(Address, "REGEXP_ALPHANUM", "^\\w+$");
|
||||
defineLazyRegExp(Address, "REGEXP_PLMN", "^\\?[\\d.-]$");
|
||||
defineLazyRegExp(Address, "REGEXP_IPV4", "^\\d{1,3}(?:\\.\\d{1,3}){3}$");
|
||||
defineLazyRegExp(Address, "REGEXP_IPV6", "^[\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7}$");
|
||||
defineLazyRegExp(Address, "REGEXP_EMAIL", "@");
|
||||
// OMA-TS-MMS_ENC-V1_3-20110913-A chapter 8:
|
||||
//
|
||||
// E-mail should match the definition of `mailbox` as described in section
|
||||
// 3.4 of RFC2822, but excluding the obsolete definitions as indicated by
|
||||
// the "obs-" prefix.
|
||||
//
|
||||
// Here we try to match addr-spec only.
|
||||
defineLazyRegExp(Address, "REGEXP_EMAIL", "(?:" +
|
||||
"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*|" +
|
||||
"\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\"" +
|
||||
")@(?:" +
|
||||
"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*|" +
|
||||
"\\[(?:" +
|
||||
"[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\\\[\x01-\x09\x0b\x0c\x0e-\x7f]" +
|
||||
")*\\]" +
|
||||
")");
|
||||
|
||||
/**
|
||||
* Header-field = MMS-header | Application-header
|
||||
|
@ -2007,9 +2007,10 @@ MmsService.prototype = {
|
||||
|
||||
// |aMessage.headers|
|
||||
let headers = aMessage["headers"] = {};
|
||||
|
||||
let receivers = aParams.receivers;
|
||||
let headersTo = headers["to"] = [];
|
||||
if (receivers.length != 0) {
|
||||
let headersTo = headers["to"] = [];
|
||||
for (let i = 0; i < receivers.length; i++) {
|
||||
let receiver = receivers[i];
|
||||
let type = MMS.Address.resolveType(receiver);
|
||||
@ -2023,8 +2024,10 @@ MmsService.prototype = {
|
||||
"from " + receiver + " to " + address);
|
||||
} else {
|
||||
address = receiver;
|
||||
isAddrValid = false;
|
||||
if (DEBUG) debug("Error! Address is invalid to send MMS: " + address);
|
||||
if (type == "Others") {
|
||||
isAddrValid = false;
|
||||
if (DEBUG) debug("Error! Address is invalid to send MMS: " + address);
|
||||
}
|
||||
}
|
||||
headersTo.push({"address": address, "type": type});
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ const RIL_GETTHREADSCURSOR_CID =
|
||||
const DEBUG = false;
|
||||
const DISABLE_MMS_GROUPING_FOR_RECEIVING = true;
|
||||
|
||||
const DB_VERSION = 23;
|
||||
|
||||
const DB_VERSION = 22;
|
||||
const MESSAGE_STORE_NAME = "sms";
|
||||
const THREAD_STORE_NAME = "thread";
|
||||
const PARTICIPANT_STORE_NAME = "participant";
|
||||
@ -142,8 +142,12 @@ MobileMessageDB.prototype = {
|
||||
let currentVersion = event.oldVersion;
|
||||
|
||||
function update(currentVersion) {
|
||||
let next = update.bind(self, currentVersion + 1);
|
||||
if (currentVersion >= self.dbVersion) {
|
||||
if (DEBUG) debug("Upgrade finished.");
|
||||
return;
|
||||
}
|
||||
|
||||
let next = update.bind(self, currentVersion + 1);
|
||||
switch (currentVersion) {
|
||||
case 0:
|
||||
if (DEBUG) debug("New database");
|
||||
@ -245,8 +249,8 @@ MobileMessageDB.prototype = {
|
||||
self.upgradeSchema21(db, event.target.transaction, next);
|
||||
break;
|
||||
case 22:
|
||||
// This will need to be moved for each new version
|
||||
if (DEBUG) debug("Upgrade finished.");
|
||||
if (DEBUG) debug("Upgrade to version 23. Add type information to receivers and to");
|
||||
self.upgradeSchema22(event.target.transaction, next);
|
||||
break;
|
||||
default:
|
||||
event.target.transaction.abort();
|
||||
@ -701,8 +705,8 @@ MobileMessageDB.prototype = {
|
||||
// retrieve the records out and insert its "senderOrReceiver" column as a
|
||||
// new record in participantStore.
|
||||
let number = mostRecentRecord.senderOrReceiver;
|
||||
self.findParticipantRecordByAddress(participantStore, number, true,
|
||||
function(participantRecord) {
|
||||
self.findParticipantRecordByPlmnAddress(participantStore, number, true,
|
||||
function(participantRecord) {
|
||||
// Also create a new record in threadStore.
|
||||
let threadRecord = {
|
||||
participantIds: [participantRecord.id],
|
||||
@ -1072,9 +1076,9 @@ MobileMessageDB.prototype = {
|
||||
threadParticipants = messageRecord.receivers;
|
||||
}
|
||||
}
|
||||
self.findThreadRecordByParticipants(threadStore, participantStore,
|
||||
threadParticipants, true,
|
||||
function(threadRecord,
|
||||
self.findThreadRecordByPlmnAddresses(threadStore, participantStore,
|
||||
threadParticipants, true,
|
||||
function(threadRecord,
|
||||
participantIds) {
|
||||
if (!participantIds) {
|
||||
debug("participantIds is empty!");
|
||||
@ -1408,6 +1412,209 @@ MobileMessageDB.prototype = {
|
||||
next();
|
||||
},
|
||||
|
||||
/**
|
||||
* Change receivers format to address and type.
|
||||
*/
|
||||
upgradeSchema22: function(transaction, next) {
|
||||
// Since bug 871433 (DB_VERSION 11), we normalize addresses before really
|
||||
// diving into participant store in findParticipantRecordByPlmnAddress.
|
||||
// This also follows that all addresses stored in participant store are
|
||||
// normalized phone numbers, although they might not be phone numbers at the
|
||||
// first place. So addresses in participant store are not reliable.
|
||||
//
|
||||
// |participantAddresses| in a thread record are reliable, but several
|
||||
// distinct threads can be wrongly mapped into one. For example, an IPv4
|
||||
// address "55.252.255.54" was normalized as US phone number "5525225554".
|
||||
// So beginning with thread store is not really a good idea.
|
||||
//
|
||||
// The only correct way is to begin with all messages records and check if
|
||||
// the findThreadRecordByTypedAddresses() call using a message record's
|
||||
// thread participants returns the same thread record with the one it
|
||||
// currently belong to.
|
||||
|
||||
function getThreadParticipantsFromMessageRecord(aMessageRecord) {
|
||||
let threadParticipants;
|
||||
|
||||
if (aMessageRecord.type == "sms") {
|
||||
let address;
|
||||
if (aMessageRecord.delivery == DELIVERY_RECEIVED) {
|
||||
address = aMessageRecord.sender;
|
||||
} else {
|
||||
address = aMessageRecord.receiver;
|
||||
}
|
||||
threadParticipants = [{
|
||||
address: address,
|
||||
type: MMS.Address.resolveType(address)
|
||||
}];
|
||||
} else { // MMS
|
||||
if ((aMessageRecord.delivery == DELIVERY_RECEIVED) ||
|
||||
(aMessageRecord.delivery == DELIVERY_NOT_DOWNLOADED)) {
|
||||
// DISABLE_MMS_GROUPING_FOR_RECEIVING is set to true at the time, so
|
||||
// we consider only |aMessageRecord.sender|.
|
||||
threadParticipants = [{
|
||||
address: aMessageRecord.sender,
|
||||
type: MMS.Address.resolveType(aMessageRecord.sender)
|
||||
}];
|
||||
} else {
|
||||
threadParticipants = aMessageRecord.headers.to;
|
||||
}
|
||||
}
|
||||
|
||||
return threadParticipants;
|
||||
}
|
||||
|
||||
let participantStore = transaction.objectStore(PARTICIPANT_STORE_NAME);
|
||||
let threadStore = transaction.objectStore(THREAD_STORE_NAME);
|
||||
let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
|
||||
|
||||
let invalidThreadIds = [];
|
||||
|
||||
let self = this;
|
||||
let messageCursorReq = messageStore.openCursor();
|
||||
messageCursorReq.onsuccess = function(aEvent) {
|
||||
let messageCursor = aEvent.target.result;
|
||||
if (messageCursor) {
|
||||
let messageRecord = messageCursor.value;
|
||||
let threadParticipants =
|
||||
getThreadParticipantsFromMessageRecord(messageRecord);
|
||||
|
||||
// 1. If thread ID of this message record has been marked as invalid,
|
||||
// skip further checks and go ahead for the next one.
|
||||
if (invalidThreadIds.indexOf(messageRecord.threadId) >= 0) {
|
||||
messageCursor.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Check if the thread record found with the new algorithm matches
|
||||
// the original one.
|
||||
self.findThreadRecordByTypedAddresses(threadStore, participantStore,
|
||||
threadParticipants, true,
|
||||
function(aThreadRecord,
|
||||
aParticipantIds) {
|
||||
if (!aThreadRecord || aThreadRecord.id !== messageRecord.threadId) {
|
||||
invalidThreadIds.push(messageRecord.threadId);
|
||||
}
|
||||
|
||||
messageCursor.continue();
|
||||
});
|
||||
|
||||
// Only calls |messageCursor.continue()| inside the callback of
|
||||
// findThreadRecordByTypedAddresses() because that may inserts new
|
||||
// participant records and hurt concurrency.
|
||||
return;
|
||||
} // End of |if (messageCursor)|.
|
||||
|
||||
// 3. If there is no any mis-grouped message found, go on to next upgrade.
|
||||
if (!invalidThreadIds.length) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Remove invalid thread records first, so that we don't have
|
||||
// unexpected match in findThreadRecordByTypedAddresses().
|
||||
invalidThreadIds.forEach(function(aInvalidThreadId) {
|
||||
threadStore.delete(aInvalidThreadId);
|
||||
});
|
||||
|
||||
// 5. For each affected thread, re-create a valid thread record for it.
|
||||
(function redoThreading(aInvalidThreadId) {
|
||||
// 5-1. For each message record originally belongs to this thread, find
|
||||
// a new home for it.
|
||||
let range = IDBKeyRange.bound([aInvalidThreadId, 0],
|
||||
[aInvalidThreadId, ""]);
|
||||
let threadMessageCursorReq = messageStore.index("threadId")
|
||||
.openCursor(range, NEXT);
|
||||
threadMessageCursorReq.onsuccess = function(aEvent) {
|
||||
let messageCursor = aEvent.target.result;
|
||||
|
||||
// 5-2. If no more message records to process in this invalid thread,
|
||||
// go on to next invalid thread if available, or pass to next
|
||||
// upgradeSchema function.
|
||||
if (!messageCursor) {
|
||||
if (invalidThreadIds.length) {
|
||||
redoThreading(invalidThreadIds.shift());
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let messageRecord = messageCursor.value;
|
||||
let threadParticipants =
|
||||
getThreadParticipantsFromMessageRecord(messageRecord);
|
||||
|
||||
// 5-3. Assign a thread record for this message record. Basically
|
||||
// copied from |realSaveRecord|, but we don't have to worry
|
||||
// about |updateThreadByMessageChange| because we've removed
|
||||
// affected threads.
|
||||
self.findThreadRecordByTypedAddresses(threadStore, participantStore,
|
||||
threadParticipants, true,
|
||||
function(aThreadRecord,
|
||||
aParticipantIds) {
|
||||
// Setup participantIdsIndex.
|
||||
messageRecord.participantIdsIndex =
|
||||
aParticipantIds.map(function(aParticipantId) {
|
||||
return [aParticipantId, messageRecord.timestamp];
|
||||
});
|
||||
|
||||
let threadExists = aThreadRecord ? true : false;
|
||||
if (!threadExists) {
|
||||
aThreadRecord = {
|
||||
participantIds: aParticipantIds,
|
||||
participantAddresses:
|
||||
threadParticipants.map(function(aTypedAddress) {
|
||||
return aTypedAddress.address;
|
||||
}),
|
||||
unreadCount: 0,
|
||||
lastTimestamp: -1
|
||||
};
|
||||
}
|
||||
|
||||
let needsUpdate = false;
|
||||
if (aThreadRecord.lastTimestamp <= messageRecord.timestamp) {
|
||||
let lastMessageSubject;
|
||||
if (messageRecord.type == "mms") {
|
||||
lastMessageSubject = messageRecord.headers.subject;
|
||||
}
|
||||
aThreadRecord.lastMessageSubject = lastMessageSubject || null;
|
||||
aThreadRecord.lastTimestamp = messageRecord.timestamp;
|
||||
aThreadRecord.body = messageRecord.body;
|
||||
aThreadRecord.lastMessageId = messageRecord.id;
|
||||
aThreadRecord.lastMessageType = messageRecord.type;
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
if (!messageRecord.read) {
|
||||
aThreadRecord.unreadCount++;
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
let updateMessageRecordThreadId = function(aThreadId) {
|
||||
// Setup threadId & threadIdIndex.
|
||||
messageRecord.threadId = aThreadId;
|
||||
messageRecord.threadIdIndex = [aThreadId, messageRecord.timestamp];
|
||||
|
||||
messageCursor.update(messageRecord);
|
||||
messageCursor.continue();
|
||||
};
|
||||
|
||||
if (threadExists) {
|
||||
if (needsUpdate) {
|
||||
threadStore.put(aThreadRecord);
|
||||
}
|
||||
updateMessageRecordThreadId(aThreadRecord.id);
|
||||
} else {
|
||||
threadStore.add(aThreadRecord).onsuccess = function(aEvent) {
|
||||
let threadId = aEvent.target.result;
|
||||
updateMessageRecordThreadId(threadId);
|
||||
};
|
||||
}
|
||||
}); // End of findThreadRecordByTypedAddresses().
|
||||
}; // End of threadMessageCursorReq.onsuccess.
|
||||
})(invalidThreadIds.shift()); // End of function redoThreading.
|
||||
}; // End of messageStore.openCursor().onsuccess
|
||||
},
|
||||
|
||||
matchParsedPhoneNumbers: function(addr1, parsedAddr1, addr2, parsedAddr2) {
|
||||
if ((parsedAddr1.internationalNumber &&
|
||||
parsedAddr1.internationalNumber === parsedAddr2.internationalNumber) ||
|
||||
@ -1536,10 +1743,22 @@ MobileMessageDB.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
findParticipantRecordByAddress: function(aParticipantStore, aAddress,
|
||||
aCreate, aCallback) {
|
||||
createParticipantRecord: function(aParticipantStore, aAddresses, aCallback) {
|
||||
let participantRecord = { addresses: aAddresses };
|
||||
let addRequest = aParticipantStore.add(participantRecord);
|
||||
addRequest.onsuccess = function(event) {
|
||||
participantRecord.id = event.target.result;
|
||||
if (DEBUG) {
|
||||
debug("createParticipantRecord: " + JSON.stringify(participantRecord));
|
||||
}
|
||||
aCallback(participantRecord);
|
||||
};
|
||||
},
|
||||
|
||||
findParticipantRecordByPlmnAddress: function(aParticipantStore, aAddress,
|
||||
aCreate, aCallback) {
|
||||
if (DEBUG) {
|
||||
debug("findParticipantRecordByAddress("
|
||||
debug("findParticipantRecordByPlmnAddress("
|
||||
+ JSON.stringify(aAddress) + ", " + aCreate + ")");
|
||||
}
|
||||
|
||||
@ -1559,7 +1778,7 @@ MobileMessageDB.prototype = {
|
||||
allPossibleAddresses.push(parsedAddress.internationalNumber);
|
||||
}
|
||||
if (DEBUG) {
|
||||
debug("findParticipantRecordByAddress: allPossibleAddresses = " +
|
||||
debug("findParticipantRecordByPlmnAddress: allPossibleAddresses = " +
|
||||
JSON.stringify(allPossibleAddresses));
|
||||
}
|
||||
|
||||
@ -1572,7 +1791,7 @@ MobileMessageDB.prototype = {
|
||||
// If we're lucky, return the fetched participant record.
|
||||
if (participantRecord) {
|
||||
if (DEBUG) {
|
||||
debug("findParticipantRecordByAddress: got "
|
||||
debug("findParticipantRecordByPlmnAddress: got "
|
||||
+ JSON.stringify(participantRecord));
|
||||
}
|
||||
aCallback(participantRecord);
|
||||
@ -1596,16 +1815,8 @@ MobileMessageDB.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let participantRecord = { addresses: [normalizedAddress] };
|
||||
let addRequest = aParticipantStore.add(participantRecord);
|
||||
addRequest.onsuccess = function(event) {
|
||||
participantRecord.id = event.target.result;
|
||||
if (DEBUG) {
|
||||
debug("findParticipantRecordByAddress: created "
|
||||
+ JSON.stringify(participantRecord));
|
||||
}
|
||||
aCallback(participantRecord);
|
||||
};
|
||||
this.createParticipantRecord(aParticipantStore, [normalizedAddress],
|
||||
aCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1628,7 +1839,7 @@ MobileMessageDB.prototype = {
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("findParticipantRecordByAddress: match "
|
||||
debug("findParticipantRecordByPlmnAddress: match "
|
||||
+ JSON.stringify(cursor.value));
|
||||
}
|
||||
aCallback(participantRecord);
|
||||
@ -1641,16 +1852,58 @@ MobileMessageDB.prototype = {
|
||||
}).bind(this);
|
||||
},
|
||||
|
||||
findParticipantIdsByAddresses: function(aParticipantStore, aAddresses,
|
||||
aCreate, aSkipNonexistent, aCallback) {
|
||||
findParticipantRecordByOtherAddress: function(aParticipantStore, aAddress,
|
||||
aCreate, aCallback) {
|
||||
if (DEBUG) {
|
||||
debug("findParticipantIdsByAddresses("
|
||||
debug("findParticipantRecordByOtherAddress(" +
|
||||
JSON.stringify(aAddress) + ", " + aCreate + ")");
|
||||
}
|
||||
|
||||
// Go full match.
|
||||
let request = aParticipantStore.index("addresses").get(aAddress);
|
||||
request.onsuccess = (function(event) {
|
||||
let participantRecord = event.target.result;
|
||||
if (participantRecord) {
|
||||
if (DEBUG) {
|
||||
debug("findParticipantRecordByOtherAddress: got "
|
||||
+ JSON.stringify(participantRecord));
|
||||
}
|
||||
aCallback(participantRecord);
|
||||
return;
|
||||
}
|
||||
if (aCreate) {
|
||||
this.createParticipantRecord(aParticipantStore, [aAddress], aCallback);
|
||||
return;
|
||||
}
|
||||
aCallback(null);
|
||||
}).bind(this);
|
||||
},
|
||||
|
||||
findParticipantRecordByTypedAddress: function(aParticipantStore,
|
||||
aTypedAddress, aCreate,
|
||||
aCallback) {
|
||||
if (aTypedAddress.type == "PLMN") {
|
||||
this.findParticipantRecordByPlmnAddress(aParticipantStore,
|
||||
aTypedAddress.address, aCreate,
|
||||
aCallback);
|
||||
} else {
|
||||
this.findParticipantRecordByOtherAddress(aParticipantStore,
|
||||
aTypedAddress.address, aCreate,
|
||||
aCallback);
|
||||
}
|
||||
},
|
||||
|
||||
// For upgradeSchema13 usage.
|
||||
findParticipantIdsByPlmnAddresses: function(aParticipantStore, aAddresses,
|
||||
aCreate, aSkipNonexistent, aCallback) {
|
||||
if (DEBUG) {
|
||||
debug("findParticipantIdsByPlmnAddresses("
|
||||
+ JSON.stringify(aAddresses) + ", "
|
||||
+ aCreate + ", " + aSkipNonexistent + ")");
|
||||
}
|
||||
|
||||
if (!aAddresses || !aAddresses.length) {
|
||||
if (DEBUG) debug("findParticipantIdsByAddresses: returning null");
|
||||
if (DEBUG) debug("findParticipantIdsByPlmnAddresses: returning null");
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
@ -1662,17 +1915,17 @@ MobileMessageDB.prototype = {
|
||||
result.sort(function(a, b) {
|
||||
return a - b;
|
||||
});
|
||||
if (DEBUG) debug("findParticipantIdsByAddresses: returning " + result);
|
||||
if (DEBUG) debug("findParticipantIdsByPlmnAddresses: returning " + result);
|
||||
aCallback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
self.findParticipantRecordByAddress(aParticipantStore,
|
||||
aAddresses[index++], aCreate,
|
||||
function(participantRecord) {
|
||||
self.findParticipantRecordByPlmnAddress(aParticipantStore,
|
||||
aAddresses[index++], aCreate,
|
||||
function(participantRecord) {
|
||||
if (!participantRecord) {
|
||||
if (!aSkipNonexistent) {
|
||||
if (DEBUG) debug("findParticipantIdsByAddresses: returning null");
|
||||
if (DEBUG) debug("findParticipantIdsByPlmnAddresses: returning null");
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
@ -1684,18 +1937,68 @@ MobileMessageDB.prototype = {
|
||||
}) (0, []);
|
||||
},
|
||||
|
||||
findThreadRecordByParticipants: function(aThreadStore, aParticipantStore,
|
||||
aAddresses, aCreateParticipants,
|
||||
aCallback) {
|
||||
findParticipantIdsByTypedAddresses: function(aParticipantStore,
|
||||
aTypedAddresses, aCreate,
|
||||
aSkipNonexistent, aCallback) {
|
||||
if (DEBUG) {
|
||||
debug("findThreadRecordByParticipants(" + JSON.stringify(aAddresses)
|
||||
debug("findParticipantIdsByTypedAddresses(" +
|
||||
JSON.stringify(aTypedAddresses) + ", " +
|
||||
aCreate + ", " + aSkipNonexistent + ")");
|
||||
}
|
||||
|
||||
if (!aTypedAddresses || !aTypedAddresses.length) {
|
||||
if (DEBUG) debug("findParticipantIdsByTypedAddresses: returning null");
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let self = this;
|
||||
(function findParticipantId(index, result) {
|
||||
if (index >= aTypedAddresses.length) {
|
||||
// Sort numerically.
|
||||
result.sort(function(a, b) {
|
||||
return a - b;
|
||||
});
|
||||
if (DEBUG) {
|
||||
debug("findParticipantIdsByTypedAddresses: returning " + result);
|
||||
}
|
||||
aCallback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
self.findParticipantRecordByTypedAddress(aParticipantStore,
|
||||
aTypedAddresses[index++],
|
||||
aCreate,
|
||||
function(participantRecord) {
|
||||
if (!participantRecord) {
|
||||
if (!aSkipNonexistent) {
|
||||
if (DEBUG) {
|
||||
debug("findParticipantIdsByTypedAddresses: returning null");
|
||||
}
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
} else if (result.indexOf(participantRecord.id) < 0) {
|
||||
result.push(participantRecord.id);
|
||||
}
|
||||
findParticipantId(index, result);
|
||||
});
|
||||
}) (0, []);
|
||||
},
|
||||
|
||||
// For upgradeSchema13 usage.
|
||||
findThreadRecordByPlmnAddresses: function(aThreadStore, aParticipantStore,
|
||||
aAddresses, aCreateParticipants,
|
||||
aCallback) {
|
||||
if (DEBUG) {
|
||||
debug("findThreadRecordByPlmnAddresses(" + JSON.stringify(aAddresses)
|
||||
+ ", " + aCreateParticipants + ")");
|
||||
}
|
||||
this.findParticipantIdsByAddresses(aParticipantStore, aAddresses,
|
||||
aCreateParticipants, false,
|
||||
function(participantIds) {
|
||||
this.findParticipantIdsByPlmnAddresses(aParticipantStore, aAddresses,
|
||||
aCreateParticipants, false,
|
||||
function(participantIds) {
|
||||
if (!participantIds) {
|
||||
if (DEBUG) debug("findThreadRecordByParticipants: returning null");
|
||||
if (DEBUG) debug("findThreadRecordByPlmnAddresses: returning null");
|
||||
aCallback(null, null);
|
||||
return;
|
||||
}
|
||||
@ -1704,7 +2007,7 @@ MobileMessageDB.prototype = {
|
||||
request.onsuccess = function(event) {
|
||||
let threadRecord = event.target.result;
|
||||
if (DEBUG) {
|
||||
debug("findThreadRecordByParticipants: return "
|
||||
debug("findThreadRecordByPlmnAddresses: return "
|
||||
+ JSON.stringify(threadRecord));
|
||||
}
|
||||
aCallback(threadRecord, participantIds);
|
||||
@ -1712,6 +2015,34 @@ MobileMessageDB.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
findThreadRecordByTypedAddresses: function(aThreadStore, aParticipantStore,
|
||||
aTypedAddresses,
|
||||
aCreateParticipants, aCallback) {
|
||||
if (DEBUG) {
|
||||
debug("findThreadRecordByTypedAddresses(" +
|
||||
JSON.stringify(aTypedAddresses) + ", " + aCreateParticipants + ")");
|
||||
}
|
||||
this.findParticipantIdsByTypedAddresses(aParticipantStore, aTypedAddresses,
|
||||
aCreateParticipants, false,
|
||||
function(participantIds) {
|
||||
if (!participantIds) {
|
||||
if (DEBUG) debug("findThreadRecordByTypedAddresses: returning null");
|
||||
aCallback(null, null);
|
||||
return;
|
||||
}
|
||||
// Find record from thread store.
|
||||
let request = aThreadStore.index("participantIds").get(participantIds);
|
||||
request.onsuccess = function(event) {
|
||||
let threadRecord = event.target.result;
|
||||
if (DEBUG) {
|
||||
debug("findThreadRecordByTypedAddresses: return " +
|
||||
JSON.stringify(threadRecord));
|
||||
}
|
||||
aCallback(threadRecord, participantIds);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
newTxnWithCallback: function(aCallback, aFunc, aStoreNames) {
|
||||
let self = this;
|
||||
this.newTxn(READ_WRITE, function(aError, aTransaction, aStores) {
|
||||
@ -1742,7 +2073,7 @@ MobileMessageDB.prototype = {
|
||||
}, aStoreNames);
|
||||
},
|
||||
|
||||
saveRecord: function(aMessageRecord, aAddresses, aCallback) {
|
||||
saveRecord: function(aMessageRecord, aThreadParticipants, aCallback) {
|
||||
if (DEBUG) debug("Going to store " + JSON.stringify(aMessageRecord));
|
||||
|
||||
let self = this;
|
||||
@ -1776,13 +2107,14 @@ MobileMessageDB.prototype = {
|
||||
let participantStore = stores[1];
|
||||
let threadStore = stores[2];
|
||||
self.replaceShortMessageOnSave(txn, messageStore, participantStore,
|
||||
threadStore, aMessageRecord, aAddresses);
|
||||
threadStore, aMessageRecord,
|
||||
aThreadParticipants);
|
||||
}, [MESSAGE_STORE_NAME, PARTICIPANT_STORE_NAME, THREAD_STORE_NAME]);
|
||||
},
|
||||
|
||||
replaceShortMessageOnSave: function(aTransaction, aMessageStore,
|
||||
aParticipantStore, aThreadStore,
|
||||
aMessageRecord, aAddresses) {
|
||||
aMessageRecord, aThreadParticipants) {
|
||||
let isReplaceTypePid = (aMessageRecord.pid) &&
|
||||
((aMessageRecord.pid >= RIL.PDU_PID_REPLACE_SHORT_MESSAGE_TYPE_1 &&
|
||||
aMessageRecord.pid <= RIL.PDU_PID_REPLACE_SHORT_MESSAGE_TYPE_7) ||
|
||||
@ -1792,7 +2124,7 @@ MobileMessageDB.prototype = {
|
||||
aMessageRecord.delivery != DELIVERY_RECEIVED ||
|
||||
!isReplaceTypePid) {
|
||||
this.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aAddresses);
|
||||
aThreadStore, aMessageRecord, aThreadParticipants);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1805,12 +2137,16 @@ MobileMessageDB.prototype = {
|
||||
// shall store the message in the normal way. ... it is recommended
|
||||
// that the SC address should not be checked by the MS."
|
||||
let self = this;
|
||||
this.findParticipantRecordByAddress(aParticipantStore,
|
||||
aMessageRecord.sender, false,
|
||||
function(participantRecord) {
|
||||
let typedSender = {
|
||||
address: aMessageRecord.sender,
|
||||
type: MMS.Address.resolveType(aMessageRecord.sender)
|
||||
};
|
||||
this.findParticipantRecordByTypedAddress(aParticipantStore, typedSender,
|
||||
false,
|
||||
function(participantRecord) {
|
||||
if (!participantRecord) {
|
||||
self.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aAddresses);
|
||||
aThreadStore, aMessageRecord, aThreadParticipants);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1821,7 +2157,7 @@ MobileMessageDB.prototype = {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
self.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aAddresses);
|
||||
aThreadStore, aMessageRecord, aThreadParticipants);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1838,17 +2174,18 @@ MobileMessageDB.prototype = {
|
||||
// Match! Now replace that found message record with current one.
|
||||
aMessageRecord.id = foundMessageRecord.id;
|
||||
self.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aAddresses);
|
||||
aThreadStore, aMessageRecord, aThreadParticipants);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
realSaveRecord: function(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aAddresses) {
|
||||
aThreadStore, aMessageRecord, aThreadParticipants) {
|
||||
let self = this;
|
||||
this.findThreadRecordByParticipants(aThreadStore, aParticipantStore,
|
||||
aAddresses, true,
|
||||
function(threadRecord, participantIds) {
|
||||
this.findThreadRecordByTypedAddresses(aThreadStore, aParticipantStore,
|
||||
aThreadParticipants, true,
|
||||
function(threadRecord,
|
||||
participantIds) {
|
||||
if (!participantIds) {
|
||||
aTransaction.abort();
|
||||
return;
|
||||
@ -1930,7 +2267,9 @@ MobileMessageDB.prototype = {
|
||||
|
||||
threadRecord = {
|
||||
participantIds: participantIds,
|
||||
participantAddresses: aAddresses,
|
||||
participantAddresses: aThreadParticipants.map(function(typedAddress) {
|
||||
return typedAddress.address;
|
||||
}),
|
||||
lastMessageId: aMessageRecord.id,
|
||||
lastTimestamp: timestamp,
|
||||
lastMessageSubject: lastMessageSubject || null,
|
||||
@ -2127,7 +2466,13 @@ MobileMessageDB.prototype = {
|
||||
if (DEBUG) debug("Error! Cannot strip out user's own phone number!");
|
||||
}
|
||||
|
||||
threadParticipants = threadParticipants.concat(slicedReceivers);
|
||||
threadParticipants =
|
||||
threadParticipants.concat(slicedReceivers).map(function(aAddress) {
|
||||
return {
|
||||
address: aAddress,
|
||||
type: MMS.Address.resolveType(aAddress)
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
updateThreadByMessageChange: function(messageStore, threadStore, threadId,
|
||||
@ -2207,13 +2552,19 @@ MobileMessageDB.prototype = {
|
||||
if (aMessage.headers.from) {
|
||||
aMessage.sender = aMessage.headers.from.address;
|
||||
} else {
|
||||
aMessage.sender = "anonymous";
|
||||
aMessage.sender = "";
|
||||
}
|
||||
|
||||
threadParticipants = [aMessage.sender];
|
||||
threadParticipants = [{
|
||||
address: aMessage.sender,
|
||||
type: MMS.Address.resolveType(aMessage.sender)
|
||||
}];
|
||||
this.fillReceivedMmsThreadParticipants(aMessage, threadParticipants);
|
||||
} else { // SMS
|
||||
threadParticipants = [aMessage.sender];
|
||||
threadParticipants = [{
|
||||
address: aMessage.sender,
|
||||
type: MMS.Address.resolveType(aMessage.sender)
|
||||
}];
|
||||
}
|
||||
|
||||
let timestamp = aMessage.timestamp;
|
||||
@ -2284,16 +2635,7 @@ MobileMessageDB.prototype = {
|
||||
aMessage.deliveryTimestamp = 0;
|
||||
}
|
||||
} else if (aMessage.type == "mms") {
|
||||
let receivers = aMessage.receivers
|
||||
if (!Array.isArray(receivers)) {
|
||||
if (DEBUG) {
|
||||
debug("Need receivers for MMS. Fail to save the sending message.");
|
||||
}
|
||||
if (aCallback) {
|
||||
aCallback.notify(Cr.NS_ERROR_FAILURE, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let receivers = aMessage.receivers;
|
||||
let readStatus = aMessage.headers["x-mms-read-report"]
|
||||
? MMS.DOM_READ_STATUS_PENDING
|
||||
: MMS.DOM_READ_STATUS_NOT_APPLICABLE;
|
||||
@ -2322,13 +2664,16 @@ MobileMessageDB.prototype = {
|
||||
// |sentTimestamp| is not available when the message is still sedning.
|
||||
aMessage.sentTimestamp = 0;
|
||||
|
||||
let addresses;
|
||||
let threadParticipants;
|
||||
if (aMessage.type == "sms") {
|
||||
addresses = [aMessage.receiver];
|
||||
threadParticipants = [{
|
||||
address: aMessage.receiver,
|
||||
type :MMS.Address.resolveType(aMessage.receiver)
|
||||
}];
|
||||
} else if (aMessage.type == "mms") {
|
||||
addresses = aMessage.receivers;
|
||||
threadParticipants = aMessage.headers.to;
|
||||
}
|
||||
this.saveRecord(aMessage, addresses, aCallback);
|
||||
this.saveRecord(aMessage, threadParticipants, aCallback);
|
||||
},
|
||||
|
||||
setMessageDeliveryByMessageId: function(messageId, receiver, delivery,
|
||||
@ -3011,9 +3356,15 @@ let FilterSearcherHelper = {
|
||||
}
|
||||
|
||||
let participantStore = txn.objectStore(PARTICIPANT_STORE_NAME);
|
||||
mmdb.findParticipantIdsByAddresses(participantStore, filter.numbers,
|
||||
false, true,
|
||||
(function(participantIds) {
|
||||
let typedAddresses = filter.numbers.map(function(number) {
|
||||
return {
|
||||
address: number,
|
||||
type: MMS.Address.resolveType(number)
|
||||
};
|
||||
});
|
||||
mmdb.findParticipantIdsByTypedAddresses(participantStore, typedAddresses,
|
||||
false, true,
|
||||
(function(participantIds) {
|
||||
if (!participantIds || !participantIds.length) {
|
||||
// Oops! No such participant at all.
|
||||
|
||||
|
@ -43,6 +43,8 @@ qemu = true
|
||||
[test_mmdb_setmessagedeliverybyid_sms.js]
|
||||
[test_mmdb_foreachmatchedmmsdeliveryinfo.js]
|
||||
[test_mmdb_full_storage.js]
|
||||
[test_mmdb_upgradeSchema_current_structure.js]
|
||||
[test_mmdb_upgradeSchema_22.js]
|
||||
[test_replace_short_message_type.js]
|
||||
[test_mt_sms_concatenation.js]
|
||||
[test_error_of_mms_manual_retrieval.js]
|
||||
|
@ -2,200 +2,100 @@
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
const NUM_THREADS = 10;
|
||||
const REMOTE_NATIONAL_NUMBER = "555531555";
|
||||
const REMOTE_NATIONAL_NUMBER = "552522555";
|
||||
const REMOTE_INTERNATIONAL_NUMBER = "+1" + REMOTE_NATIONAL_NUMBER;
|
||||
|
||||
SpecialPowers.addPermission("sms", true, document);
|
||||
SpecialPowers.setBoolPref("dom.sms.enabled", true);
|
||||
const TEXT_1 = "Nice to meet you";
|
||||
const TEXT_2 = "Nice to meet you, too";
|
||||
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendSmsToEmulator(from, text) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
let cmd = "sms send " + from + " " + text;
|
||||
runEmulatorCmd(cmd, function(result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
|
||||
is(result[0], "OK", "Emulator response");
|
||||
});
|
||||
// Have a long long subject causes the send fails, so we don't need
|
||||
// networking here.
|
||||
const MMS_MAX_LENGTH_SUBJECT = 40;
|
||||
function genMmsSubject(sep) {
|
||||
return "Hello " + (new Array(MMS_MAX_LENGTH_SUBJECT).join(sep)) + " World!";
|
||||
}
|
||||
|
||||
let tasks = {
|
||||
// List of test fuctions. Each of them should call |tasks.next()| when
|
||||
// completed or |tasks.finish()| to jump to the last one.
|
||||
_tasks: [],
|
||||
_nextTaskIndex: 0,
|
||||
|
||||
push: function(func) {
|
||||
this._tasks.push(func);
|
||||
},
|
||||
|
||||
next: function() {
|
||||
let index = this._nextTaskIndex++;
|
||||
let task = this._tasks[index];
|
||||
try {
|
||||
task();
|
||||
} catch (ex) {
|
||||
ok(false, "test task[" + index + "] throws: " + ex);
|
||||
// Run last task as clean up if possible.
|
||||
if (index != this._tasks.length - 1) {
|
||||
this.finish();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
finish: function() {
|
||||
this._tasks[this._tasks.length - 1]();
|
||||
},
|
||||
|
||||
run: function() {
|
||||
this.next();
|
||||
}
|
||||
};
|
||||
|
||||
let manager;
|
||||
function getAllMessages(callback, filter, reverse) {
|
||||
if (!filter) {
|
||||
filter = new MozSmsFilter;
|
||||
}
|
||||
let messages = [];
|
||||
let request = manager.getMessages(filter, reverse || false);
|
||||
request.onsuccess = function(event) {
|
||||
if (request.result) {
|
||||
messages.push(request.result);
|
||||
request.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
window.setTimeout(callback.bind(null, messages), 0);
|
||||
}
|
||||
function genFailingMms(aReceivers) {
|
||||
return {
|
||||
receivers: aReceivers,
|
||||
subject: genMmsSubject(' '),
|
||||
attachments: [],
|
||||
};
|
||||
}
|
||||
|
||||
function deleteAllMessages() {
|
||||
log("Deleting all messages.");
|
||||
getAllMessages(function deleteAll(messages) {
|
||||
let message = messages.shift();
|
||||
if (!message) {
|
||||
ok(true, "all messages deleted");
|
||||
tasks.next();
|
||||
return;
|
||||
}
|
||||
|
||||
let request = manager.delete(message.id);
|
||||
request.onsuccess = deleteAll.bind(null, messages);
|
||||
request.onerror = function(event) {
|
||||
ok(false, "failed to delete all messages");
|
||||
tasks.finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkMessage(needle, secondary) {
|
||||
log(" Verifying " + needle);
|
||||
function checkMessage(aNeedle, aValidNumbers) {
|
||||
log(" Verifying " + aNeedle);
|
||||
|
||||
let filter = new MozSmsFilter();
|
||||
filter.numbers = [needle];
|
||||
getAllMessages(function(messages) {
|
||||
is(messages.length, 2, "should have exactly 2 messages");
|
||||
filter.numbers = [aNeedle];
|
||||
return getMessages(filter)
|
||||
.then(function(messages) {
|
||||
// Check the messages are sent to/received from aValidNumbers.
|
||||
is(messages.length, aValidNumbers.length, "messages.length");
|
||||
|
||||
// Check the messages are sent to/received from either 'needle' or
|
||||
// 'secondary' number.
|
||||
let validNumbers = [needle, secondary];
|
||||
for (let message of messages) {
|
||||
let number = (message.delivery === "received") ? message.sender
|
||||
for (let message of messages) {
|
||||
let number;
|
||||
if (message.type == "sms") {
|
||||
number = (message.delivery === "received") ? message.sender
|
||||
: message.receiver;
|
||||
let index = validNumbers.indexOf(number);
|
||||
ok(index >= 0, "message.number");
|
||||
validNumbers.splice(index, 1); // Remove from validNumbers.
|
||||
}
|
||||
} else {
|
||||
number = message.receivers[0];
|
||||
}
|
||||
|
||||
tasks.next();
|
||||
}, filter);
|
||||
let index = aValidNumbers.indexOf(number);
|
||||
ok(index >= 0, "message.number");
|
||||
aValidNumbers.splice(index, 1); // Remove from aValidNumbers.
|
||||
}
|
||||
|
||||
is(aValidNumbers.length, 0, "aValidNumbers.length");
|
||||
});
|
||||
}
|
||||
|
||||
tasks.push(function verifyInitialState() {
|
||||
log("Verifying initial state.");
|
||||
manager = window.navigator.mozMobileMessage;
|
||||
ok(manager instanceof MozMobileMessageManager,
|
||||
"manager is instance of " + manager.constructor);
|
||||
tasks.next();
|
||||
startTestCommon(function testCaseMain() {
|
||||
return Promise.resolve()
|
||||
|
||||
// SMS, MO:international, MT:national
|
||||
.then(() => sendSmsWithSuccess(REMOTE_INTERNATIONAL_NUMBER + '1', TEXT_1))
|
||||
.then(() => sendTextSmsToEmulator(REMOTE_NATIONAL_NUMBER + '1', TEXT_2))
|
||||
// SMS, MO:national, MT:international
|
||||
.then(() => sendSmsWithSuccess(REMOTE_NATIONAL_NUMBER + '2', TEXT_1))
|
||||
.then(() => sendTextSmsToEmulator(REMOTE_INTERNATIONAL_NUMBER + '2', TEXT_2))
|
||||
// MMS, international
|
||||
.then(() => sendMmsWithFailure(genFailingMms([REMOTE_INTERNATIONAL_NUMBER + '3'])))
|
||||
// MMS, national
|
||||
.then(() => sendMmsWithFailure(genFailingMms([REMOTE_NATIONAL_NUMBER + '3'])))
|
||||
// SMS, MO:international, MT:national, ambiguous number.
|
||||
.then(() => sendSmsWithSuccess(REMOTE_INTERNATIONAL_NUMBER + '4', TEXT_1))
|
||||
.then(() => sendTextSmsToEmulator(REMOTE_NATIONAL_NUMBER + '4', TEXT_2))
|
||||
.then(() => sendMmsWithFailure(genFailingMms(["jkalbcjklg"])))
|
||||
.then(() => sendMmsWithFailure(genFailingMms(["jk.alb.cjk.lg"])))
|
||||
// MMS, email
|
||||
.then(() => sendMmsWithFailure(genFailingMms(["jk@alb.cjk.lg"])))
|
||||
// MMS, IPv4
|
||||
.then(() => sendMmsWithFailure(genFailingMms(["55.252.255.54"])))
|
||||
// MMS, IPv6
|
||||
.then(() => sendMmsWithFailure(genFailingMms(["5:5:2:5:2:2:55:54"])))
|
||||
|
||||
.then(() => checkMessage(REMOTE_INTERNATIONAL_NUMBER + '1',
|
||||
[ REMOTE_INTERNATIONAL_NUMBER + '1',
|
||||
REMOTE_NATIONAL_NUMBER + '1' ]))
|
||||
.then(() => checkMessage(REMOTE_NATIONAL_NUMBER + '2',
|
||||
[ REMOTE_NATIONAL_NUMBER + '2',
|
||||
REMOTE_INTERNATIONAL_NUMBER + '2' ]))
|
||||
.then(() => checkMessage(REMOTE_INTERNATIONAL_NUMBER + '3',
|
||||
[ REMOTE_INTERNATIONAL_NUMBER + '3',
|
||||
REMOTE_NATIONAL_NUMBER + '3' ]))
|
||||
.then(() => checkMessage(REMOTE_NATIONAL_NUMBER + '3',
|
||||
[ REMOTE_NATIONAL_NUMBER + '3',
|
||||
REMOTE_INTERNATIONAL_NUMBER + '3' ]))
|
||||
.then(() => checkMessage(REMOTE_NATIONAL_NUMBER + '4',
|
||||
[ REMOTE_NATIONAL_NUMBER + '4',
|
||||
REMOTE_INTERNATIONAL_NUMBER + '4',
|
||||
"jkalbcjklg",
|
||||
"jk.alb.cjk.lg" ]))
|
||||
.then(() => checkMessage("jk@alb.cjk.lg", ["jk@alb.cjk.lg"]))
|
||||
.then(() => checkMessage("55.252.255.54", ["55.252.255.54"]))
|
||||
.then(() => checkMessage("5:5:2:5:2:2:55:54", ["5:5:2:5:2:2:55:54"]))
|
||||
});
|
||||
|
||||
tasks.push(deleteAllMessages);
|
||||
|
||||
/**
|
||||
* Populate database with messages to being tests. We'll have NUM_THREADS
|
||||
* sent and received messages.
|
||||
*
|
||||
* send to "+15555315550"
|
||||
* receive from "5555315550", count = 1
|
||||
*
|
||||
* send to "+15555315551"
|
||||
* receive from "5555315551", count = 2
|
||||
* ...
|
||||
* send to "+15555315559"
|
||||
* receive from "5555315559", count = 10
|
||||
*/
|
||||
tasks.push(function populateMessages() {
|
||||
log("Populating messages.");
|
||||
let count = 0;
|
||||
|
||||
function sendMessage(iter) {
|
||||
let request = manager.send(REMOTE_INTERNATIONAL_NUMBER + iter,
|
||||
"Nice to meet you");
|
||||
request.onsuccess = function onRequestSuccess(event) {
|
||||
sendSmsToEmulator(REMOTE_NATIONAL_NUMBER + iter,
|
||||
"Nice to meet you, too");
|
||||
}
|
||||
request.onerror = function onRequestError(event) {
|
||||
tasks.finish();
|
||||
}
|
||||
}
|
||||
|
||||
manager.addEventListener("received", function onReceived(event) {
|
||||
++count;
|
||||
if (count < NUM_THREADS) {
|
||||
sendMessage(count);
|
||||
} else {
|
||||
manager.removeEventListener("received", onReceived);
|
||||
tasks.next();
|
||||
}
|
||||
});
|
||||
|
||||
sendMessage(count);
|
||||
});
|
||||
|
||||
tasks.push(function() {
|
||||
log("Verifying number of messages in database");
|
||||
getAllMessages(function(messages) {
|
||||
is(messages.length, NUM_THREADS * 2,
|
||||
"should have exactly " + (NUM_THREADS * 2) + " messages");
|
||||
|
||||
tasks.next();
|
||||
});
|
||||
});
|
||||
|
||||
for (let iter = 0; iter < NUM_THREADS; iter++) {
|
||||
let national = REMOTE_NATIONAL_NUMBER + iter;
|
||||
let international = REMOTE_INTERNATIONAL_NUMBER + iter;
|
||||
tasks.push(checkMessage.bind(null, national, international));
|
||||
tasks.push(checkMessage.bind(null, international, national));
|
||||
}
|
||||
|
||||
tasks.push(deleteAllMessages);
|
||||
|
||||
// WARNING: All tasks should be pushed before this!!!
|
||||
tasks.push(function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("sms", document);
|
||||
SpecialPowers.clearUserPref("dom.sms.enabled");
|
||||
finish();
|
||||
});
|
||||
|
||||
tasks.run();
|
||||
|
@ -2,14 +2,15 @@
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
MARIONETTE_HEAD_JS = 'mmdb_head.js';
|
||||
|
||||
const RIL_MOBILEMESSAGEDATABASESERVICE_CONTRACTID =
|
||||
"@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1";
|
||||
const DBNAME = "test_mmdb_setmessagedeliverybyid_sms:" + newUUID();
|
||||
|
||||
const RECEIVER = "+1234567890";
|
||||
const TEXT = "The quick brown fox jumps over the lazy dog.";
|
||||
|
||||
const MESSAGE_STORE_NAME = "sms";
|
||||
|
||||
const DELIVERY_SENDING = "sending";
|
||||
const DELIVERY_SENT = "sent";
|
||||
const DELIVERY_RECEIVED = "received";
|
||||
@ -21,96 +22,113 @@ const DELIVERY_STATUS_SUCCESS = "success";
|
||||
const DELIVERY_STATUS_PENDING = "pending";
|
||||
const DELIVERY_STATUS_ERROR = "error";
|
||||
|
||||
let dbService;
|
||||
function verify(aMmdb, aMessageId, aDelivery, aDeliveryStatus, aRv, aDomMessage) {
|
||||
log(" Verify(" + aMessageId + ", " + aDelivery + ", " + aDeliveryStatus + ")");
|
||||
|
||||
/**
|
||||
* @param aMessageId
|
||||
* @param aParams
|
||||
* An array of four elements [<delivery>, <deliveryStatus>,
|
||||
* <expected delivery>, <expected deliveryStatus>].
|
||||
*/
|
||||
function setMessageDeliveryByMessageId(aMessageId, aParams) {
|
||||
if (!dbService) {
|
||||
dbService = Cc[RIL_MOBILEMESSAGEDATABASESERVICE_CONTRACTID]
|
||||
.getService(Ci.nsIRilMobileMessageDatabaseService);
|
||||
if (!dbService) {
|
||||
log(" Failed to get database service.");
|
||||
return Promise.reject();
|
||||
}
|
||||
}
|
||||
ok(Components.isSuccessCode(aRv), "Components.isSuccessCode(" + aRv + ")");
|
||||
ok(aDomMessage, "DOM message validity");
|
||||
is(aDomMessage.delivery, aDelivery, "message.delivery");
|
||||
is(aDomMessage.deliveryStatus, aDeliveryStatus, "message.deliveryStatus");
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
log(" Set to " + aParams[0] + ":" + aParams[1]);
|
||||
dbService.setMessageDeliveryByMessageId(aMessageId, null, aParams[0],
|
||||
aParams[1], null,
|
||||
function(aRv, aDomMessage) {
|
||||
if (aRv !== Cr.NS_OK) {
|
||||
deferred.reject(aRv);
|
||||
return;
|
||||
}
|
||||
// Verify deliveryIndex, sentTimestamp and deliveryTimestamp
|
||||
aMmdb.newTxn("readonly", function(aError, aTransaction, aMessageStore) {
|
||||
let messageRecord;
|
||||
aTransaction.oncomplete = function() {
|
||||
ok(true, "transaction complete");
|
||||
|
||||
is(aDomMessage.delivery, aParams[2], "message.delivery");
|
||||
is(aDomMessage.deliveryStatus, aParams[3], "message.deliveryStatus");
|
||||
is(messageRecord.deliveryIndex[0], aDelivery,
|
||||
"messageRecord.deliveryIndex[0]");
|
||||
is(messageRecord.deliveryIndex[1], messageRecord.timestamp,
|
||||
"messageRecord.deliveryIndex[1]");
|
||||
if (aDelivery == DELIVERY_SENT) {
|
||||
ok(messageRecord.sentTimestamp >= messageRecord.timestamp,
|
||||
"messageRecord.sentTimestamp");
|
||||
}
|
||||
if (aDeliveryStatus == DELIVERY_STATUS_SUCCESS) {
|
||||
ok(messageRecord.deliveryTimestamp >= messageRecord.timestamp,
|
||||
"messageRecord.deliveryTimestamp");
|
||||
}
|
||||
|
||||
deferred.resolve([aRv, aDomMessage]);
|
||||
});
|
||||
deferred.resolve(aMmdb);
|
||||
};
|
||||
|
||||
aMessageStore.get(aMessageId).onsuccess = function(event) {
|
||||
messageRecord = event.target.result;
|
||||
ok(true, "Got messageRecord " + messageRecord.id);
|
||||
};
|
||||
}, [MESSAGE_STORE_NAME]);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function test(aTitle, aParamArray) {
|
||||
function test(aTitle, aMmdb, aDeliveryStatusRequested, aParamArray) {
|
||||
log(aTitle);
|
||||
|
||||
return sendSmsWithSuccess(RECEIVER, TEXT)
|
||||
.then(function(aDomMessage) {
|
||||
let id = aDomMessage.id;
|
||||
|
||||
let promise = Promise.resolve();
|
||||
let message = {
|
||||
type: "sms",
|
||||
sender: null,
|
||||
timestamp: Date.now(),
|
||||
deliveryStatusRequested: aDeliveryStatusRequested,
|
||||
receiver: RECEIVER,
|
||||
iccId: null,
|
||||
body: TEXT,
|
||||
};
|
||||
return saveSendingMessage(aMmdb, message)
|
||||
.then(function(aValues) {
|
||||
let [resultCode, domMessage] = aValues;
|
||||
let promise =
|
||||
verify(aMmdb, domMessage.id, DELIVERY_SENDING,
|
||||
(aDeliveryStatusRequested ? DELIVERY_STATUS_PENDING
|
||||
: DELIVERY_STATUS_NOT_APPLICABLE),
|
||||
resultCode, domMessage);
|
||||
while (aParamArray.length) {
|
||||
let params = aParamArray.shift();
|
||||
promise =
|
||||
promise.then(setMessageDeliveryByMessageId.bind(null, id, params));
|
||||
let v = verify.bind(null, aMmdb, domMessage.id, params[2], params[3]);
|
||||
promise = promise
|
||||
.then(() => {
|
||||
log(" Set to " + params[0] + ":" + params[1]);
|
||||
return setMessageDeliveryByMessageId(aMmdb, domMessage.id, null,
|
||||
params[0], params[1], null);
|
||||
})
|
||||
.then((aValues) => v(aValues[0], aValues[1]));
|
||||
}
|
||||
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
startTestCommon(function testCaseMain() {
|
||||
return Promise.resolve()
|
||||
.then(test.bind(null, "Simulate send failed without delivery report requisition", [
|
||||
[DELIVERY_SENDING, DELIVERY_STATUS_NOT_APPLICABLE,
|
||||
DELIVERY_SENDING, DELIVERY_STATUS_NOT_APPLICABLE],
|
||||
startTestBase(function testCaseMain() {
|
||||
return initMobileMessageDB(newMobileMessageDB(), DBNAME, 0)
|
||||
.then((aMmdb) => test("Simulate send failed without delivery report requisition",
|
||||
aMmdb, false, [
|
||||
[DELIVERY_ERROR, DELIVERY_STATUS_ERROR,
|
||||
DELIVERY_ERROR, DELIVERY_STATUS_ERROR],
|
||||
]))
|
||||
.then(test.bind(null, "Simulate send failed with delivery report requisition", [
|
||||
[DELIVERY_SENDING, DELIVERY_STATUS_PENDING,
|
||||
DELIVERY_SENDING, DELIVERY_STATUS_PENDING],
|
||||
.then((aMmdb) => test("Simulate send failed with delivery report requisition",
|
||||
aMmdb, true, [
|
||||
[DELIVERY_ERROR, DELIVERY_STATUS_ERROR,
|
||||
DELIVERY_ERROR, DELIVERY_STATUS_ERROR],
|
||||
]))
|
||||
.then(test.bind(null, "Simulate sent without delivery report requisition", [
|
||||
[DELIVERY_SENDING, DELIVERY_STATUS_NOT_APPLICABLE,
|
||||
DELIVERY_SENDING, DELIVERY_STATUS_NOT_APPLICABLE],
|
||||
.then((aMmdb) => test("Simulate sent without delivery report requisition",
|
||||
aMmdb, false, [
|
||||
[DELIVERY_SENT, null,
|
||||
DELIVERY_SENT, DELIVERY_STATUS_NOT_APPLICABLE],
|
||||
]))
|
||||
.then(test.bind(null, "Simulate sent with delivery report success", [
|
||||
[DELIVERY_SENDING, DELIVERY_STATUS_PENDING,
|
||||
DELIVERY_SENDING, DELIVERY_STATUS_PENDING],
|
||||
.then((aMmdb) => test("Simulate sent with delivery report success",
|
||||
aMmdb, true, [
|
||||
[DELIVERY_SENT, null,
|
||||
DELIVERY_SENT, DELIVERY_STATUS_PENDING],
|
||||
[null, DELIVERY_STATUS_SUCCESS,
|
||||
DELIVERY_SENT, DELIVERY_STATUS_SUCCESS],
|
||||
]))
|
||||
.then(test.bind(null, "Simulate sent with delivery report error", [
|
||||
[DELIVERY_SENDING, DELIVERY_STATUS_PENDING,
|
||||
DELIVERY_SENDING, DELIVERY_STATUS_PENDING],
|
||||
.then((aMmdb) => test("Simulate sent with delivery report error",
|
||||
aMmdb, true, [
|
||||
[DELIVERY_SENT, null,
|
||||
DELIVERY_SENT, DELIVERY_STATUS_PENDING],
|
||||
[null, DELIVERY_ERROR,
|
||||
DELIVERY_SENT, DELIVERY_ERROR],
|
||||
]));
|
||||
]))
|
||||
.then(closeMobileMessageDB);
|
||||
});
|
||||
|
737
dom/mobilemessage/tests/marionette/test_mmdb_upgradeSchema_22.js
Normal file
737
dom/mobilemessage/tests/marionette/test_mmdb_upgradeSchema_22.js
Normal file
@ -0,0 +1,737 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'mmdb_head.js';
|
||||
|
||||
Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
|
||||
|
||||
let RIL = {};
|
||||
Cu.import("resource://gre/modules/ril_consts.js", RIL);
|
||||
|
||||
let MMS = {};
|
||||
Cu.import("resource://gre/modules/MmsPduHelper.jsm", MMS);
|
||||
|
||||
const DBNAME = "test_mmdb_upgradeSchema_22:" + newUUID();
|
||||
|
||||
const MESSAGE_STORE_NAME = "sms";
|
||||
const THREAD_STORE_NAME = "thread";
|
||||
const PARTICIPANT_STORE_NAME = "participant";
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const READ_WRITE = "readwrite";
|
||||
|
||||
const DELIVERY_SENDING = "sending";
|
||||
const DELIVERY_SENT = "sent";
|
||||
const DELIVERY_RECEIVED = "received";
|
||||
const DELIVERY_NOT_DOWNLOADED = "not-downloaded";
|
||||
const DELIVERY_ERROR = "error";
|
||||
|
||||
const DELIVERY_STATUS_NOT_APPLICABLE = "not-applicable";
|
||||
const DELIVERY_STATUS_SUCCESS = "success";
|
||||
const DELIVERY_STATUS_PENDING = "pending";
|
||||
const DELIVERY_STATUS_ERROR = "error";
|
||||
|
||||
const MESSAGE_CLASS_NORMAL = "normal";
|
||||
|
||||
const FILTER_READ_UNREAD = 0;
|
||||
const FILTER_READ_READ = 1;
|
||||
|
||||
const DISABLE_MMS_GROUPING_FOR_RECEIVING = true;
|
||||
|
||||
let LEGACY = {
|
||||
saveRecord: function(aMessageRecord, aAddresses, aCallback) {
|
||||
if (DEBUG) debug("Going to store " + JSON.stringify(aMessageRecord));
|
||||
|
||||
let self = this;
|
||||
this.newTxn(READ_WRITE, function(error, txn, stores) {
|
||||
let notifyResult = function(aRv, aMessageRecord) {
|
||||
if (!aCallback) {
|
||||
return;
|
||||
}
|
||||
let domMessage =
|
||||
aMessageRecord && self.createDomMessageFromRecord(aMessageRecord);
|
||||
aCallback.notify(aRv, domMessage);
|
||||
};
|
||||
|
||||
if (error) {
|
||||
notifyResult(error, null);
|
||||
return;
|
||||
}
|
||||
|
||||
txn.oncomplete = function oncomplete(event) {
|
||||
if (aMessageRecord.id > self.lastMessageId) {
|
||||
self.lastMessageId = aMessageRecord.id;
|
||||
}
|
||||
notifyResult(Cr.NS_OK, aMessageRecord);
|
||||
};
|
||||
txn.onabort = function onabort(event) {
|
||||
// TODO bug 832140 check event.target.errorCode
|
||||
notifyResult(Cr.NS_ERROR_FAILURE, null);
|
||||
};
|
||||
|
||||
let messageStore = stores[0];
|
||||
let participantStore = stores[1];
|
||||
let threadStore = stores[2];
|
||||
LEGACY.replaceShortMessageOnSave.call(self, txn, messageStore,
|
||||
participantStore, threadStore,
|
||||
aMessageRecord, aAddresses);
|
||||
}, [MESSAGE_STORE_NAME, PARTICIPANT_STORE_NAME, THREAD_STORE_NAME]);
|
||||
},
|
||||
|
||||
replaceShortMessageOnSave: function(aTransaction, aMessageStore,
|
||||
aParticipantStore, aThreadStore,
|
||||
aMessageRecord, aAddresses) {
|
||||
let isReplaceTypePid = (aMessageRecord.pid) &&
|
||||
((aMessageRecord.pid >= RIL.PDU_PID_REPLACE_SHORT_MESSAGE_TYPE_1 &&
|
||||
aMessageRecord.pid <= RIL.PDU_PID_REPLACE_SHORT_MESSAGE_TYPE_7) ||
|
||||
aMessageRecord.pid == RIL.PDU_PID_RETURN_CALL_MESSAGE);
|
||||
|
||||
if (aMessageRecord.type != "sms" ||
|
||||
aMessageRecord.delivery != DELIVERY_RECEIVED ||
|
||||
!isReplaceTypePid) {
|
||||
LEGACY.realSaveRecord.call(this, aTransaction, aMessageStore,
|
||||
aParticipantStore, aThreadStore,
|
||||
aMessageRecord, aAddresses);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3GPP TS 23.040 subclause 9.2.3.9 "TP-Protocol-Identifier (TP-PID)":
|
||||
//
|
||||
// ... the MS shall check the originating address and replace any
|
||||
// existing stored message having the same Protocol Identifier code
|
||||
// and originating address with the new short message and other
|
||||
// parameter values. If there is no message to be replaced, the MS
|
||||
// shall store the message in the normal way. ... it is recommended
|
||||
// that the SC address should not be checked by the MS."
|
||||
let self = this;
|
||||
this.findParticipantRecordByPlmnAddress(aParticipantStore,
|
||||
aMessageRecord.sender, false,
|
||||
function(participantRecord) {
|
||||
if (!participantRecord) {
|
||||
LEGACY.realSaveRecord.call(self, aTransaction, aMessageStore,
|
||||
aParticipantStore, aThreadStore,
|
||||
aMessageRecord, aAddresses);
|
||||
return;
|
||||
}
|
||||
|
||||
let participantId = participantRecord.id;
|
||||
let range = IDBKeyRange.bound([participantId, 0], [participantId, ""]);
|
||||
let request = aMessageStore.index("participantIds").openCursor(range);
|
||||
request.onsuccess = function onsuccess(event) {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
LEGACY.realSaveRecord.call(self, aTransaction, aMessageStore,
|
||||
aParticipantStore, aThreadStore,
|
||||
aMessageRecord, aAddresses);
|
||||
return;
|
||||
}
|
||||
|
||||
// A message record with same participantId found.
|
||||
// Verify matching criteria.
|
||||
let foundMessageRecord = cursor.value;
|
||||
if (foundMessageRecord.type != "sms" ||
|
||||
foundMessageRecord.sender != aMessageRecord.sender ||
|
||||
foundMessageRecord.pid != aMessageRecord.pid) {
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
// Match! Now replace that found message record with current one.
|
||||
aMessageRecord.id = foundMessageRecord.id;
|
||||
LEGACY.realSaveRecord.call(self, aTransaction, aMessageStore,
|
||||
aParticipantStore, aThreadStore,
|
||||
aMessageRecord, aAddresses);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
realSaveRecord: function(aTransaction, aMessageStore, aParticipantStore,
|
||||
aThreadStore, aMessageRecord, aAddresses) {
|
||||
let self = this;
|
||||
this.findThreadRecordByPlmnAddresses(aThreadStore, aParticipantStore,
|
||||
aAddresses, true,
|
||||
function(threadRecord, participantIds) {
|
||||
if (!participantIds) {
|
||||
aTransaction.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
let isOverriding = (aMessageRecord.id !== undefined);
|
||||
if (!isOverriding) {
|
||||
// |self.lastMessageId| is only updated in |txn.oncomplete|.
|
||||
aMessageRecord.id = self.lastMessageId + 1;
|
||||
}
|
||||
|
||||
let timestamp = aMessageRecord.timestamp;
|
||||
let insertMessageRecord = function(threadId) {
|
||||
// Setup threadId & threadIdIndex.
|
||||
aMessageRecord.threadId = threadId;
|
||||
aMessageRecord.threadIdIndex = [threadId, timestamp];
|
||||
// Setup participantIdsIndex.
|
||||
aMessageRecord.participantIdsIndex = [];
|
||||
for each (let id in participantIds) {
|
||||
aMessageRecord.participantIdsIndex.push([id, timestamp]);
|
||||
}
|
||||
|
||||
if (!isOverriding) {
|
||||
// Really add to message store.
|
||||
aMessageStore.put(aMessageRecord);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're going to override an old message, we need to update the
|
||||
// info of the original thread containing the overridden message.
|
||||
// To get the original thread ID and read status of the overridden
|
||||
// message record, we need to retrieve it before overriding it.
|
||||
aMessageStore.get(aMessageRecord.id).onsuccess = function(event) {
|
||||
let oldMessageRecord = event.target.result;
|
||||
aMessageStore.put(aMessageRecord);
|
||||
if (oldMessageRecord) {
|
||||
self.updateThreadByMessageChange(aMessageStore,
|
||||
aThreadStore,
|
||||
oldMessageRecord.threadId,
|
||||
aMessageRecord.id,
|
||||
oldMessageRecord.read);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
if (threadRecord) {
|
||||
let needsUpdate = false;
|
||||
|
||||
if (threadRecord.lastTimestamp <= timestamp) {
|
||||
let lastMessageSubject;
|
||||
if (aMessageRecord.type == "mms") {
|
||||
lastMessageSubject = aMessageRecord.headers.subject;
|
||||
}
|
||||
threadRecord.lastMessageSubject = lastMessageSubject || null;
|
||||
threadRecord.lastTimestamp = timestamp;
|
||||
threadRecord.body = aMessageRecord.body;
|
||||
threadRecord.lastMessageId = aMessageRecord.id;
|
||||
threadRecord.lastMessageType = aMessageRecord.type;
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
if (!aMessageRecord.read) {
|
||||
threadRecord.unreadCount++;
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
if (needsUpdate) {
|
||||
aThreadStore.put(threadRecord);
|
||||
}
|
||||
|
||||
insertMessageRecord(threadRecord.id);
|
||||
return;
|
||||
}
|
||||
|
||||
let lastMessageSubject;
|
||||
if (aMessageRecord.type == "mms") {
|
||||
lastMessageSubject = aMessageRecord.headers.subject;
|
||||
}
|
||||
|
||||
threadRecord = {
|
||||
participantIds: participantIds,
|
||||
participantAddresses: aAddresses,
|
||||
lastMessageId: aMessageRecord.id,
|
||||
lastTimestamp: timestamp,
|
||||
lastMessageSubject: lastMessageSubject || null,
|
||||
body: aMessageRecord.body,
|
||||
unreadCount: aMessageRecord.read ? 0 : 1,
|
||||
lastMessageType: aMessageRecord.type,
|
||||
};
|
||||
aThreadStore.add(threadRecord).onsuccess = function(event) {
|
||||
let threadId = event.target.result;
|
||||
insertMessageRecord(threadId);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
fillReceivedMmsThreadParticipants: function(aMessage, threadParticipants) {
|
||||
let receivers = aMessage.receivers;
|
||||
// If we don't want to disable the MMS grouping for receiving, we need to
|
||||
// add the receivers (excluding the user's own number) to the participants
|
||||
// for creating the thread. Some cases might be investigated as below:
|
||||
//
|
||||
// 1. receivers.length == 0
|
||||
// This usually happens when receiving an MMS notification indication
|
||||
// which doesn't carry any receivers.
|
||||
// 2. receivers.length == 1
|
||||
// If the receivers contain single phone number, we don't need to
|
||||
// add it into participants because we know that number is our own.
|
||||
// 3. receivers.length >= 2
|
||||
// If the receivers contain multiple phone numbers, we need to add all
|
||||
// of them but not the user's own number into participants.
|
||||
if (DISABLE_MMS_GROUPING_FOR_RECEIVING || receivers.length < 2) {
|
||||
return;
|
||||
}
|
||||
let isSuccess = false;
|
||||
let slicedReceivers = receivers.slice();
|
||||
if (aMessage.msisdn) {
|
||||
let found = slicedReceivers.indexOf(aMessage.msisdn);
|
||||
if (found !== -1) {
|
||||
isSuccess = true;
|
||||
slicedReceivers.splice(found, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSuccess) {
|
||||
// For some SIMs we cannot retrieve the vaild MSISDN (i.e. the user's
|
||||
// own phone number), so we cannot correcly exclude the user's own
|
||||
// number from the receivers, thus wrongly building the thread index.
|
||||
if (DEBUG) debug("Error! Cannot strip out user's own phone number!");
|
||||
}
|
||||
|
||||
threadParticipants = threadParticipants.concat(slicedReceivers);
|
||||
},
|
||||
|
||||
saveReceivedMessage: function(aMessage, aCallback) {
|
||||
if ((aMessage.type != "sms" && aMessage.type != "mms") ||
|
||||
(aMessage.type == "sms" && (aMessage.messageClass == undefined ||
|
||||
aMessage.sender == undefined)) ||
|
||||
(aMessage.type == "mms" && (aMessage.delivery == undefined ||
|
||||
aMessage.deliveryStatus == undefined ||
|
||||
!Array.isArray(aMessage.receivers))) ||
|
||||
aMessage.timestamp == undefined) {
|
||||
if (aCallback) {
|
||||
aCallback.notify(Cr.NS_ERROR_FAILURE, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let threadParticipants;
|
||||
if (aMessage.type == "mms") {
|
||||
if (aMessage.headers.from) {
|
||||
aMessage.sender = aMessage.headers.from.address;
|
||||
} else {
|
||||
aMessage.sender = "anonymous";
|
||||
}
|
||||
|
||||
threadParticipants = [aMessage.sender];
|
||||
LEGACY.fillReceivedMmsThreadParticipants.call(this, aMessage,
|
||||
threadParticipants);
|
||||
} else { // SMS
|
||||
threadParticipants = [aMessage.sender];
|
||||
}
|
||||
|
||||
let timestamp = aMessage.timestamp;
|
||||
|
||||
// Adding needed indexes and extra attributes for internal use.
|
||||
// threadIdIndex & participantIdsIndex are filled in saveRecord().
|
||||
aMessage.readIndex = [FILTER_READ_UNREAD, timestamp];
|
||||
aMessage.read = FILTER_READ_UNREAD;
|
||||
|
||||
// If |sentTimestamp| is not specified, use 0 as default.
|
||||
if (aMessage.sentTimestamp == undefined) {
|
||||
aMessage.sentTimestamp = 0;
|
||||
}
|
||||
|
||||
if (aMessage.type == "mms") {
|
||||
aMessage.transactionIdIndex = aMessage.headers["x-mms-transaction-id"];
|
||||
aMessage.isReadReportSent = false;
|
||||
|
||||
// As a receiver, we don't need to care about the delivery status of
|
||||
// others, so we put a single element with self's phone number in the
|
||||
// |deliveryInfo| array.
|
||||
aMessage.deliveryInfo = [{
|
||||
receiver: aMessage.phoneNumber,
|
||||
deliveryStatus: aMessage.deliveryStatus,
|
||||
deliveryTimestamp: 0,
|
||||
readStatus: MMS.DOM_READ_STATUS_NOT_APPLICABLE,
|
||||
readTimestamp: 0,
|
||||
}];
|
||||
|
||||
delete aMessage.deliveryStatus;
|
||||
}
|
||||
|
||||
if (aMessage.type == "sms") {
|
||||
aMessage.delivery = DELIVERY_RECEIVED;
|
||||
aMessage.deliveryStatus = DELIVERY_STATUS_SUCCESS;
|
||||
aMessage.deliveryTimestamp = 0;
|
||||
|
||||
if (aMessage.pid == undefined) {
|
||||
aMessage.pid = RIL.PDU_PID_DEFAULT;
|
||||
}
|
||||
}
|
||||
aMessage.deliveryIndex = [aMessage.delivery, timestamp];
|
||||
|
||||
LEGACY.saveRecord.call(this, aMessage, threadParticipants, aCallback);
|
||||
},
|
||||
|
||||
saveSendingMessage: function(aMessage, aCallback) {
|
||||
if ((aMessage.type != "sms" && aMessage.type != "mms") ||
|
||||
(aMessage.type == "sms" && aMessage.receiver == undefined) ||
|
||||
(aMessage.type == "mms" && !Array.isArray(aMessage.receivers)) ||
|
||||
aMessage.deliveryStatusRequested == undefined ||
|
||||
aMessage.timestamp == undefined) {
|
||||
if (aCallback) {
|
||||
aCallback.notify(Cr.NS_ERROR_FAILURE, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Set |aMessage.deliveryStatus|. Note that for MMS record
|
||||
// it must be an array of strings; For SMS, it's a string.
|
||||
let deliveryStatus = aMessage.deliveryStatusRequested
|
||||
? DELIVERY_STATUS_PENDING
|
||||
: DELIVERY_STATUS_NOT_APPLICABLE;
|
||||
if (aMessage.type == "sms") {
|
||||
aMessage.deliveryStatus = deliveryStatus;
|
||||
// If |deliveryTimestamp| is not specified, use 0 as default.
|
||||
if (aMessage.deliveryTimestamp == undefined) {
|
||||
aMessage.deliveryTimestamp = 0;
|
||||
}
|
||||
} else if (aMessage.type == "mms") {
|
||||
let receivers = aMessage.receivers
|
||||
if (!Array.isArray(receivers)) {
|
||||
if (DEBUG) {
|
||||
debug("Need receivers for MMS. Fail to save the sending message.");
|
||||
}
|
||||
if (aCallback) {
|
||||
aCallback.notify(Cr.NS_ERROR_FAILURE, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let readStatus = aMessage.headers["x-mms-read-report"]
|
||||
? MMS.DOM_READ_STATUS_PENDING
|
||||
: MMS.DOM_READ_STATUS_NOT_APPLICABLE;
|
||||
aMessage.deliveryInfo = [];
|
||||
for (let i = 0; i < receivers.length; i++) {
|
||||
aMessage.deliveryInfo.push({
|
||||
receiver: receivers[i],
|
||||
deliveryStatus: deliveryStatus,
|
||||
deliveryTimestamp: 0,
|
||||
readStatus: readStatus,
|
||||
readTimestamp: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let timestamp = aMessage.timestamp;
|
||||
|
||||
// Adding needed indexes and extra attributes for internal use.
|
||||
// threadIdIndex & participantIdsIndex are filled in saveRecord().
|
||||
aMessage.deliveryIndex = [DELIVERY_SENDING, timestamp];
|
||||
aMessage.readIndex = [FILTER_READ_READ, timestamp];
|
||||
aMessage.delivery = DELIVERY_SENDING;
|
||||
aMessage.messageClass = MESSAGE_CLASS_NORMAL;
|
||||
aMessage.read = FILTER_READ_READ;
|
||||
|
||||
// |sentTimestamp| is not available when the message is still sedning.
|
||||
aMessage.sentTimestamp = 0;
|
||||
|
||||
let addresses;
|
||||
if (aMessage.type == "sms") {
|
||||
addresses = [aMessage.receiver];
|
||||
} else if (aMessage.type == "mms") {
|
||||
addresses = aMessage.receivers;
|
||||
}
|
||||
LEGACY.saveRecord.call(this, aMessage, addresses, aCallback);
|
||||
},
|
||||
};
|
||||
|
||||
function callMmdbMethodLegacy(aMmdb, aMethodName) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let args = Array.slice(arguments, 2);
|
||||
args.push({
|
||||
notify: function(aRv) {
|
||||
if (!Components.isSuccessCode(aRv)) {
|
||||
ok(true, aMethodName + " returns a unsuccessful code: " + aRv);
|
||||
deferred.reject(Array.slice(arguments));
|
||||
} else {
|
||||
ok(true, aMethodName + " returns a successful code: " + aRv);
|
||||
deferred.resolve(Array.slice(arguments));
|
||||
}
|
||||
}
|
||||
});
|
||||
LEGACY[aMethodName].apply(aMmdb, args);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function saveSendingMessageLegacy(aMmdb, aMessage) {
|
||||
return callMmdbMethodLegacy(aMmdb, "saveSendingMessage", aMessage);
|
||||
}
|
||||
|
||||
function saveReceivedMessageLegacy(aMmdb, aMessage) {
|
||||
return callMmdbMethodLegacy(aMmdb, "saveReceivedMessage", aMessage);
|
||||
}
|
||||
|
||||
// Have a long long subject causes the send fails, so we don't need
|
||||
// networking here.
|
||||
const MMS_MAX_LENGTH_SUBJECT = 40;
|
||||
function genMmsSubject(sep) {
|
||||
return "Hello " + (new Array(MMS_MAX_LENGTH_SUBJECT).join(sep)) + " World!";
|
||||
}
|
||||
|
||||
function generateMms(aSender, aReceivers, aDelivery) {
|
||||
let message = {
|
||||
headers: {},
|
||||
type: "mms",
|
||||
timestamp: Date.now(),
|
||||
receivers: aReceivers,
|
||||
subject: genMmsSubject(' '),
|
||||
attachments: [],
|
||||
};
|
||||
|
||||
message.headers.subject = message.subject;
|
||||
message.headers.to = [];
|
||||
for (let i = 0; i < aReceivers.length; i++) {
|
||||
let receiver = aReceivers[i];
|
||||
let entry = { type: MMS.Address.resolveType(receiver) };
|
||||
if (entry.type == "PLMN") {
|
||||
entry.address = PhoneNumberUtils.normalize(receiver, false);
|
||||
} else {
|
||||
entry.address = receiver;
|
||||
}
|
||||
ok(true, "MMS to address '" + receiver +"' resolved as type " + entry.type);
|
||||
message.headers.to.push(entry);
|
||||
}
|
||||
if (aSender) {
|
||||
message.headers.from = {
|
||||
address: aSender,
|
||||
type: MMS.Address.resolveType(aSender)
|
||||
};
|
||||
ok(true, "MMS from address '" + aSender +"' resolved as type " +
|
||||
message.headers.from.type);
|
||||
}
|
||||
|
||||
if ((aDelivery === DELIVERY_RECEIVED) ||
|
||||
(aDelivery === DELIVERY_NOT_DOWNLOADED)) {
|
||||
message.delivery = aDelivery;
|
||||
message.deliveryStatus = DELIVERY_STATUS_SUCCESS;
|
||||
} else {
|
||||
message.deliveryStatusRequested = false;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
function generateSms(aSender, aReceiver, aDelivery) {
|
||||
let message = {
|
||||
type: "sms",
|
||||
sender: aSender,
|
||||
timestamp: Date.now(),
|
||||
receiver: aReceiver,
|
||||
body: "The snow grows white on the mountain tonight.",
|
||||
};
|
||||
|
||||
if (aDelivery === DELIVERY_RECEIVED) {
|
||||
message.messageClass = MESSAGE_CLASS_NORMAL;
|
||||
} else {
|
||||
message.deliveryStatusRequested = false;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
function matchArray(lhs, rhs) {
|
||||
if (rhs.length != lhs.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let k = 0; k < lhs.length; k++) {
|
||||
if (lhs[k] != rhs[k]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const TEST_ADDRESSES = [
|
||||
"+15525225554", // MMS, TYPE=PLMN
|
||||
"5525225554", // MMS, TYPE=PLMN
|
||||
"jkalbcjklg", // MMS, TYPE=PLMN, because of PhoneNumberNormalizer
|
||||
"jk.alb.cjk.lg", // MMS, TYPE=PLMN, because of PhoneNumberNormalizer
|
||||
"j:k:a:l:b:c:jk:lg", // MMS, TYPE=PLMN, because of PhoneNumberNormalizer
|
||||
"55.252.255.54", // MMS, TYPE=IPv4
|
||||
"5:5:2:5:2:2:55:54", // MMS, TYPE=IPv6
|
||||
"jk@alb.cjk.lg", // MMS, TYPE=email
|
||||
"___" // MMS, TYPE=Others
|
||||
];
|
||||
|
||||
function populateDatabase(aMmdb) {
|
||||
log("Populating database:");
|
||||
|
||||
let promise = Promise.resolve()
|
||||
|
||||
// We're generating other messages that would be identified as the same
|
||||
// participant with "+15525225554".
|
||||
.then(() => saveReceivedMessageLegacy(
|
||||
aMmdb, generateSms("+15525225554", null, DELIVERY_RECEIVED)))
|
||||
|
||||
// SMS, national number.
|
||||
.then(() => saveReceivedMessageLegacy(
|
||||
aMmdb, generateSms("5525225554", null, DELIVERY_RECEIVED)));
|
||||
|
||||
for (let i = 0; i < TEST_ADDRESSES.length; i++) {
|
||||
let address = TEST_ADDRESSES[i];
|
||||
promise = promise.then(() => saveReceivedMessageLegacy(
|
||||
aMmdb, generateMms(address, ["a"], DELIVERY_RECEIVED)));
|
||||
}
|
||||
|
||||
// Permutation of TEST_ADDRESSES.
|
||||
for (let i = 0; i < TEST_ADDRESSES.length; i++) {
|
||||
for (let j = i + 1; j < TEST_ADDRESSES.length; j++) {
|
||||
let addr_i = TEST_ADDRESSES[i], addr_j = TEST_ADDRESSES[j];
|
||||
promise = promise.then(() => saveSendingMessageLegacy(
|
||||
aMmdb, generateMms(null, [addr_i, addr_j], DELIVERY_SENDING)));
|
||||
}
|
||||
}
|
||||
|
||||
// At this time, we have 3 threads, whose |participants| are:
|
||||
// ["+15525225554"], ["___"], and ["+15525225554", "___"]. The number of each
|
||||
// thread are [ (2 + 8 + 8 * 7 / 2), 1, 8 ] = [ 38, 1, 8 ].
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function doVerifyDatabase(aMmdb, aExpected) {
|
||||
// 1) retrieve all threads.
|
||||
return createThreadCursor(aMmdb)
|
||||
.then(function(aValues) {
|
||||
let [errorCode, domThreads] = aValues;
|
||||
is(errorCode, 0, "errorCode");
|
||||
is(domThreads.length, aExpected.length, "domThreads.length");
|
||||
|
||||
let numMessagesInThread = [];
|
||||
let totalMessages = 0;
|
||||
|
||||
for (let i = 0; i < domThreads.length; i++) {
|
||||
let domThread = domThreads[i];
|
||||
log(" thread<" + domThread.id + "> : " + domThread.participants);
|
||||
|
||||
let index = (function() {
|
||||
let rhs = domThread.participants;
|
||||
for (let j = 0; j < aExpected.length; j++) {
|
||||
let lhs = aExpected[j].participants;
|
||||
if (matchArray(lhs, rhs)) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
})();
|
||||
// 2) make sure all retrieved threads are in expected array.
|
||||
ok(index >= 0, "validity of domThread.participants");
|
||||
|
||||
// 3) fill out numMessagesInThread, which is a <thread id> =>
|
||||
// <num messages> map.
|
||||
numMessagesInThread[domThread.id] = aExpected[index].messages;
|
||||
totalMessages += aExpected[index].messages
|
||||
|
||||
aExpected.splice(index, 1);
|
||||
}
|
||||
|
||||
// 4) make sure no thread is missing by checking |aExpected.length == 0|.
|
||||
is(aExpected.length, 0, "remaining unmatched threads");
|
||||
|
||||
// 5) retrieve all messages.
|
||||
return createMessageCursor(aMmdb, {})
|
||||
.then(function(aValues) {
|
||||
let [errorCode, domMessages] = aValues;
|
||||
is(errorCode, 0, "errorCode");
|
||||
// 6) check total number of messages.
|
||||
is(domMessages.length, totalMessages, "domMessages.length");
|
||||
|
||||
for (let i = 0; i < domMessages.length; i++) {
|
||||
let domMessage = domMessages[i];
|
||||
// 7) make sure message thread id is valid by checking
|
||||
// |numMessagesInThread[domMessage.threadId] != null|.
|
||||
ok(numMessagesInThread[domMessage.threadId] != null,
|
||||
"domMessage.threadId");
|
||||
|
||||
// 8) for each message, reduce
|
||||
// |numMessagesInThread[domMessage.threadId]| by 1.
|
||||
--numMessagesInThread[domMessage.threadId];
|
||||
ok(true, "numMessagesInThread[" + domMessage.threadId + "] = " +
|
||||
numMessagesInThread[domMessage.threadId]);
|
||||
}
|
||||
|
||||
// 9) check if |numMessagesInThread| is now an array of all zeros.
|
||||
for (let i = 0; i < domThreads.length; i++) {
|
||||
let domThread = domThreads[i];
|
||||
is(numMessagesInThread[domThread.id], 0,
|
||||
"numMessagesInThread[" + domThread.id + "]");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function verifyDatabaseBeforeUpgrade(aMmdb) {
|
||||
log("Before updateSchema22:");
|
||||
return doVerifyDatabase(aMmdb, [{
|
||||
participants: ["+15525225554"],
|
||||
messages: 38 // 2 + (9 - 1) + (9 - 1) * ( 9 - 1 - 1) / 2
|
||||
}, {
|
||||
participants: ["___"],
|
||||
messages: 1
|
||||
}, {
|
||||
participants: ["+15525225554", "___"],
|
||||
messages: 8
|
||||
}]);
|
||||
}
|
||||
|
||||
function verifyDatabaseAfterUpgrade(aMmdb) {
|
||||
log("After updateSchema22:");
|
||||
return doVerifyDatabase(aMmdb, [{
|
||||
participants: ["+15525225554"],
|
||||
messages: 17 // 2 + 5 + 5 * (5 - 1) / 2
|
||||
}, {
|
||||
participants: ["55.252.255.54"],
|
||||
messages: 1
|
||||
}, {
|
||||
participants: ["5:5:2:5:2:2:55:54"],
|
||||
messages: 1
|
||||
}, {
|
||||
participants: ["jk@alb.cjk.lg"],
|
||||
messages: 1
|
||||
}, {
|
||||
participants: ["___"],
|
||||
messages: 1
|
||||
}, {
|
||||
participants: ["+15525225554", "55.252.255.54"],
|
||||
messages: 5
|
||||
}, {
|
||||
participants: ["+15525225554", "5:5:2:5:2:2:55:54"],
|
||||
messages: 5
|
||||
}, {
|
||||
participants: ["+15525225554", "jk@alb.cjk.lg"],
|
||||
messages: 5
|
||||
}, {
|
||||
participants: ["+15525225554", "___"],
|
||||
messages: 5
|
||||
}, {
|
||||
participants: ["55.252.255.54", "5:5:2:5:2:2:55:54"],
|
||||
messages: 1
|
||||
}, {
|
||||
participants: ["55.252.255.54", "jk@alb.cjk.lg"],
|
||||
messages: 1
|
||||
}, {
|
||||
participants: ["55.252.255.54", "___"],
|
||||
messages: 1
|
||||
}, {
|
||||
participants: ["5:5:2:5:2:2:55:54", "jk@alb.cjk.lg"],
|
||||
messages: 1
|
||||
}, {
|
||||
participants: ["5:5:2:5:2:2:55:54", "___"],
|
||||
messages: 1
|
||||
}, {
|
||||
participants: ["jk@alb.cjk.lg", "___"],
|
||||
messages: 1
|
||||
}]);
|
||||
}
|
||||
|
||||
startTestBase(function testCaseMain() {
|
||||
let mmdb = newMobileMessageDB();
|
||||
return initMobileMessageDB(mmdb, DBNAME, 22)
|
||||
.then(() => populateDatabase(mmdb))
|
||||
.then(() => verifyDatabaseBeforeUpgrade(mmdb))
|
||||
.then(() => closeMobileMessageDB(mmdb))
|
||||
|
||||
.then(() => initMobileMessageDB(mmdb, DBNAME, 23))
|
||||
.then(() => verifyDatabaseAfterUpgrade(mmdb))
|
||||
.then(() => closeMobileMessageDB(mmdb));
|
||||
});
|
@ -0,0 +1,171 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'mmdb_head.js';
|
||||
|
||||
const DBNAME = "test_mmdb_upgradeSchema_current_structure:" + newUUID();
|
||||
|
||||
const LAYOUT = {
|
||||
sms: {
|
||||
keyPath: "id",
|
||||
autoIncrement: false,
|
||||
indice: {
|
||||
delivery: {
|
||||
keyPath: "deliveryIndex",
|
||||
multiEntry: false,
|
||||
unique: false,
|
||||
},
|
||||
|
||||
envelopeId: {
|
||||
keyPath: "envelopeIdIndex",
|
||||
multiEntry: false,
|
||||
unique: true,
|
||||
},
|
||||
|
||||
participantIds: {
|
||||
keyPath: "participantIdsIndex",
|
||||
multiEntry: true,
|
||||
unique: false,
|
||||
},
|
||||
|
||||
read: {
|
||||
keyPath: "readIndex",
|
||||
multiEntry: false,
|
||||
unique: false,
|
||||
},
|
||||
|
||||
threadId: {
|
||||
keyPath: "threadIdIndex",
|
||||
multiEntry: false,
|
||||
unique: false,
|
||||
},
|
||||
|
||||
timestamp: {
|
||||
keyPath: "timestamp",
|
||||
multiEntry: false,
|
||||
unique: false,
|
||||
},
|
||||
|
||||
transactionId: {
|
||||
keyPath: "transactionIdIndex",
|
||||
multiEntry: false,
|
||||
unique: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
thread: {
|
||||
keyPath: "id",
|
||||
autoIncrement: true,
|
||||
indice: {
|
||||
lastTimestamp: {
|
||||
keyPath: "lastTimestamp",
|
||||
multiEntry: false,
|
||||
unique: false,
|
||||
},
|
||||
|
||||
participantIds: {
|
||||
keyPath: "participantIds",
|
||||
multiEntry: false,
|
||||
unique: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
participant: {
|
||||
keyPath: "id",
|
||||
autoIncrement: true,
|
||||
indice: {
|
||||
addresses: {
|
||||
keyPath: "addresses",
|
||||
multiEntry: true,
|
||||
unique: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
"sms-segment": {
|
||||
keyPath: "id",
|
||||
autoIncrement: true,
|
||||
indice: {
|
||||
hash: {
|
||||
keyPath: "hash",
|
||||
multiEntry: false,
|
||||
unique: true,
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
function verifyIndex(aIndex, aIndexLayout) {
|
||||
log(" Verifying index '" + aIndex.name + "'");
|
||||
|
||||
is(aIndex.keyPath, aIndexLayout.keyPath, "aIndex.keyPath");
|
||||
is(aIndex.multiEntry, aIndexLayout.multiEntry, "aIndex.multiEntry");
|
||||
is(aIndex.unique, aIndexLayout.unique, "aIndex.unique");
|
||||
}
|
||||
|
||||
function verifyStore(aObjectStore, aStoreLayout) {
|
||||
log("Verifying object store '" + aObjectStore.name + "'");
|
||||
|
||||
is(aObjectStore.keyPath, aStoreLayout.keyPath, "aObjectStore.keyPath");
|
||||
is(aObjectStore.autoIncrement, aStoreLayout.autoIncrement,
|
||||
"aObjectStore.autoIncrement");
|
||||
|
||||
let expectedIndexNames = Object.keys(aStoreLayout.indice);
|
||||
for (let i = 0; i < aObjectStore.indexNames.length; i++) {
|
||||
let indexName = aObjectStore.indexNames.item(i);
|
||||
|
||||
let index = expectedIndexNames.indexOf(indexName);
|
||||
ok(index >= 0, "Index name '" + indexName + "' validity");
|
||||
expectedIndexNames.splice(index, 1);
|
||||
|
||||
verifyIndex(aObjectStore.index(indexName), aStoreLayout.indice[indexName]);
|
||||
}
|
||||
|
||||
// All index names should have been verified and leaves expectedIndexNames an
|
||||
// empty array.
|
||||
is(expectedIndexNames.length, 0, "Extra indice: " + expectedIndexNames);
|
||||
}
|
||||
|
||||
function verifyDatabase(aMmdb) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let expectedStoreNames = Object.keys(LAYOUT);
|
||||
aMmdb.newTxn("readonly", function(aError, aTransaction, aObjectStores) {
|
||||
if (!Array.isArray(aObjectStores)) {
|
||||
// When we have only one object store open, aObjectStores is an instance
|
||||
// of IDBObjectStore. Push it to an array for convenience here.
|
||||
aObjectStores = [aObjectStores];
|
||||
}
|
||||
|
||||
is(aObjectStores.length, expectedStoreNames.length,
|
||||
"expected number of object stores");
|
||||
|
||||
let slicedStoreNames = expectedStoreNames.slice();
|
||||
for (let i = 0; i < aObjectStores.length; i++) {
|
||||
let objectStore = aObjectStores[i];
|
||||
|
||||
let index = slicedStoreNames.indexOf(objectStore.name);
|
||||
ok(index >= 0, "objectStore.name '" + objectStore.name + "' validity");
|
||||
slicedStoreNames.splice(index, 1);
|
||||
|
||||
verifyStore(objectStore, LAYOUT[objectStore.name]);
|
||||
}
|
||||
|
||||
// All store names should have been verified and leaves slicedStoreNames an
|
||||
// empty array.
|
||||
is(slicedStoreNames.length, 0, "Extra object stores: " + slicedStoreNames);
|
||||
|
||||
deferred.resolve(aMmdb);
|
||||
}, expectedStoreNames);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
startTestBase(function testCaseMain() {
|
||||
return initMobileMessageDB(newMobileMessageDB(), DBNAME, 0)
|
||||
.then(verifyDatabase)
|
||||
.then(closeMobileMessageDB);
|
||||
});
|
@ -50,30 +50,155 @@ add_test(function test_Address_decode() {
|
||||
{address: "+123.456-789", type: "PLMN"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("123456789/TYPE=PLMN"),
|
||||
{address: "123456789", type: "PLMN"});
|
||||
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("a23456789/TYPE=PLMN"),
|
||||
null, "CodeError");
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("++123456789/TYPE=PLMN"),
|
||||
null, "CodeError");
|
||||
|
||||
// Test for IPv4
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1.2.3.4/TYPE=IPv4"),
|
||||
{address: "1.2.3.4", type: "IPv4"});
|
||||
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1.2.3.256/TYPE=IPv4"),
|
||||
null, "CodeError");
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1.2.3.00/TYPE=IPv4"),
|
||||
null, "CodeError");
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1.2.3.a/TYPE=IPv4"),
|
||||
null, "CodeError");
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1.2.3/TYPE=IPv4"),
|
||||
null, "CodeError");
|
||||
|
||||
// Test for IPv6
|
||||
wsp_decode_test(MMS.Address,
|
||||
strToCharCodeArray("1111:AAAA:bbbb:CdEf:1ABC:2cde:3Def:0000/TYPE=IPv6"),
|
||||
{address: "1111:AAAA:bbbb:CdEf:1ABC:2cde:3Def:0000", type: "IPv6"}
|
||||
);
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4:5:6:7:8/TYPE=IPv6"),
|
||||
{address: "1:2:3:4:5:6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4:5:6:7::/TYPE=IPv6"),
|
||||
{address: "1:2:3:4:5:6:7::", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4:5:6::/TYPE=IPv6"),
|
||||
{address: "1:2:3:4:5:6::", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4:5::/TYPE=IPv6"),
|
||||
{address: "1:2:3:4:5::", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4::/TYPE=IPv6"),
|
||||
{address: "1:2:3:4::", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3::/TYPE=IPv6"),
|
||||
{address: "1:2:3::", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2::/TYPE=IPv6"),
|
||||
{address: "1:2::", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1::/TYPE=IPv6"),
|
||||
{address: "1::", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("::/TYPE=IPv6"),
|
||||
{address: "::", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4:5:6::8/TYPE=IPv6"),
|
||||
{address: "1:2:3:4:5:6::8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4:5::8/TYPE=IPv6"),
|
||||
{address: "1:2:3:4:5::8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4::8/TYPE=IPv6"),
|
||||
{address: "1:2:3:4::8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3::8/TYPE=IPv6"),
|
||||
{address: "1:2:3::8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2::8/TYPE=IPv6"),
|
||||
{address: "1:2::8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1::8/TYPE=IPv6"),
|
||||
{address: "1::8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("::8/TYPE=IPv6"),
|
||||
{address: "::8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4:5::7:8/TYPE=IPv6"),
|
||||
{address: "1:2:3:4:5::7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4::7:8/TYPE=IPv6"),
|
||||
{address: "1:2:3:4::7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3::7:8/TYPE=IPv6"),
|
||||
{address: "1:2:3::7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2::7:8/TYPE=IPv6"),
|
||||
{address: "1:2::7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1::7:8/TYPE=IPv6"),
|
||||
{address: "1::7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("::7:8/TYPE=IPv6"),
|
||||
{address: "::7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3:4::6:7:8/TYPE=IPv6"),
|
||||
{address: "1:2:3:4::6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3::6:7:8/TYPE=IPv6"),
|
||||
{address: "1:2:3::6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2::6:7:8/TYPE=IPv6"),
|
||||
{address: "1:2::6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1::6:7:8/TYPE=IPv6"),
|
||||
{address: "1::6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("::6:7:8/TYPE=IPv6"),
|
||||
{address: "::6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2:3::5:6:7:8/TYPE=IPv6"),
|
||||
{address: "1:2:3::5:6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2::5:6:7:8/TYPE=IPv6"),
|
||||
{address: "1:2::5:6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1::5:6:7:8/TYPE=IPv6"),
|
||||
{address: "1::5:6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("::5:6:7:8/TYPE=IPv6"),
|
||||
{address: "::5:6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:2::4:5:6:7:8/TYPE=IPv6"),
|
||||
{address: "1:2::4:5:6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1::4:5:6:7:8/TYPE=IPv6"),
|
||||
{address: "1::4:5:6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("::4:5:6:7:8/TYPE=IPv6"),
|
||||
{address: "::4:5:6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1::3:4:5:6:7:8/TYPE=IPv6"),
|
||||
{address: "1::3:4:5:6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("::3:4:5:6:7:8/TYPE=IPv6"),
|
||||
{address: "::3:4:5:6:7:8", type: "IPv6"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("::2:3:4:5:6:7:8/TYPE=IPv6"),
|
||||
{address: "::2:3:4:5:6:7:8", type: "IPv6"});
|
||||
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1:g:3:4:5:6:7:8/TYPE=IPv6"),
|
||||
null, "CodeError");
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1::3:4::6:7:8/TYPE=IPv6"),
|
||||
null, "CodeError");
|
||||
|
||||
// Test for other device-address
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("+H-e.l%l_o/TYPE=W0r1d_"),
|
||||
{address: "+H-e.l%l_o", type: "W0r1d_"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("+H-e.1%l_o/TYPE=W0r1d_"),
|
||||
{address: "+H-e.1%l_o", type: "W0r1d_"});
|
||||
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("addr/TYPE=type!"),
|
||||
null, "CodeError");
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("addr!/TYPE=type"),
|
||||
null, "CodeError");
|
||||
|
||||
// Test for num-shortcode
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1"),
|
||||
{address: "1", type: "num"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("123"),
|
||||
{address: "123", type: "num"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("+123"),
|
||||
{address: "+123", type: "num"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("*123"),
|
||||
{address: "*123", type: "num"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("#123"),
|
||||
{address: "#123", type: "num"});
|
||||
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("++123"),
|
||||
null, "CodeError");
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("!123"),
|
||||
null, "CodeError");
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("1*23"),
|
||||
null, "CodeError");
|
||||
|
||||
// Test for alphanum-shortcode
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("H0wD0Y0uTurnTh1s0n"),
|
||||
{address: "H0wD0Y0uTurnTh1s0n", type: "alphanum"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("a"),
|
||||
{address: "a", type: "alphanum"});
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("H0w_D0_Y0u_Turn_Th1s_0n"),
|
||||
{address: "H0w_D0_Y0u_Turn_Th1s_0n", type: "alphanum"});
|
||||
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("abc#"),
|
||||
null, "CodeError");
|
||||
|
||||
// Test for email address
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("Joe User <joe@user.org>"),
|
||||
{address: "Joe User <joe@user.org>", type: "email"});
|
||||
wsp_decode_test(MMS.Address,
|
||||
strToCharCodeArray("a-z.A-.Z.0-9!#$.%&.'*+./=?^._`{|}~-@a-.zA-Z.0-9!.#$%&'.*+/=?.^_`.{|}~-"),
|
||||
{address: "a-z.A-.Z.0-9!#$.%&.'*+./=?^._`{|}~-@a-.zA-Z.0-9!.#$%&'.*+/=?.^_`.{|}~-", type: "email"}
|
||||
);
|
||||
|
||||
// Test for invalid address
|
||||
wsp_decode_test(MMS.Address, strToCharCodeArray("@@@@@"),
|
||||
null, "CodeError");
|
||||
@ -89,30 +214,182 @@ add_test(function test_Address_encode() {
|
||||
strToCharCodeArray("+123.456-789/TYPE=PLMN"));
|
||||
wsp_encode_test(MMS.Address, {address: "123456789", type: "PLMN"},
|
||||
strToCharCodeArray("123456789/TYPE=PLMN"));
|
||||
|
||||
wsp_encode_test(MMS.Address, {address: "a23456789", type: "PLMN"},
|
||||
null, "CodeError");
|
||||
wsp_encode_test(MMS.Address, {address: "++123456789", type: "PLMN"},
|
||||
null, "CodeError");
|
||||
|
||||
// Test for IPv4
|
||||
wsp_encode_test(MMS.Address, {address: "1.2.3.4", type: "IPv4"},
|
||||
strToCharCodeArray("1.2.3.4/TYPE=IPv4"));
|
||||
|
||||
wsp_encode_test(MMS.Address, {address: "1.2.3.256", type: "IPv4"},
|
||||
null, "CodeError");
|
||||
wsp_encode_test(MMS.Address, {address: "1.2.3.00", type: "IPv4"},
|
||||
null, "CodeError");
|
||||
wsp_encode_test(MMS.Address, {address: "1.2.3.a", type: "IPv4"},
|
||||
null, "CodeError");
|
||||
wsp_encode_test(MMS.Address, {address: "1.2.3", type: "IPv4"},
|
||||
null, "CodeError");
|
||||
|
||||
// Test for IPv6
|
||||
wsp_encode_test(MMS.Address,
|
||||
{address: "1111:AAAA:bbbb:CdEf:1ABC:2cde:3Def:0000", type: "IPv6"},
|
||||
strToCharCodeArray("1111:AAAA:bbbb:CdEf:1ABC:2cde:3Def:0000/TYPE=IPv6")
|
||||
);
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4:5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4:5:6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4:5:6:7::", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4:5:6:7::/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4:5:6::", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4:5:6::/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4:5::", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4:5::/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4::", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4::/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3::", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3::/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2::", type: "IPv6"},
|
||||
strToCharCodeArray("1:2::/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1::", type: "IPv6"},
|
||||
strToCharCodeArray("1::/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "::", type: "IPv6"},
|
||||
strToCharCodeArray("::/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4:5:6::8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4:5:6::8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4:5::8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4:5::8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4::8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4::8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3::8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3::8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2::8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2::8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1::8", type: "IPv6"},
|
||||
strToCharCodeArray("1::8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "::8", type: "IPv6"},
|
||||
strToCharCodeArray("::8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4:5::7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4:5::7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4::7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4::7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3::7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3::7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2::7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2::7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1::7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1::7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "::7:8", type: "IPv6"},
|
||||
strToCharCodeArray("::7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3:4::6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3:4::6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3::6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3::6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2::6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2::6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1::6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1::6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "::6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("::6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2:3::5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2:3::5:6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2::5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2::5:6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1::5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1::5:6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "::5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("::5:6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1:2::4:5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1:2::4:5:6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1::4:5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1::4:5:6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "::4:5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("::4:5:6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "1::3:4:5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("1::3:4:5:6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "::3:4:5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("::3:4:5:6:7:8/TYPE=IPv6"));
|
||||
wsp_encode_test(MMS.Address, {address: "::2:3:4:5:6:7:8", type: "IPv6"},
|
||||
strToCharCodeArray("::2:3:4:5:6:7:8/TYPE=IPv6"));
|
||||
|
||||
wsp_encode_test(MMS.Address, {address: "1:g:3:4:5:6:7:8", type: "IPv6"},
|
||||
null, "CodeError");
|
||||
wsp_encode_test(MMS.Address, {address: "1::3:4:5:6::8", type: "IPv6"},
|
||||
null, "CodeError");
|
||||
|
||||
// Test for other device-address
|
||||
wsp_encode_test(MMS.Address, {address: "+H-e.l%l_o", type: "W0r1d_"},
|
||||
strToCharCodeArray("+H-e.l%l_o/TYPE=W0r1d_"));
|
||||
wsp_encode_test(MMS.Address, {address: "+H-e.1%l_o", type: "W0r1d_"},
|
||||
strToCharCodeArray("+H-e.1%l_o/TYPE=W0r1d_"));
|
||||
|
||||
wsp_encode_test(MMS.Address, {address: "addr!", type: "type"},
|
||||
null, "CodeError");
|
||||
wsp_encode_test(MMS.Address, {address: "addr", type: "type!"},
|
||||
null, "CodeError");
|
||||
|
||||
// Test for num-shortcode
|
||||
wsp_encode_test(MMS.Address, {address: "1", type: "num"},
|
||||
strToCharCodeArray("1"));
|
||||
wsp_encode_test(MMS.Address, {address: "123", type: "num"},
|
||||
strToCharCodeArray("123"));
|
||||
wsp_encode_test(MMS.Address, {address: "+123", type: "num"},
|
||||
strToCharCodeArray("+123"));
|
||||
wsp_encode_test(MMS.Address, {address: "*123", type: "num"},
|
||||
strToCharCodeArray("*123"));
|
||||
wsp_encode_test(MMS.Address, {address: "#123", type: "num"},
|
||||
strToCharCodeArray("#123"));
|
||||
|
||||
wsp_encode_test(MMS.Address, {address: "++123", type: "num"},
|
||||
null, "CodeError");
|
||||
wsp_encode_test(MMS.Address, {address: "!123", type: "num"},
|
||||
null, "CodeError");
|
||||
wsp_encode_test(MMS.Address, {address: "1#23", type: "num"},
|
||||
null, "CodeError");
|
||||
|
||||
// Test for alphanum-shortcode
|
||||
wsp_encode_test(MMS.Address, {address: "H0wD0Y0uTurnTh1s0n", type: "alphanum"},
|
||||
strToCharCodeArray("H0wD0Y0uTurnTh1s0n"));
|
||||
wsp_encode_test(MMS.Address, {address: "a", type: "alphanum"},
|
||||
strToCharCodeArray("a"));
|
||||
wsp_encode_test(MMS.Address, {address: "1", type: "alphanum"},
|
||||
strToCharCodeArray("1"));
|
||||
wsp_encode_test(MMS.Address, {address: "H0w_D0_Y0u_Turn_Th1s_0n", type: "alphanum"},
|
||||
strToCharCodeArray("H0w_D0_Y0u_Turn_Th1s_0n"));
|
||||
|
||||
wsp_encode_test(MMS.Address, {address: "abc#", type: "alphanum"},
|
||||
null, "CodeError");
|
||||
|
||||
// Test for email address
|
||||
wsp_encode_test(MMS.Address, {address: "Joe User <joe@user.org>", type: "email"},
|
||||
strToCharCodeArray("Joe User <joe@user.org>"));
|
||||
wsp_encode_test(MMS.Address,
|
||||
{address: "a-z.A-.Z.0-9!#$.%&.'*+./=?^._`{|}~-@a-.zA-Z.0-9!.#$%&'.*+/=?.^_`.{|}~-", type: "email"},
|
||||
strToCharCodeArray("a-z.A-.Z.0-9!#$.%&.'*+./=?^._`{|}~-@a-.zA-Z.0-9!.#$%&'.*+/=?.^_`.{|}~-")
|
||||
);
|
||||
|
||||
// Test for invalid address
|
||||
wsp_encode_test(MMS.Address, {address: "a"}, null, "CodeError");
|
||||
wsp_encode_test(MMS.Address, {type: "alphanum"}, null, "CodeError");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
//// Address.resolveType ////
|
||||
|
||||
add_test(function test_Address_encode() {
|
||||
function test(address, type) {
|
||||
do_check_eq(MMS.Address.resolveType(address), type);
|
||||
}
|
||||
|
||||
// Test ambiguous addresses.
|
||||
test("+15525225554", "PLMN");
|
||||
test("5525225554", "PLMN");
|
||||
test("jkalbcjklg", "PLMN");
|
||||
test("jk.alb.cjk.lg", "PLMN");
|
||||
test("j:k:a:l:b:c:jk:lg", "PLMN");
|
||||
test("55.252.255.54", "IPv4");
|
||||
test("5:5:2:5:2:2:55:54", "IPv6");
|
||||
test("jk@alb.cjk.lg", "email");
|
||||
// Test empty address. This is for received anonymous MMS messages.
|
||||
test("", "Others");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -11,14 +11,12 @@ import org.mozilla.gecko.toolbar.BrowserToolbar.OnDismissListener;
|
||||
import org.mozilla.gecko.toolbar.BrowserToolbar.OnFilterListener;
|
||||
import org.mozilla.gecko.CustomEditText;
|
||||
import org.mozilla.gecko.CustomEditText.OnKeyPreImeListener;
|
||||
import org.mozilla.gecko.InputMethods;
|
||||
import org.mozilla.gecko.util.GamepadUtils;
|
||||
import org.mozilla.gecko.util.StringUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
@ -27,13 +25,11 @@ import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnKeyListener;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
/**
|
||||
* {@code ToolbarEditText} is the text entry used when the toolbar
|
||||
* is in edit state. It handles all the necessary input method machinery
|
||||
* as well as the tracking of different text types (empty, search, or url).
|
||||
* is in edit state. It handles all the necessary input method machinery.
|
||||
* It's meant to be owned by {@code ToolbarEditLayout}.
|
||||
*/
|
||||
public class ToolbarEditText extends CustomEditText
|
||||
@ -41,28 +37,11 @@ public class ToolbarEditText extends CustomEditText
|
||||
|
||||
private static final String LOGTAG = "GeckoToolbarEditText";
|
||||
|
||||
// Used to track the current type of content in the
|
||||
// text entry so that ToolbarEditLayout can update its
|
||||
// state accordingly.
|
||||
enum TextType {
|
||||
EMPTY,
|
||||
SEARCH_QUERY,
|
||||
URL
|
||||
}
|
||||
|
||||
interface OnTextTypeChangeListener {
|
||||
public void onTextTypeChange(ToolbarEditText editText, TextType textType);
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
// Type of the URL bar go/search button
|
||||
private TextType mToolbarTextType;
|
||||
|
||||
private OnCommitListener mCommitListener;
|
||||
private OnDismissListener mDismissListener;
|
||||
private OnFilterListener mFilterListener;
|
||||
private OnTextTypeChangeListener mTextTypeListener;
|
||||
|
||||
// The previous autocomplete result returned to us
|
||||
private String mAutoCompleteResult = "";
|
||||
@ -73,8 +52,6 @@ public class ToolbarEditText extends CustomEditText
|
||||
public ToolbarEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
|
||||
mToolbarTextType = TextType.EMPTY;
|
||||
}
|
||||
|
||||
void setOnCommitListener(OnCommitListener listener) {
|
||||
@ -89,10 +66,6 @@ public class ToolbarEditText extends CustomEditText
|
||||
mFilterListener = listener;
|
||||
}
|
||||
|
||||
void setOnTextTypeChangeListener(OnTextTypeChangeListener listener) {
|
||||
mTextTypeListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
setOnKeyListener(new KeyListener());
|
||||
@ -144,12 +117,6 @@ public class ToolbarEditText extends CustomEditText
|
||||
setSelection(text.length(), result.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
updateTextTypeFromText(getText().toString());
|
||||
}
|
||||
|
||||
private void resetAutocompleteState() {
|
||||
mAutoCompleteResult = "";
|
||||
mAutoCompletePrefix = null;
|
||||
@ -170,29 +137,6 @@ public class ToolbarEditText extends CustomEditText
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setTextType(TextType textType) {
|
||||
mToolbarTextType = textType;
|
||||
|
||||
if (mTextTypeListener != null) {
|
||||
mTextTypeListener.onTextTypeChange(this, textType);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTextTypeFromText(String text) {
|
||||
if (text.length() == 0) {
|
||||
setTextType(TextType.EMPTY);
|
||||
return;
|
||||
}
|
||||
|
||||
final TextType newType;
|
||||
if (StringUtils.isSearchQuery(text, mToolbarTextType == TextType.SEARCH_QUERY)) {
|
||||
newType = TextType.SEARCH_QUERY;
|
||||
} else {
|
||||
newType = TextType.URL;
|
||||
}
|
||||
setTextType(newType);
|
||||
}
|
||||
|
||||
private class TextChangeListener implements TextWatcher {
|
||||
@Override
|
||||
public void afterTextChanged(final Editable s) {
|
||||
@ -232,8 +176,6 @@ public class ToolbarEditText extends CustomEditText
|
||||
onAutocomplete(mAutoCompleteResult);
|
||||
}
|
||||
}
|
||||
|
||||
updateTextTypeFromText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user