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
f50b1bd5df
1
.hgtags
1
.hgtags
@ -105,3 +105,4 @@ ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE-m
|
||||
0000000000000000000000000000000000000000 FIREFOX_AURORA_29_BASE-m
|
||||
ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE
|
||||
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
|
||||
83c9853e136451474dfa6d1aaa60a7fca7d2d83a FIREFOX_AURORA_30_BASE
|
||||
|
@ -676,6 +676,19 @@ pref("hal.processPriorityManager.gonk.BACKGROUND.Nice", 18);
|
||||
// Processes get this niceness when they have low CPU priority.
|
||||
pref("hal.processPriorityManager.gonk.LowCPUNice", 18);
|
||||
|
||||
// By default the compositor thread on gonk runs without real-time priority. RT
|
||||
// priority can be enabled by setting this pref to a value between 1 and 99.
|
||||
// Note that audio processing currently runs at RT priority 2 or 3 at most.
|
||||
//
|
||||
// If RT priority is disabled, then the compositor nice value is used. The
|
||||
// code will default to ANDROID_PRIORITY_URGENT_DISPLAY which is -8. Per gfx
|
||||
// request we are keeping the compositor at nice level 0 until we can complete
|
||||
// the investigation in bug 982972.
|
||||
//
|
||||
// Do not change these values without gfx team review.
|
||||
pref("hal.gonk.compositor.rt_priority", 0);
|
||||
pref("hal.gonk.compositor.nice", 0);
|
||||
|
||||
// Fire a memory pressure event when the system has less than Xmb of memory
|
||||
// remaining. You should probably set this just above Y.KillUnderKB for
|
||||
// the highest priority class Y that you want to make an effort to keep alive.
|
||||
|
@ -44,9 +44,10 @@ let developerHUD = {
|
||||
|
||||
/**
|
||||
* This method registers a metric watcher that will watch one or more metrics
|
||||
* of apps that are being tracked. A watcher must implement the trackApp(app)
|
||||
* and untrackApp(app) methods, add entries to the app.metrics map, keep them
|
||||
* up-to-date, and call app.display() when values were changed.
|
||||
* on app frames that are being tracked. A watcher must implement the
|
||||
* `trackTarget(target)` and `untrackTarget(target)` methods, register
|
||||
* observed metrics with `target.register(metric)`, and keep them up-to-date
|
||||
* with `target.update(metric, value, message)` when necessary.
|
||||
*/
|
||||
registerWatcher: function dwp_registerWatcher(watcher) {
|
||||
this._watchers.unshift(watcher);
|
||||
@ -87,7 +88,7 @@ let developerHUD = {
|
||||
});
|
||||
});
|
||||
|
||||
SettingsListener.observe('hud.logging', enabled => {
|
||||
SettingsListener.observe('hud.logging', this._logging, enabled => {
|
||||
this._logging = enabled;
|
||||
});
|
||||
},
|
||||
@ -194,9 +195,9 @@ let developerHUD = {
|
||||
|
||||
|
||||
/**
|
||||
* An App object represents all there is to know about a Firefox OS app that is
|
||||
* being tracked, e.g. its manifest information, current values of watched
|
||||
* metrics, and how to update these values on the front-end.
|
||||
* A Target object represents all there is to know about a Firefox OS app frame
|
||||
* that is being tracked, e.g. a pointer to the frame, current values of watched
|
||||
* metrics, and how to notify the front-end when metrics have changed.
|
||||
*/
|
||||
function Target(frame, actor) {
|
||||
this.frame = frame;
|
||||
@ -276,17 +277,27 @@ Target.prototype = {
|
||||
|
||||
/**
|
||||
* The Console Watcher tracks the following metrics in apps: reflows, warnings,
|
||||
* and errors.
|
||||
* and errors, with security errors reported separately.
|
||||
*/
|
||||
let consoleWatcher = {
|
||||
|
||||
_client: null,
|
||||
_targets: new Map(),
|
||||
_watching: {
|
||||
reflows: false,
|
||||
warnings: false,
|
||||
errors: false
|
||||
errors: false,
|
||||
security: false
|
||||
},
|
||||
_client: null,
|
||||
_security: [
|
||||
'Mixed Content Blocker',
|
||||
'Mixed Content Message',
|
||||
'CSP',
|
||||
'Invalid HSTS Headers',
|
||||
'Insecure Password Field',
|
||||
'SSL',
|
||||
'CORS'
|
||||
],
|
||||
|
||||
init: function cw_init(client) {
|
||||
this._client = client;
|
||||
@ -296,7 +307,7 @@ let consoleWatcher = {
|
||||
|
||||
for (let key in watching) {
|
||||
let metric = key;
|
||||
SettingsListener.observe('hud.' + metric, false, watch => {
|
||||
SettingsListener.observe('hud.' + metric, watching[metric], watch => {
|
||||
// Watch or unwatch the metric.
|
||||
if (watching[metric] = watch) {
|
||||
return;
|
||||
@ -319,6 +330,7 @@ let consoleWatcher = {
|
||||
target.register('reflows');
|
||||
target.register('warnings');
|
||||
target.register('errors');
|
||||
target.register('security');
|
||||
|
||||
this._client.request({
|
||||
to: target.actor.consoleActor,
|
||||
@ -357,13 +369,17 @@ let consoleWatcher = {
|
||||
output += 'error (';
|
||||
}
|
||||
|
||||
if (this._security.indexOf(pageError.category) > -1) {
|
||||
metric = 'security';
|
||||
}
|
||||
|
||||
let {errorMessage, sourceName, category, lineNumber, columnNumber} = pageError;
|
||||
output += category + '): "' + (errorMessage.initial || errorMessage) +
|
||||
'" in ' + sourceName + ':' + lineNumber + ':' + columnNumber;
|
||||
break;
|
||||
|
||||
case 'consoleAPICall':
|
||||
switch (packet.output.level) {
|
||||
switch (packet.message.level) {
|
||||
|
||||
case 'error':
|
||||
metric = 'errors';
|
||||
|
@ -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="0701e08a025787481a5d23443c75be7473c866f5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c03a6af9028c4b74a84b5a98085bbb0c07261175"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0701e08a025787481a5d23443c75be7473c866f5"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c03a6af9028c4b74a84b5a98085bbb0c07261175"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0701e08a025787481a5d23443c75be7473c866f5"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c03a6af9028c4b74a84b5a98085bbb0c07261175"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0701e08a025787481a5d23443c75be7473c866f5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c03a6af9028c4b74a84b5a98085bbb0c07261175"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"branch": "",
|
||||
"revision": ""
|
||||
},
|
||||
"revision": "fb54fbd4849b9adfdb4fc236c2cf8161545d736d",
|
||||
"revision": "f5280df8a66c88c8bafc0062e19a4770ce18e08d",
|
||||
"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="0701e08a025787481a5d23443c75be7473c866f5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c03a6af9028c4b74a84b5a98085bbb0c07261175"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0701e08a025787481a5d23443c75be7473c866f5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c03a6af9028c4b74a84b5a98085bbb0c07261175"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0701e08a025787481a5d23443c75be7473c866f5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c03a6af9028c4b74a84b5a98085bbb0c07261175"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0701e08a025787481a5d23443c75be7473c866f5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c03a6af9028c4b74a84b5a98085bbb0c07261175"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0701e08a025787481a5d23443c75be7473c866f5"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c03a6af9028c4b74a84b5a98085bbb0c07261175"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0701e08a025787481a5d23443c75be7473c866f5"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c03a6af9028c4b74a84b5a98085bbb0c07261175"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -5,7 +5,7 @@
|
||||
MOZ_APP_BASENAME=B2G
|
||||
MOZ_APP_VENDOR=Mozilla
|
||||
|
||||
MOZ_APP_VERSION=30.0a1
|
||||
MOZ_APP_VERSION=31.0a1
|
||||
MOZ_APP_UA_NAME=Firefox
|
||||
|
||||
MOZ_UA_OS_AGNOSTIC=1
|
||||
|
@ -1 +1 @@
|
||||
30.0a1
|
||||
31.0a1
|
||||
|
@ -7,8 +7,9 @@
|
||||
"use strict";
|
||||
|
||||
// Used to detect minification for automatic pretty printing
|
||||
const SAMPLE_SIZE = 30; // no of lines
|
||||
const INDENT_COUNT_THRESHOLD = 20; // percentage
|
||||
const SAMPLE_SIZE = 50; // no of lines
|
||||
const INDENT_COUNT_THRESHOLD = 5; // percentage
|
||||
const CHARACTER_LIMIT = 250; // line character limit
|
||||
|
||||
/**
|
||||
* Functions handling the sources UI.
|
||||
@ -1500,6 +1501,7 @@ let SourceUtils = {
|
||||
let lineStartIndex = 0;
|
||||
let lines = 0;
|
||||
let indentCount = 0;
|
||||
let overCharLimit = false;
|
||||
|
||||
// Strip comments.
|
||||
aText = aText.replace(/\/\*[\S\s]*?\*\/|\/\/(.+|\n)/g, "");
|
||||
@ -1512,9 +1514,15 @@ let SourceUtils = {
|
||||
if (/^\s+/.test(aText.slice(lineStartIndex, lineEndIndex))) {
|
||||
indentCount++;
|
||||
}
|
||||
// For files with no indents but are not minified.
|
||||
if ((lineEndIndex - lineStartIndex) > CHARACTER_LIMIT) {
|
||||
overCharLimit = true;
|
||||
break;
|
||||
}
|
||||
lineStartIndex = lineEndIndex + 1;
|
||||
}
|
||||
isMinified = ((indentCount / lines ) * 100) < INDENT_COUNT_THRESHOLD;
|
||||
isMinified = ((indentCount / lines ) * 100) < INDENT_COUNT_THRESHOLD ||
|
||||
overCharLimit;
|
||||
|
||||
this._minifiedCache.set(sourceClient, isMinified);
|
||||
return isMinified;
|
||||
|
@ -1,4 +1,4 @@
|
||||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 0.8.1114
|
||||
Current extension version is: 0.8.1181
|
||||
|
||||
|
@ -61,6 +61,27 @@ function getIntPref(aPref, aDefaultValue) {
|
||||
}
|
||||
}
|
||||
|
||||
function initializeDefaultPreferences() {
|
||||
Cu.import('resource://pdf.js/default_preferences.js');
|
||||
|
||||
var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.');
|
||||
var defaultValue;
|
||||
for (var key in DEFAULT_PREFERENCES) {
|
||||
defaultValue = DEFAULT_PREFERENCES[key];
|
||||
switch (typeof defaultValue) {
|
||||
case 'boolean':
|
||||
defaultBranch.setBoolPref(key, defaultValue);
|
||||
break;
|
||||
case 'number':
|
||||
defaultBranch.setIntPref(key, defaultValue);
|
||||
break;
|
||||
case 'string':
|
||||
defaultBranch.setCharPref(key, defaultValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register/unregister a constructor as a factory.
|
||||
function Factory() {}
|
||||
Factory.prototype = {
|
||||
@ -104,6 +125,8 @@ let PdfJs = {
|
||||
Services.obs.addObserver(this, TOPIC_PDFJS_HANDLER_CHANGED, false);
|
||||
Services.obs.addObserver(this, TOPIC_PLUGINS_LIST_UPDATED, false);
|
||||
Services.obs.addObserver(this, TOPIC_PLUGIN_INFO_UPDATED, false);
|
||||
|
||||
initializeDefaultPreferences();
|
||||
},
|
||||
|
||||
_migrate: function migrate() {
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
/* jshint esnext:true */
|
||||
/* globals Components, Services, XPCOMUtils, NetUtil, PrivateBrowsingUtils,
|
||||
dump, NetworkManager, PdfJsTelemetry, DEFAULT_PREFERENCES */
|
||||
dump, NetworkManager, PdfJsTelemetry */
|
||||
|
||||
'use strict';
|
||||
|
||||
@ -33,16 +33,14 @@ const PDF_CONTENT_TYPE = 'application/pdf';
|
||||
const PREF_PREFIX = 'pdfjs';
|
||||
const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html';
|
||||
const MAX_DATABASE_LENGTH = 4096;
|
||||
const MAX_STRING_PREF_LENGTH = 4096;
|
||||
const MAX_NUMBER_OF_PREFS = 50;
|
||||
const MAX_STRING_PREF_LENGTH = 128;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/NetUtil.jsm');
|
||||
Cu.import('resource://pdf.js/network.js');
|
||||
|
||||
// Load the default preferences.
|
||||
Cu.import('resource://pdf.js/default_preferences.js');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
|
||||
'resource://gre/modules/PrivateBrowsingUtils.jsm');
|
||||
|
||||
@ -453,56 +451,60 @@ ChromeActions.prototype = {
|
||||
.updateControlState(result, findPrevious);
|
||||
},
|
||||
setPreferences: function(prefs) {
|
||||
var prefValue, defaultValue, prefName, prefType, defaultType;
|
||||
|
||||
for (var key in DEFAULT_PREFERENCES) {
|
||||
var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.');
|
||||
var numberOfPrefs = 0;
|
||||
var prefValue, prefName;
|
||||
for (var key in prefs) {
|
||||
if (++numberOfPrefs > MAX_NUMBER_OF_PREFS) {
|
||||
log('setPreferences - Exceeded the maximum number of preferences ' +
|
||||
'that is allowed to be set at once.');
|
||||
break;
|
||||
} else if (!defaultBranch.getPrefType(key)) {
|
||||
continue;
|
||||
}
|
||||
prefValue = prefs[key];
|
||||
defaultValue = DEFAULT_PREFERENCES[key];
|
||||
prefName = (PREF_PREFIX + '.' + key);
|
||||
|
||||
if (prefValue === undefined || prefValue === defaultValue) {
|
||||
Services.prefs.clearUserPref(prefName);
|
||||
} else {
|
||||
prefType = typeof prefValue;
|
||||
defaultType = typeof defaultValue;
|
||||
|
||||
if (prefType !== defaultType) {
|
||||
continue;
|
||||
}
|
||||
switch (defaultType) {
|
||||
case 'boolean':
|
||||
setBoolPref(prefName, prefValue);
|
||||
break;
|
||||
case 'number':
|
||||
setIntPref(prefName, prefValue);
|
||||
break;
|
||||
case 'string':
|
||||
// Protect against adding arbitrarily long strings in about:config.
|
||||
if (prefValue.length <= MAX_STRING_PREF_LENGTH) {
|
||||
setStringPref(prefName, prefValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (typeof prefValue) {
|
||||
case 'boolean':
|
||||
setBoolPref(prefName, prefValue);
|
||||
break;
|
||||
case 'number':
|
||||
setIntPref(prefName, prefValue);
|
||||
break;
|
||||
case 'string':
|
||||
if (prefValue.length > MAX_STRING_PREF_LENGTH) {
|
||||
log('setPreferences - Exceeded the maximum allowed length ' +
|
||||
'for a string preference.');
|
||||
} else {
|
||||
setStringPref(prefName, prefValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
getPreferences: function() {
|
||||
var currentPrefs = {};
|
||||
var defaultValue, prefName;
|
||||
|
||||
for (var key in DEFAULT_PREFERENCES) {
|
||||
defaultValue = DEFAULT_PREFERENCES[key];
|
||||
getPreferences: function(prefs) {
|
||||
var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.');
|
||||
var currentPrefs = {}, numberOfPrefs = 0;
|
||||
var prefValue, prefName;
|
||||
for (var key in prefs) {
|
||||
if (++numberOfPrefs > MAX_NUMBER_OF_PREFS) {
|
||||
log('getPreferences - Exceeded the maximum number of preferences ' +
|
||||
'that is allowed to be fetched at once.');
|
||||
break;
|
||||
} else if (!defaultBranch.getPrefType(key)) {
|
||||
continue;
|
||||
}
|
||||
prefValue = prefs[key];
|
||||
prefName = (PREF_PREFIX + '.' + key);
|
||||
|
||||
switch (typeof defaultValue) {
|
||||
switch (typeof prefValue) {
|
||||
case 'boolean':
|
||||
currentPrefs[key] = getBoolPref(prefName, defaultValue);
|
||||
currentPrefs[key] = getBoolPref(prefName, prefValue);
|
||||
break;
|
||||
case 'number':
|
||||
currentPrefs[key] = getIntPref(prefName, defaultValue);
|
||||
currentPrefs[key] = getIntPref(prefName, prefValue);
|
||||
break;
|
||||
case 'string':
|
||||
currentPrefs[key] = getStringPref(prefName, defaultValue);
|
||||
currentPrefs[key] = getStringPref(prefName, prefValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
1824
browser/extensions/pdfjs/content/build/pdf.worker.js
vendored
1824
browser/extensions/pdfjs/content/build/pdf.worker.js
vendored
File diff suppressed because it is too large
Load Diff
@ -46,13 +46,17 @@ var FontInspector = (function FontInspectorClosure() {
|
||||
}
|
||||
}
|
||||
function textLayerClick(e) {
|
||||
if (!e.target.dataset.fontName || e.target.tagName.toUpperCase() !== 'DIV')
|
||||
if (!e.target.dataset.fontName ||
|
||||
e.target.tagName.toUpperCase() !== 'DIV') {
|
||||
return;
|
||||
}
|
||||
var fontName = e.target.dataset.fontName;
|
||||
var selects = document.getElementsByTagName('input');
|
||||
for (var i = 0; i < selects.length; ++i) {
|
||||
var select = selects[i];
|
||||
if (select.dataset.fontName != fontName) continue;
|
||||
if (select.dataset.fontName != fontName) {
|
||||
continue;
|
||||
}
|
||||
select.checked = !select.checked;
|
||||
selectFont(fontName, select.checked);
|
||||
select.scrollIntoView();
|
||||
@ -140,8 +144,9 @@ var FontInspector = (function FontInspectorClosure() {
|
||||
// Somewhat of a hack, should probably add a hook for when the text layer
|
||||
// is done rendering.
|
||||
setTimeout(function() {
|
||||
if (this.active)
|
||||
if (this.active) {
|
||||
resetSelection();
|
||||
}
|
||||
}.bind(this), 2000);
|
||||
}
|
||||
};
|
||||
@ -172,8 +177,9 @@ var StepperManager = (function StepperManagerClosure() {
|
||||
stepperDiv = document.createElement('div');
|
||||
this.panel.appendChild(stepperControls);
|
||||
this.panel.appendChild(stepperDiv);
|
||||
if (sessionStorage.getItem('pdfjsBreakPoints'))
|
||||
if (sessionStorage.getItem('pdfjsBreakPoints')) {
|
||||
breakPoints = JSON.parse(sessionStorage.getItem('pdfjsBreakPoints'));
|
||||
}
|
||||
},
|
||||
enabled: false,
|
||||
active: false,
|
||||
@ -191,19 +197,22 @@ var StepperManager = (function StepperManagerClosure() {
|
||||
var initBreakPoints = breakPoints[pageIndex] || [];
|
||||
var stepper = new Stepper(debug, pageIndex, initBreakPoints);
|
||||
steppers.push(stepper);
|
||||
if (steppers.length === 1)
|
||||
if (steppers.length === 1) {
|
||||
this.selectStepper(pageIndex, false);
|
||||
}
|
||||
return stepper;
|
||||
},
|
||||
selectStepper: function selectStepper(pageIndex, selectPanel) {
|
||||
if (selectPanel)
|
||||
if (selectPanel) {
|
||||
this.manager.selectPanel(1);
|
||||
}
|
||||
for (var i = 0; i < steppers.length; ++i) {
|
||||
var stepper = steppers[i];
|
||||
if (stepper.pageIndex == pageIndex)
|
||||
if (stepper.pageIndex == pageIndex) {
|
||||
stepper.panel.removeAttribute('hidden');
|
||||
else
|
||||
} else {
|
||||
stepper.panel.setAttribute('hidden', true);
|
||||
}
|
||||
}
|
||||
var options = stepperChooser.options;
|
||||
for (var i = 0; i < options.length; ++i) {
|
||||
@ -223,8 +232,9 @@ var Stepper = (function StepperClosure() {
|
||||
// Shorter way to create element and optionally set textContent.
|
||||
function c(tag, textContent) {
|
||||
var d = document.createElement(tag);
|
||||
if (textContent)
|
||||
if (textContent) {
|
||||
d.textContent = textContent;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
@ -297,10 +307,11 @@ var Stepper = (function StepperClosure() {
|
||||
cbox.checked = checked;
|
||||
cbox.onclick = (function(x) {
|
||||
return function() {
|
||||
if (this.checked)
|
||||
if (this.checked) {
|
||||
self.breakPoints.push(x);
|
||||
else
|
||||
} else {
|
||||
self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
|
||||
}
|
||||
StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
|
||||
};
|
||||
})(i);
|
||||
@ -336,8 +347,9 @@ var Stepper = (function StepperClosure() {
|
||||
getNextBreakPoint: function getNextBreakPoint() {
|
||||
this.breakPoints.sort(function(a, b) { return a - b; });
|
||||
for (var i = 0; i < this.breakPoints.length; i++) {
|
||||
if (this.breakPoints[i] > this.currentIdx)
|
||||
if (this.breakPoints[i] > this.currentIdx) {
|
||||
return this.breakPoints[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@ -385,13 +397,16 @@ var Stepper = (function StepperClosure() {
|
||||
var Stats = (function Stats() {
|
||||
var stats = [];
|
||||
function clear(node) {
|
||||
while (node.hasChildNodes())
|
||||
while (node.hasChildNodes()) {
|
||||
node.removeChild(node.lastChild);
|
||||
}
|
||||
}
|
||||
function getStatIndex(pageNumber) {
|
||||
for (var i = 0, ii = stats.length; i < ii; ++i)
|
||||
if (stats[i].pageNumber === pageNumber)
|
||||
for (var i = 0, ii = stats.length; i < ii; ++i) {
|
||||
if (stats[i].pageNumber === pageNumber) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
@ -408,8 +423,9 @@ var Stats = (function Stats() {
|
||||
active: false,
|
||||
// Stats specific functions.
|
||||
add: function(pageNumber, stat) {
|
||||
if (!stat)
|
||||
if (!stat) {
|
||||
return;
|
||||
}
|
||||
var statsIndex = getStatIndex(pageNumber);
|
||||
if (statsIndex !== false) {
|
||||
var b = stats[statsIndex];
|
||||
@ -428,8 +444,9 @@ var Stats = (function Stats() {
|
||||
stats.push({ pageNumber: pageNumber, div: wrapper });
|
||||
stats.sort(function(a, b) { return a.pageNumber - b.pageNumber; });
|
||||
clear(this.panel);
|
||||
for (var i = 0, ii = stats.length; i < ii; ++i)
|
||||
for (var i = 0, ii = stats.length; i < ii; ++i) {
|
||||
this.panel.appendChild(stats[i].div);
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
@ -448,12 +465,14 @@ var PDFBug = (function PDFBugClosure() {
|
||||
],
|
||||
enable: function(ids) {
|
||||
var all = false, tools = this.tools;
|
||||
if (ids.length === 1 && ids[0] === 'all')
|
||||
if (ids.length === 1 && ids[0] === 'all') {
|
||||
all = true;
|
||||
}
|
||||
for (var i = 0; i < tools.length; ++i) {
|
||||
var tool = tools[i];
|
||||
if (all || ids.indexOf(tool.id) !== -1)
|
||||
if (all || ids.indexOf(tool.id) !== -1) {
|
||||
tool.enabled = true;
|
||||
}
|
||||
}
|
||||
if (!all) {
|
||||
// Sort the tools by the order they are enabled.
|
||||
@ -509,19 +528,21 @@ var PDFBug = (function PDFBugClosure() {
|
||||
panels.appendChild(panel);
|
||||
tool.panel = panel;
|
||||
tool.manager = this;
|
||||
if (tool.enabled)
|
||||
if (tool.enabled) {
|
||||
tool.init();
|
||||
else
|
||||
} else {
|
||||
panel.textContent = tool.name + ' is disabled. To enable add ' +
|
||||
' "' + tool.id + '" to the pdfBug parameter ' +
|
||||
'and refresh (seperate multiple by commas).';
|
||||
}
|
||||
buttons.push(panelButton);
|
||||
}
|
||||
this.selectPanel(0);
|
||||
},
|
||||
selectPanel: function selectPanel(index) {
|
||||
if (index === activePanel)
|
||||
if (index === activePanel) {
|
||||
return;
|
||||
}
|
||||
activePanel = index;
|
||||
var tools = this.tools;
|
||||
for (var j = 0; j < tools.length; ++j) {
|
||||
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
</svg>
|
After Width: | Height: | Size: 158 B |
@ -1157,14 +1157,7 @@ canvas {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.page > a,
|
||||
.annotationLayer > a {
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.page > a:hover,
|
||||
.annotationLayer > a:hover {
|
||||
.annotLink > a:hover {
|
||||
opacity: 0.2;
|
||||
background: #ff0;
|
||||
box-shadow: 0px 2px 10px #ff0;
|
||||
@ -1229,29 +1222,49 @@ canvas {
|
||||
::selection { background:rgba(0,0,255,0.3); }
|
||||
::-moz-selection { background:rgba(0,0,255,0.3); }
|
||||
|
||||
.annotText > div {
|
||||
z-index: 200;
|
||||
.annotationHighlight {
|
||||
position: absolute;
|
||||
padding: 0.6em;
|
||||
max-width: 20em;
|
||||
background-color: #FFFF99;
|
||||
box-shadow: 0px 2px 10px #333;
|
||||
border-radius: 7px;
|
||||
border: 2px #FFFF99 solid;
|
||||
}
|
||||
|
||||
.annotText > img {
|
||||
position: absolute;
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.annotText > img:hover {
|
||||
opacity: 1;
|
||||
.annotTextContentWrapper {
|
||||
position: absolute;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
.annotText > div > h1 {
|
||||
font-size: 1.2em;
|
||||
.annotTextContent {
|
||||
z-index: 200;
|
||||
float: left;
|
||||
max-width: 20em;
|
||||
background-color: #FFFF99;
|
||||
box-shadow: 0px 2px 5px #333;
|
||||
border-radius: 2px;
|
||||
padding: 0.6em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.annotTextContent > h1 {
|
||||
font-size: 1em;
|
||||
border-bottom: 1px solid #000000;
|
||||
margin: 0px;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.annotTextContent > p {
|
||||
padding-top: 0.2em;
|
||||
}
|
||||
|
||||
.annotLink > a {
|
||||
position: absolute;
|
||||
font-size: 1em;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#errorWrapper {
|
||||
|
@ -68,10 +68,9 @@ var CustomStyle = (function CustomStyleClosure() {
|
||||
// in some versions of IE9 it is critical that ms appear in this list
|
||||
// before Moz
|
||||
var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
|
||||
var _cache = { };
|
||||
var _cache = {};
|
||||
|
||||
function CustomStyle() {
|
||||
}
|
||||
function CustomStyle() {}
|
||||
|
||||
CustomStyle.getProp = function get(propName, element) {
|
||||
// check cache only when no element is given
|
||||
@ -104,8 +103,9 @@ var CustomStyle = (function CustomStyleClosure() {
|
||||
|
||||
CustomStyle.setProp = function set(propName, element, str) {
|
||||
var prop = this.getProp(propName);
|
||||
if (prop != 'undefined')
|
||||
if (prop != 'undefined') {
|
||||
element.style[prop] = str;
|
||||
}
|
||||
};
|
||||
|
||||
return CustomStyle;
|
||||
@ -291,11 +291,13 @@ var Cache = function cacheCache(size) {
|
||||
var data = [];
|
||||
this.push = function cachePush(view) {
|
||||
var i = data.indexOf(view);
|
||||
if (i >= 0)
|
||||
if (i >= 0) {
|
||||
data.splice(i);
|
||||
}
|
||||
data.push(view);
|
||||
if (data.length > size)
|
||||
if (data.length > size) {
|
||||
data.shift().destroy();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -315,12 +317,13 @@ var Preferences = (function PreferencesClosure() {
|
||||
function Preferences() {
|
||||
this.prefs = {};
|
||||
this.isInitializedPromiseResolved = false;
|
||||
this.initializedPromise = this.readFromStorage().then(function(prefObj) {
|
||||
this.isInitializedPromiseResolved = true;
|
||||
if (prefObj) {
|
||||
this.prefs = prefObj;
|
||||
}
|
||||
}.bind(this));
|
||||
this.initializedPromise = this.readFromStorage(DEFAULT_PREFERENCES).then(
|
||||
function(prefObj) {
|
||||
this.isInitializedPromiseResolved = true;
|
||||
if (prefObj) {
|
||||
this.prefs = prefObj;
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
Preferences.prototype = {
|
||||
@ -328,7 +331,7 @@ var Preferences = (function PreferencesClosure() {
|
||||
return;
|
||||
},
|
||||
|
||||
readFromStorage: function Preferences_readFromStorage() {
|
||||
readFromStorage: function Preferences_readFromStorage(prefObj) {
|
||||
var readFromStoragePromise = Promise.resolve();
|
||||
return readFromStoragePromise;
|
||||
},
|
||||
@ -336,7 +339,7 @@ var Preferences = (function PreferencesClosure() {
|
||||
reset: function Preferences_reset() {
|
||||
if (this.isInitializedPromiseResolved) {
|
||||
this.prefs = {};
|
||||
this.writeToStorage(this.prefs);
|
||||
this.writeToStorage(DEFAULT_PREFERENCES);
|
||||
}
|
||||
},
|
||||
|
||||
@ -361,6 +364,12 @@ var Preferences = (function PreferencesClosure() {
|
||||
valueType + '\", expected a \"' + defaultType + '\".');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (valueType === 'number' && (value | 0) !== value) {
|
||||
console.error('Preferences_set: \'' + value +
|
||||
'\' must be an \"integer\".');
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.prefs[name] = value;
|
||||
this.writeToStorage(this.prefs);
|
||||
@ -427,8 +436,8 @@ var FirefoxCom = (function FirefoxComClosure() {
|
||||
var request = document.createTextNode('');
|
||||
if (callback) {
|
||||
document.addEventListener('pdf.js.response', function listener(event) {
|
||||
var node = event.target,
|
||||
response = event.detail.response;
|
||||
var node = event.target;
|
||||
var response = event.detail.response;
|
||||
|
||||
document.documentElement.removeChild(node);
|
||||
|
||||
@ -483,9 +492,10 @@ Preferences.prototype.writeToStorage = function(prefObj) {
|
||||
FirefoxCom.requestSync('setPreferences', prefObj);
|
||||
};
|
||||
|
||||
Preferences.prototype.readFromStorage = function() {
|
||||
Preferences.prototype.readFromStorage = function(prefObj) {
|
||||
var readFromStoragePromise = new Promise(function (resolve) {
|
||||
var readPrefs = JSON.parse(FirefoxCom.requestSync('getPreferences'));
|
||||
var readPrefs = JSON.parse(FirefoxCom.requestSync('getPreferences',
|
||||
prefObj));
|
||||
resolve(readPrefs);
|
||||
});
|
||||
return readFromStoragePromise;
|
||||
@ -576,8 +586,6 @@ var ViewHistory = (function ViewHistoryClosure() {
|
||||
})();
|
||||
|
||||
|
||||
/* globals PDFFindController, FindStates, mozL10n */
|
||||
|
||||
/**
|
||||
* Creates a "search bar" given set of DOM elements
|
||||
* that act as controls for searching, or for setting
|
||||
@ -586,7 +594,6 @@ var ViewHistory = (function ViewHistoryClosure() {
|
||||
* searching is done by PDFFindController
|
||||
*/
|
||||
var PDFFindBar = {
|
||||
|
||||
opened: false,
|
||||
bar: null,
|
||||
toggleButton: null,
|
||||
@ -600,7 +607,7 @@ var PDFFindBar = {
|
||||
|
||||
initialize: function(options) {
|
||||
if(typeof PDFFindController === 'undefined' || PDFFindController === null) {
|
||||
throw 'PDFFindBar cannot be initialized ' +
|
||||
throw 'PDFFindBar cannot be initialized ' +
|
||||
'without a PDFFindController instance.';
|
||||
}
|
||||
|
||||
@ -715,8 +722,9 @@ var PDFFindBar = {
|
||||
},
|
||||
|
||||
close: function() {
|
||||
if (!this.opened) return;
|
||||
|
||||
if (!this.opened) {
|
||||
return;
|
||||
}
|
||||
this.opened = false;
|
||||
this.toggleButton.classList.remove('toggled');
|
||||
this.bar.classList.add('hidden');
|
||||
@ -735,8 +743,6 @@ var PDFFindBar = {
|
||||
|
||||
|
||||
|
||||
/* globals PDFFindBar, PDFJS, FindStates, FirefoxCom, Promise */
|
||||
|
||||
/**
|
||||
* Provides a "search" or "find" functionality for the PDF.
|
||||
* This object actually performs the search for a given string.
|
||||
@ -876,8 +882,9 @@ var PDFFindController = {
|
||||
self.pageContents.push(str);
|
||||
|
||||
extractTextPromisesResolves[pageIndex](pageIndex);
|
||||
if ((pageIndex + 1) < self.pdfPageSource.pages.length)
|
||||
if ((pageIndex + 1) < self.pdfPageSource.pages.length) {
|
||||
extractPageText(pageIndex + 1);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -2432,10 +2439,11 @@ var PDFView = {
|
||||
viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) {
|
||||
var currentY = viewAreaElement.scrollTop;
|
||||
var lastY = state.lastY;
|
||||
if (currentY > lastY)
|
||||
if (currentY > lastY) {
|
||||
state.down = true;
|
||||
else if (currentY < lastY)
|
||||
} else if (currentY < lastY) {
|
||||
state.down = false;
|
||||
}
|
||||
// else do nothing and use previous value
|
||||
state.lastY = currentY;
|
||||
callback();
|
||||
@ -2688,8 +2696,9 @@ var PDFView = {
|
||||
}
|
||||
var args = e.data;
|
||||
|
||||
if (typeof args !== 'object' || !('pdfjsLoadAction' in args))
|
||||
if (typeof args !== 'object' || !('pdfjsLoadAction' in args)) {
|
||||
return;
|
||||
}
|
||||
switch (args.pdfjsLoadAction) {
|
||||
case 'supportsRangedLoading':
|
||||
PDFView.open(args.pdfUrl, 0, undefined, pdfDataRangeTransport, {
|
||||
@ -2884,8 +2893,9 @@ var PDFView = {
|
||||
},
|
||||
|
||||
getDestinationHash: function pdfViewGetDestinationHash(dest) {
|
||||
if (typeof dest === 'string')
|
||||
if (typeof dest === 'string') {
|
||||
return PDFView.getAnchorUrl('#' + escape(dest));
|
||||
}
|
||||
if (dest instanceof Array) {
|
||||
var destRef = dest[0]; // see navigateTo method for dest format
|
||||
var pageNumber = destRef instanceof Object ?
|
||||
@ -3003,15 +3013,18 @@ var PDFView = {
|
||||
var thumbsView = document.getElementById('thumbnailView');
|
||||
thumbsView.parentNode.scrollTop = 0;
|
||||
|
||||
while (thumbsView.hasChildNodes())
|
||||
while (thumbsView.hasChildNodes()) {
|
||||
thumbsView.removeChild(thumbsView.lastChild);
|
||||
}
|
||||
|
||||
if ('_loadingInterval' in thumbsView)
|
||||
if ('_loadingInterval' in thumbsView) {
|
||||
clearInterval(thumbsView._loadingInterval);
|
||||
}
|
||||
|
||||
var container = document.getElementById('viewer');
|
||||
while (container.hasChildNodes())
|
||||
while (container.hasChildNodes()) {
|
||||
container.removeChild(container.lastChild);
|
||||
}
|
||||
|
||||
var pagesCount = pdfDocument.numPages;
|
||||
|
||||
@ -3178,16 +3191,17 @@ var PDFView = {
|
||||
(PDFJS.version ? ' (PDF.js: ' + PDFJS.version + ')' : ''));
|
||||
|
||||
var pdfTitle;
|
||||
if (metadata) {
|
||||
if (metadata.has('dc:title'))
|
||||
pdfTitle = metadata.get('dc:title');
|
||||
if (metadata && metadata.has('dc:title')) {
|
||||
pdfTitle = metadata.get('dc:title');
|
||||
}
|
||||
|
||||
if (!pdfTitle && info && info['Title'])
|
||||
if (!pdfTitle && info && info['Title']) {
|
||||
pdfTitle = info['Title'];
|
||||
}
|
||||
|
||||
if (pdfTitle)
|
||||
if (pdfTitle) {
|
||||
self.setTitle(pdfTitle + ' - ' + document.title);
|
||||
}
|
||||
|
||||
if (info.IsAcroFormPresent) {
|
||||
console.warn('Warning: AcroForm/XFA is not supported');
|
||||
@ -3313,21 +3327,24 @@ var PDFView = {
|
||||
}
|
||||
for (var i = 0; i < numVisible; ++i) {
|
||||
var view = visibleViews[i].view;
|
||||
if (!this.isViewFinished(view))
|
||||
if (!this.isViewFinished(view)) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
// All the visible views have rendered, try to render next/previous pages.
|
||||
if (scrolledDown) {
|
||||
var nextPageIndex = visible.last.id;
|
||||
// ID's start at 1 so no need to add 1.
|
||||
if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex]))
|
||||
if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) {
|
||||
return views[nextPageIndex];
|
||||
}
|
||||
} else {
|
||||
var previousPageIndex = visible.first.id - 2;
|
||||
if (views[previousPageIndex] &&
|
||||
!this.isViewFinished(views[previousPageIndex]))
|
||||
!this.isViewFinished(views[previousPageIndex])) {
|
||||
return views[previousPageIndex];
|
||||
}
|
||||
}
|
||||
// Everything that needs to be rendered has been.
|
||||
return false;
|
||||
@ -3360,8 +3377,9 @@ var PDFView = {
|
||||
},
|
||||
|
||||
setHash: function pdfViewSetHash(hash) {
|
||||
if (!hash)
|
||||
if (!hash) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hash.indexOf('=') >= 0) {
|
||||
var params = PDFView.parseQueryString(hash);
|
||||
@ -3449,8 +3467,9 @@ var PDFView = {
|
||||
thumbsView.classList.add('hidden');
|
||||
outlineView.classList.remove('hidden');
|
||||
|
||||
if (outlineButton.getAttribute('disabled'))
|
||||
if (outlineButton.getAttribute('disabled')) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -3567,8 +3586,9 @@ var PDFView = {
|
||||
|
||||
afterPrint: function pdfViewSetupAfterPrint() {
|
||||
var div = document.getElementById('printContainer');
|
||||
while (div.hasChildNodes())
|
||||
while (div.hasChildNodes()) {
|
||||
div.removeChild(div.lastChild);
|
||||
}
|
||||
},
|
||||
|
||||
rotatePages: function pdfViewRotatePages(delta) {
|
||||
@ -3610,14 +3630,16 @@ var PDFView = {
|
||||
// In case one page has already been flipped there is a cooldown time
|
||||
// which has to expire before next page can be scrolled on to.
|
||||
if (currentTime > storedTime &&
|
||||
currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME)
|
||||
currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
|
||||
return;
|
||||
}
|
||||
|
||||
// In case the user decides to scroll to the opposite direction than before
|
||||
// clear the accumulated delta.
|
||||
if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) ||
|
||||
(this.mouseScrollDelta < 0 && mouseScrollDelta > 0))
|
||||
(this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) {
|
||||
this.clearMouseScrollState();
|
||||
}
|
||||
|
||||
this.mouseScrollDelta += mouseScrollDelta;
|
||||
|
||||
@ -3640,8 +3662,9 @@ var PDFView = {
|
||||
// to do anything.
|
||||
if ((currentPage == 1 && pageFlipDirection == PageFlipDirection.UP) ||
|
||||
(currentPage == this.pages.length &&
|
||||
pageFlipDirection == PageFlipDirection.DOWN))
|
||||
pageFlipDirection == PageFlipDirection.DOWN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.page += pageFlipDirection;
|
||||
this.mouseScrollTimeStamp = currentTime;
|
||||
@ -3946,10 +3969,13 @@ var PageView = function pageView(container, id, scale,
|
||||
CustomStyle.setProp('transformOrigin', element, transformOriginStr);
|
||||
|
||||
if (data.subtype === 'Link' && !data.url) {
|
||||
if (data.action) {
|
||||
bindNamedAction(element, data.action);
|
||||
} else {
|
||||
bindLink(element, ('dest' in data) ? data.dest : null);
|
||||
var link = element.getElementsByTagName('a')[0];
|
||||
if (link) {
|
||||
if (data.action) {
|
||||
bindNamedAction(link, data.action);
|
||||
} else {
|
||||
bindLink(link, ('dest' in data) ? data.dest : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4215,6 +4241,7 @@ var PageView = function pageView(container, id, scale,
|
||||
canvasContext: ctx,
|
||||
viewport: this.viewport,
|
||||
textLayer: textLayer,
|
||||
// intent: 'default', // === 'display'
|
||||
continueCallback: function pdfViewcContinueCallback(cont) {
|
||||
if (PDFView.highestPriorityPage !== 'page' + self.id) {
|
||||
self.renderingState = RenderingStates.PAUSED;
|
||||
@ -4286,13 +4313,13 @@ var PageView = function pageView(container, id, scale,
|
||||
|
||||
var renderContext = {
|
||||
canvasContext: ctx,
|
||||
viewport: viewport
|
||||
viewport: viewport,
|
||||
intent: 'print'
|
||||
};
|
||||
|
||||
pdfPage.render(renderContext).promise.then(function() {
|
||||
// Tell the printEngine that rendering this canvas/page has finished.
|
||||
obj.done();
|
||||
self.pdfPage.destroy();
|
||||
}, function(error) {
|
||||
console.error(error);
|
||||
// Tell the printEngine that rendering this canvas/page has failed.
|
||||
@ -4302,7 +4329,6 @@ var PageView = function pageView(container, id, scale,
|
||||
} else {
|
||||
obj.done();
|
||||
}
|
||||
self.pdfPage.destroy();
|
||||
});
|
||||
};
|
||||
};
|
||||
@ -4522,12 +4548,12 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
||||
this.viewport = options.viewport;
|
||||
this.isViewerInPresentationMode = options.isViewerInPresentationMode;
|
||||
|
||||
if(typeof PDFFindController === 'undefined') {
|
||||
window.PDFFindController = null;
|
||||
if (typeof PDFFindController === 'undefined') {
|
||||
window.PDFFindController = null;
|
||||
}
|
||||
|
||||
if(typeof this.lastScrollSource === 'undefined') {
|
||||
this.lastScrollSource = null;
|
||||
if (typeof this.lastScrollSource === 'undefined') {
|
||||
this.lastScrollSource = null;
|
||||
}
|
||||
|
||||
this.beginLayout = function textLayerBuilderBeginLayout() {
|
||||
@ -4548,8 +4574,9 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
||||
// No point in rendering so many divs as it'd make the browser unusable
|
||||
// even after the divs are rendered
|
||||
var MAX_TEXT_DIVS_TO_RENDER = 100000;
|
||||
if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER)
|
||||
if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0, ii = textDivs.length; i < ii; i++) {
|
||||
var textDiv = textDivs[i];
|
||||
@ -4581,16 +4608,17 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
||||
// run it right away
|
||||
var RENDER_DELAY = 200; // in ms
|
||||
var self = this;
|
||||
var lastScroll = this.lastScrollSource === null ?
|
||||
0 : this.lastScrollSource.lastScroll;
|
||||
var lastScroll = (this.lastScrollSource === null ?
|
||||
0 : this.lastScrollSource.lastScroll);
|
||||
|
||||
if (Date.now() - lastScroll > RENDER_DELAY) {
|
||||
// Render right away
|
||||
this.renderLayer();
|
||||
} else {
|
||||
// Schedule
|
||||
if (this.renderTimer)
|
||||
if (this.renderTimer) {
|
||||
clearTimeout(this.renderTimer);
|
||||
}
|
||||
this.renderTimer = setTimeout(function() {
|
||||
self.setupRenderLayoutTimer();
|
||||
}, RENDER_DELAY);
|
||||
@ -4608,8 +4636,8 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
||||
|
||||
textDiv.style.fontSize = fontHeight + 'px';
|
||||
textDiv.style.fontFamily = geom.fontFamily;
|
||||
var fontAscent = geom.ascent ? geom.ascent * fontHeight :
|
||||
geom.descent ? (1 + geom.descent) * fontHeight : fontHeight;
|
||||
var fontAscent = (geom.ascent ? geom.ascent * fontHeight :
|
||||
(geom.descent ? (1 + geom.descent) * fontHeight : fontHeight));
|
||||
textDiv.style.left = (geom.x + (fontAscent * Math.sin(geom.angle))) + 'px';
|
||||
textDiv.style.top = (geom.y - (fontAscent * Math.cos(geom.angle))) + 'px';
|
||||
|
||||
@ -4621,8 +4649,9 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
||||
this.insertDivContent = function textLayerUpdateTextContent() {
|
||||
// Only set the content of the divs once layout has finished, the content
|
||||
// for the divs is available and content is not yet set on the divs.
|
||||
if (!this.layoutDone || this.divContentDone || !this.textContent)
|
||||
if (!this.layoutDone || this.divContentDone || !this.textContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.divContentDone = true;
|
||||
|
||||
@ -4661,8 +4690,8 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
||||
var iIndex = 0;
|
||||
var bidiTexts = this.textContent;
|
||||
var end = bidiTexts.length - 1;
|
||||
var queryLen = PDFFindController === null ?
|
||||
0 : PDFFindController.state.query.length;
|
||||
var queryLen = (PDFFindController === null ?
|
||||
0 : PDFFindController.state.query.length);
|
||||
|
||||
var lastDivIdx = -1;
|
||||
var pos;
|
||||
@ -4721,14 +4750,14 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
||||
var bidiTexts = this.textContent;
|
||||
var textDivs = this.textDivs;
|
||||
var prevEnd = null;
|
||||
var isSelectedPage = PDFFindController === null ?
|
||||
false : (this.pageIdx === PDFFindController.selected.pageIdx);
|
||||
var isSelectedPage = (PDFFindController === null ?
|
||||
false : (this.pageIdx === PDFFindController.selected.pageIdx));
|
||||
|
||||
var selectedMatchIdx = PDFFindController === null ?
|
||||
-1 : PDFFindController.selected.matchIdx;
|
||||
var selectedMatchIdx = (PDFFindController === null ?
|
||||
-1 : PDFFindController.selected.matchIdx);
|
||||
|
||||
var highlightAll = PDFFindController === null ?
|
||||
false : PDFFindController.state.highlightAll;
|
||||
var highlightAll = (PDFFindController === null ?
|
||||
false : PDFFindController.state.highlightAll);
|
||||
|
||||
var infty = {
|
||||
divIdx: -1,
|
||||
@ -4827,8 +4856,9 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
||||
|
||||
this.updateMatches = function textLayerUpdateMatches() {
|
||||
// Only show matches, once all rendering is done.
|
||||
if (!this.renderingDone)
|
||||
if (!this.renderingDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear out all matches.
|
||||
var matches = this.matches;
|
||||
@ -4848,14 +4878,14 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
||||
clearedUntilDivIdx = match.end.divIdx + 1;
|
||||
}
|
||||
|
||||
if (PDFFindController === null || !PDFFindController.active)
|
||||
if (PDFFindController === null || !PDFFindController.active) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert the matches on the page controller into the match format used
|
||||
// for the textLayer.
|
||||
this.matches = matches =
|
||||
this.convertMatches(PDFFindController === null ?
|
||||
[] : (PDFFindController.pageMatches[this.pageIdx] || []));
|
||||
this.matches = matches = (this.convertMatches(PDFFindController === null ?
|
||||
[] : (PDFFindController.pageMatches[this.pageIdx] || [])));
|
||||
|
||||
this.renderMatches(this.matches);
|
||||
};
|
||||
@ -4866,13 +4896,14 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
||||
var DocumentOutlineView = function documentOutlineView(outline) {
|
||||
var outlineView = document.getElementById('outlineView');
|
||||
var outlineButton = document.getElementById('viewOutline');
|
||||
while (outlineView.firstChild)
|
||||
while (outlineView.firstChild) {
|
||||
outlineView.removeChild(outlineView.firstChild);
|
||||
}
|
||||
|
||||
if (!outline) {
|
||||
if (!outlineView.classList.contains('hidden'))
|
||||
if (!outlineView.classList.contains('hidden')) {
|
||||
PDFView.switchSidebarView('thumbs');
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5101,8 +5132,9 @@ document.addEventListener('DOMContentLoaded', webViewerLoad, true);
|
||||
|
||||
function updateViewarea() {
|
||||
|
||||
if (!PDFView.initialized)
|
||||
if (!PDFView.initialized) {
|
||||
return;
|
||||
}
|
||||
var visible = PDFView.getVisiblePages();
|
||||
var visiblePages = visible.views;
|
||||
if (visiblePages.length === 0) {
|
||||
@ -5118,9 +5150,9 @@ function updateViewarea() {
|
||||
i < ii; ++i) {
|
||||
var page = visiblePages[i];
|
||||
|
||||
if (page.percent < 100)
|
||||
if (page.percent < 100) {
|
||||
break;
|
||||
|
||||
}
|
||||
if (page.id === PDFView.page) {
|
||||
stillFullyVisible = true;
|
||||
break;
|
||||
@ -5473,23 +5505,6 @@ window.addEventListener('keydown', function keydown(evt) {
|
||||
PDFView.rotatePages(90);
|
||||
break;
|
||||
}
|
||||
if (!handled && !PresentationMode.active) {
|
||||
// 33=Page Up 34=Page Down 35=End 36=Home
|
||||
// 37=Left 38=Up 39=Right 40=Down
|
||||
if (evt.keyCode >= 33 && evt.keyCode <= 40 &&
|
||||
!PDFView.container.contains(curElement)) {
|
||||
// The page container is not focused, but a page navigation key has been
|
||||
// pressed. Change the focus to the viewer container to make sure that
|
||||
// navigation by keyboard works as expected.
|
||||
PDFView.container.focus();
|
||||
}
|
||||
// 32=Spacebar
|
||||
if (evt.keyCode === 32 && curElementTagName !== 'BUTTON') {
|
||||
// Workaround for issue in Firefox, that prevents scroll keys from working
|
||||
// when elements with 'tabindex' are focused. (#3499)
|
||||
PDFView.container.blur();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd === 4) { // shift-key
|
||||
@ -5509,6 +5524,24 @@ window.addEventListener('keydown', function keydown(evt) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled && !PresentationMode.active) {
|
||||
// 33=Page Up 34=Page Down 35=End 36=Home
|
||||
// 37=Left 38=Up 39=Right 40=Down
|
||||
if (evt.keyCode >= 33 && evt.keyCode <= 40 &&
|
||||
!PDFView.container.contains(curElement)) {
|
||||
// The page container is not focused, but a page navigation key has been
|
||||
// pressed. Change the focus to the viewer container to make sure that
|
||||
// navigation by keyboard works as expected.
|
||||
PDFView.container.focus();
|
||||
}
|
||||
// 32=Spacebar
|
||||
if (evt.keyCode === 32 && curElementTagName !== 'BUTTON') {
|
||||
// Workaround for issue in Firefox, that prevents scroll keys from
|
||||
// working when elements with 'tabindex' are focused. (#3498)
|
||||
PDFView.container.blur();
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd === 2) { // alt-key
|
||||
switch (evt.keyCode) {
|
||||
case 37: // left arrow
|
||||
|
@ -10,4 +10,4 @@
|
||||
# hardcoded milestones in the tree from these two files.
|
||||
#--------------------------------------------------------
|
||||
|
||||
30.0a1
|
||||
31.0a1
|
||||
|
@ -1221,6 +1221,16 @@ MediaStreamGraphImpl::RunThread()
|
||||
}
|
||||
}
|
||||
|
||||
// The loop is woken up so soon that mCurrentTime barely advances and we
|
||||
// end up having endBlockingDecisions == mStateComputedTime.
|
||||
// Since stream blocking is computed in the interval of
|
||||
// [mStateComputedTime, endBlockingDecisions), it won't be computed at all.
|
||||
// We should ensure next iteration so that pending blocking changes will be
|
||||
// computed in next loop.
|
||||
if (endBlockingDecisions == mStateComputedTime) {
|
||||
ensureNextIteration = true;
|
||||
}
|
||||
|
||||
// Figure out which streams are blocked and when.
|
||||
GraphTime prevComputedTime = mStateComputedTime;
|
||||
RecomputeBlocking(endBlockingDecisions);
|
||||
|
@ -380,7 +380,6 @@ skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(bug 90
|
||||
skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(timed out in small-shot.mp3) b2g-desktop(timed out in small-shot.mp3)
|
||||
[test_readyState.html]
|
||||
[test_referer.html]
|
||||
skip-if = buildapp == 'b2g'
|
||||
[test_reset_events_async.html]
|
||||
[test_replay_metadata.html]
|
||||
skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
|
||||
|
@ -74,7 +74,7 @@ var tests = [
|
||||
// is(v.networkState, v.NETWORK_IDLE, "(1) NetworkState must be NETWORK_IDLE");
|
||||
maybeFinish(v, 1);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadStart = false;
|
||||
@ -86,6 +86,8 @@ var tests = [
|
||||
v.src = test.name;
|
||||
document.body.appendChild(v); // Causes implicit load, which will be halted due to preload:none.
|
||||
},
|
||||
|
||||
name: "test1",
|
||||
},
|
||||
{
|
||||
// 2. Add preload:metadata video with src to document. Should halt with NETWORK_IDLE, HAVE_CURRENT_DATA
|
||||
@ -100,7 +102,7 @@ var tests = [
|
||||
// is(v.networkState, v.NETWORK_IDLE, "(2) NetworkState must be NETWORK_IDLE");
|
||||
maybeFinish(v, 2);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadStart = false;
|
||||
@ -113,6 +115,8 @@ var tests = [
|
||||
document.body.appendChild(v); // Causes implicit load, which will be halted after
|
||||
// metadata due to preload:metadata.
|
||||
},
|
||||
|
||||
name: "test2",
|
||||
},
|
||||
{
|
||||
// 3. Add preload:auto to document. Should receive canplaythrough eventually.
|
||||
@ -123,7 +127,7 @@ var tests = [
|
||||
is(v._gotLoadedMetaData, true, "(3) Must get loadedmetadata.");
|
||||
maybeFinish(v, 3);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadStart = false;
|
||||
@ -135,6 +139,8 @@ var tests = [
|
||||
v.src = test.name; // Causes implicit load.
|
||||
document.body.appendChild(v);
|
||||
},
|
||||
|
||||
name: "test3",
|
||||
},
|
||||
{
|
||||
// 4. Add preload:none video to document. Call play(), should load then play through.
|
||||
@ -152,13 +158,13 @@ var tests = [
|
||||
// is(v.networkState, v.NETWORK_IDLE, "(4) NetworkState must be NETWORK_IDLE");
|
||||
v.play(); // Should load and play through.
|
||||
},
|
||||
|
||||
|
||||
ended:
|
||||
function(e) {
|
||||
ok(true, "(4) Got playback ended");
|
||||
maybeFinish(e.target, 4);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadStart = false;
|
||||
@ -172,6 +178,8 @@ var tests = [
|
||||
v.src = test.name;
|
||||
document.body.appendChild(v);
|
||||
},
|
||||
|
||||
name: "test4",
|
||||
},
|
||||
{
|
||||
// 5. preload:none video without resource, add to document, will implicitly start a
|
||||
@ -186,7 +194,7 @@ var tests = [
|
||||
// is(v.networkState, v.NETWORK_IDLE, "(5) NetworkState must be NETWORK_IDLE");
|
||||
maybeFinish(v, 5);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadStart = false;
|
||||
@ -198,6 +206,8 @@ var tests = [
|
||||
document.body.appendChild(v); // Causes implicit load, which will be halted due to no resource.
|
||||
v.src = test.name; // Load should start, and halt at preload:none.
|
||||
},
|
||||
|
||||
name: "test5",
|
||||
},
|
||||
{
|
||||
// 6. preload:none video without resource, add to document, will implicitly start a
|
||||
@ -212,7 +222,7 @@ var tests = [
|
||||
// is(v.networkState, v.NETWORK_IDLE, "(6) NetworkState must be NETWORK_IDLE");
|
||||
maybeFinish(v, 6);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadStart = false;
|
||||
@ -227,6 +237,8 @@ var tests = [
|
||||
s.type = test.type;
|
||||
v.appendChild(s); // Load should start, and halt at preload:none.
|
||||
},
|
||||
|
||||
name: "test6",
|
||||
},
|
||||
{
|
||||
// 7. create a preload:none document with multiple sources, the first of which is invalid.
|
||||
@ -252,7 +264,7 @@ var tests = [
|
||||
is(v._gotErrorEvent, true, "(7) Should get error event from first source load failure");
|
||||
maybeFinish(v, 7);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadStart = false;
|
||||
@ -274,6 +286,8 @@ var tests = [
|
||||
v.appendChild(s2);
|
||||
document.body.appendChild(v); // Causes implicit load, which will be halt at preload:none on the second resource.
|
||||
},
|
||||
|
||||
name: "test7",
|
||||
},
|
||||
{
|
||||
// 8. Change preload value from none to metadata should cause metadata to be loaded.
|
||||
@ -286,7 +300,7 @@ var tests = [
|
||||
// is(v.networkState, v.NETWORK_IDLE, "(8) NetworkState must be NETWORK_IDLE when load is halted");
|
||||
maybeFinish(v, 8);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadedMetaData = false;
|
||||
@ -297,6 +311,8 @@ var tests = [
|
||||
v.src = test.name; // Causes implicit load.
|
||||
document.body.appendChild(v);
|
||||
},
|
||||
|
||||
name: "test8",
|
||||
},
|
||||
/*{
|
||||
// 9. Change preload value from metadata to auto should cause entire media to be loaded.
|
||||
@ -309,7 +325,7 @@ var tests = [
|
||||
is(v._gotLoadedMetaData, true, "(9) Must get loadedmetadata.");
|
||||
maybeFinish(v, 9);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadStart = false;
|
||||
@ -331,7 +347,7 @@ var tests = [
|
||||
is(v._gotLoadedMetaData, true, "(10) Must get loadedmetadata.");
|
||||
maybeFinish(v, 10);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadedMetaData = false;
|
||||
@ -342,6 +358,8 @@ var tests = [
|
||||
v.src = test.name; // Causes implicit load.
|
||||
document.body.appendChild(v);
|
||||
},
|
||||
|
||||
name: "test10",
|
||||
},
|
||||
{
|
||||
// 11. Change preload value from none to metadata should cause metadata to load.
|
||||
@ -365,10 +383,14 @@ var tests = [
|
||||
v.src = test.name; // Causes implicit load.
|
||||
document.body.appendChild(v);
|
||||
},
|
||||
|
||||
name: "test11",
|
||||
},
|
||||
{
|
||||
/*{
|
||||
// 12. Change preload value from auto to metadata after load started,
|
||||
// should still do full load, should not halt after metadata only.
|
||||
// disable this test since the spec is no longer found in the document
|
||||
// http://dev.w3.org/html5/spec-preview/media-elements.html
|
||||
canplaythrough:
|
||||
function(e) {
|
||||
var v = e.target;
|
||||
@ -389,7 +411,9 @@ var tests = [
|
||||
document.body.appendChild(v);
|
||||
v.preload = "metadata";
|
||||
},
|
||||
},
|
||||
|
||||
name: "test12",
|
||||
},*/
|
||||
{
|
||||
// 13. Change preload value from auto to none after specifying a src
|
||||
// should load according to preload none, no buffering should have taken place
|
||||
@ -416,7 +440,9 @@ var tests = [
|
||||
v.addEventListener("suspend", this.suspend, false);
|
||||
document.body.appendChild(v); // Causes implicit load, should load according to preload none
|
||||
var s = document.createElement("source");
|
||||
}
|
||||
},
|
||||
|
||||
name: "test13",
|
||||
},
|
||||
{
|
||||
// 14. Add preload:metadata video with src to document. Play(), should play through.
|
||||
@ -430,7 +456,7 @@ var tests = [
|
||||
// is(v.networkState, v.NETWORK_IDLE, "(14) NetworkState must be NETWORK_IDLE");
|
||||
v.play();
|
||||
},
|
||||
|
||||
|
||||
ended:
|
||||
function(e) {
|
||||
ok(true, "(14) Got playback ended");
|
||||
@ -451,6 +477,8 @@ var tests = [
|
||||
document.body.appendChild(v); // Causes implicit load, which will be halted after
|
||||
// metadata due to preload:metadata.
|
||||
},
|
||||
|
||||
name: "test14",
|
||||
},
|
||||
{
|
||||
// 15. Autoplay should override preload:none.
|
||||
@ -460,7 +488,7 @@ var tests = [
|
||||
var v = e.target;
|
||||
maybeFinish(v, 15);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v._gotLoadStart = false;
|
||||
@ -473,6 +501,8 @@ var tests = [
|
||||
v.src = test.name; // Causes implicit load.
|
||||
document.body.appendChild(v);
|
||||
},
|
||||
|
||||
name: "test15",
|
||||
},
|
||||
{
|
||||
// 16. Autoplay should override preload:metadata.
|
||||
@ -482,7 +512,7 @@ var tests = [
|
||||
var v = e.target;
|
||||
maybeFinish(v, 16);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v.preload = "metadata";
|
||||
@ -491,6 +521,8 @@ var tests = [
|
||||
v.src = test.name; // Causes implicit load.
|
||||
document.body.appendChild(v);
|
||||
},
|
||||
|
||||
name: "test16",
|
||||
},
|
||||
{
|
||||
// 17. On a preload:none video, adding autoplay should disable preload none, i.e. don't break autoplay!
|
||||
@ -500,7 +532,7 @@ var tests = [
|
||||
var v = e.target;
|
||||
maybeFinish(v, 17);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v.addEventListener("ended", this.ended, false);
|
||||
@ -508,7 +540,9 @@ var tests = [
|
||||
document.body.appendChild(v); // Causes implicit load, which will be halted due to preload:none.
|
||||
v.autoplay = true;
|
||||
v.src = test.name;
|
||||
},
|
||||
},
|
||||
|
||||
name: "test17",
|
||||
},
|
||||
{
|
||||
// 18. On a preload='none' video, call play() before load algorithms's sync
|
||||
@ -519,7 +553,7 @@ var tests = [
|
||||
var v = e.target;
|
||||
maybeFinish(v, 18);
|
||||
},
|
||||
|
||||
|
||||
setup:
|
||||
function(v) {
|
||||
v.addEventListener("ended", this.ended, false);
|
||||
@ -527,7 +561,9 @@ var tests = [
|
||||
v.src = test.name; // Schedules async section to continue load algorithm.
|
||||
document.body.appendChild(v);
|
||||
v.play(); // Should cause preload:none to be overridden.
|
||||
},
|
||||
},
|
||||
|
||||
name: "test18",
|
||||
},
|
||||
{
|
||||
// 19. Set preload='auto' on first video source then switching preload='none' and swapping the video source to another.
|
||||
@ -555,7 +591,9 @@ var tests = [
|
||||
// add a listener for when the video has loaded, so we know preload auto has worked
|
||||
v.addEventListener( "loadedmetadata", this.loadedmetadata, false);
|
||||
document.body.appendChild(v);
|
||||
}
|
||||
},
|
||||
|
||||
name: "test19",
|
||||
}
|
||||
];
|
||||
|
||||
@ -563,6 +601,7 @@ var iterationCount = 0;
|
||||
function startTest(test, token) {
|
||||
if (test == tests[0]) {
|
||||
++iterationCount;
|
||||
info("iterationCount=" + iterationCount);
|
||||
}
|
||||
if (iterationCount == 2) {
|
||||
// Do this series of tests on logically different resources
|
||||
|
@ -28,12 +28,20 @@ function checkComplete() {
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
||||
function removeNode(v) {
|
||||
v.removeEventListener("error", loadError, false);
|
||||
v.removeEventListener("loadedmetadata", loadedMetadata, false);
|
||||
v.remove();
|
||||
v.src = "";
|
||||
}
|
||||
|
||||
function loadError(evt) {
|
||||
// If no referer is sent then the sjs returns an error
|
||||
ok(false, "check referer is sent with media request");
|
||||
evt.target._complete = true;
|
||||
checkComplete();
|
||||
removeNode(evt.target);
|
||||
}
|
||||
|
||||
function loadedMetadata(evt) {
|
||||
@ -41,6 +49,7 @@ function loadedMetadata(evt) {
|
||||
ok(true, "check referer is sent with media request");
|
||||
evt.target._complete = true;
|
||||
checkComplete();
|
||||
removeNode(evt.target);
|
||||
}
|
||||
|
||||
// Create all media objects.
|
||||
@ -56,6 +65,8 @@ for (var i=0; i<gSmallTests.length; ++i) {
|
||||
if (!v.canPlayType(test.type)) {
|
||||
continue;
|
||||
}
|
||||
// ensure metadata is loaded for default preload is none on b2g
|
||||
v.preload = "metadata";
|
||||
v._complete = false;
|
||||
v.addEventListener("error", loadError, false);
|
||||
v.addEventListener("loadedmetadata", loadedMetadata, false);
|
||||
|
@ -25,7 +25,16 @@ interface nsIRilMobileMessageDatabaseRecordCallback : nsISupports
|
||||
void notify(in nsresult aRv, in jsval aMessageRecord, in nsISupports aDomMessage);
|
||||
};
|
||||
|
||||
[scriptable, uuid(596ef782-9f7a-11e3-8508-ff3a7d599531)]
|
||||
[scriptable, function, uuid(1b0ff03c-a2bc-11e3-a443-838d034c9805)]
|
||||
interface nsIRilMobileMessageDatabaseConcatenationCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* |aCompleteMessage|: jsval: the completely concatenated message. Noted, this value might be null.
|
||||
*/
|
||||
void notify(in nsresult aRv, in jsval aCompleteMessage);
|
||||
};
|
||||
|
||||
[scriptable, uuid(0b437a5c-a2bc-11e3-bd1b-dbb173eb35f8)]
|
||||
interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
|
||||
{
|
||||
/**
|
||||
@ -126,4 +135,12 @@ interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
|
||||
* @returns the error code defined in nsIMobileMessageCallback
|
||||
*/
|
||||
jsval translateCrErrorToMessageCallbackError(in nsresult aCrError);
|
||||
|
||||
/**
|
||||
* |aSmsSegment| jsval: Decoded Single SMS PDU.
|
||||
* |aCallback| nsIRilMobileMessageDatabaseConcatenationCallback: a callback which
|
||||
* takes result flag, and complete mesage as parameters.
|
||||
*/
|
||||
void saveSmsSegment(in jsval aSmsSegment,
|
||||
in nsIRilMobileMessageDatabaseConcatenationCallback aCallback);
|
||||
};
|
||||
|
@ -23,11 +23,12 @@ const DEBUG = false;
|
||||
const DISABLE_MMS_GROUPING_FOR_RECEIVING = true;
|
||||
|
||||
|
||||
const DB_VERSION = 21;
|
||||
const DB_VERSION = 22;
|
||||
const MESSAGE_STORE_NAME = "sms";
|
||||
const THREAD_STORE_NAME = "thread";
|
||||
const PARTICIPANT_STORE_NAME = "participant";
|
||||
const MOST_RECENT_STORE_NAME = "most-recent";
|
||||
const SMS_SEGMENT_STORE_NAME = "sms-segment";
|
||||
|
||||
const DELIVERY_SENDING = "sending";
|
||||
const DELIVERY_SENT = "sent";
|
||||
@ -240,6 +241,10 @@ MobileMessageDB.prototype = {
|
||||
self.upgradeSchema20(event.target.transaction, next);
|
||||
break;
|
||||
case 21:
|
||||
if (DEBUG) debug("Upgrade to version 22. Add sms-segment store.");
|
||||
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.");
|
||||
break;
|
||||
@ -1344,6 +1349,65 @@ MobileMessageDB.prototype = {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Add smsSegmentStore to store uncomplete SMS segments.
|
||||
*/
|
||||
upgradeSchema21: function(db, transaction, next) {
|
||||
/**
|
||||
* This smsSegmentStore is used to store uncomplete SMS segments.
|
||||
* Each entry looks like this:
|
||||
*
|
||||
* {
|
||||
* [Common fields in SMS segment]
|
||||
* messageType: <Number>,
|
||||
* teleservice: <Number>,
|
||||
* SMSC: <String>,
|
||||
* sentTimestamp: <Number>,
|
||||
* timestamp: <Number>,
|
||||
* sender: <String>,
|
||||
* pid: <Number>,
|
||||
* encoding: <Number>,
|
||||
* messageClass: <String>,
|
||||
* iccId: <String>,
|
||||
*
|
||||
* [Concatenation Info]
|
||||
* segmentRef: <Number>,
|
||||
* segmentSeq: <Number>,
|
||||
* segmentMaxSeq: <Number>,
|
||||
*
|
||||
* [Application Port Info]
|
||||
* originatorPort: <Number>,
|
||||
* destinationPort: <Number>,
|
||||
*
|
||||
* [MWI status]
|
||||
* mwiPresent: <Boolean>,
|
||||
* mwiDiscard: <Boolean>,
|
||||
* mwiMsgCount: <Number>,
|
||||
* mwiActive: <Boolean>,
|
||||
*
|
||||
* [CDMA Cellbroadcast related fields]
|
||||
* serviceCategory: <Number>,
|
||||
* language: <String>,
|
||||
*
|
||||
* [Message Body]
|
||||
* data: <Uint8Array>, (available if it's 8bit encoding)
|
||||
* body: <String>, (normal text body)
|
||||
*
|
||||
* [Handy fields created by DB for concatenation]
|
||||
* id: <Number>, keypath of this objectStore.
|
||||
* hash: <String>, Use to identify the segments to the same SMS.
|
||||
* receivedSegments: <Number>,
|
||||
* segments: []
|
||||
* }
|
||||
*
|
||||
*/
|
||||
let smsSegmentStore = db.createObjectStore(SMS_SEGMENT_STORE_NAME,
|
||||
{ keyPath: "id",
|
||||
autoIncrement: true });
|
||||
smsSegmentStore.createIndex("hash", "hash", { unique: true });
|
||||
next();
|
||||
},
|
||||
|
||||
matchParsedPhoneNumbers: function(addr1, parsedAddr1, addr2, parsedAddr2) {
|
||||
if ((parsedAddr1.internationalNumber &&
|
||||
parsedAddr1.internationalNumber === parsedAddr2.internationalNumber) ||
|
||||
@ -2428,6 +2492,128 @@ MobileMessageDB.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
saveSmsSegment: function(aSmsSegment, aCallback) {
|
||||
let completeMessage = null;
|
||||
this.newTxn(READ_WRITE, function(error, txn, segmentStore) {
|
||||
if (error) {
|
||||
if (DEBUG) debug(error);
|
||||
aCallback.notify(error, null);
|
||||
return;
|
||||
}
|
||||
|
||||
txn.oncomplete = function oncomplete(event) {
|
||||
if (DEBUG) debug("Transaction " + txn + " completed.");
|
||||
if (completeMessage) {
|
||||
// Rebuild full body
|
||||
if (completeMessage.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
|
||||
// Uint8Array doesn't have `concat`, so
|
||||
// we have to merge all segements by hand.
|
||||
let fullDataLen = 0;
|
||||
for (let i = 1; i <= completeMessage.segmentMaxSeq; i++) {
|
||||
fullDataLen += completeMessage.segments[i].length;
|
||||
}
|
||||
|
||||
completeMessage.fullData = new Uint8Array(fullDataLen);
|
||||
for (let d = 0, i = 1; i <= completeMessage.segmentMaxSeq; i++) {
|
||||
let data = completeMessage.segments[i];
|
||||
for (let j = 0; j < data.length; j++) {
|
||||
completeMessage.fullData[d++] = data[j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completeMessage.fullBody = completeMessage.segments.join("");
|
||||
}
|
||||
|
||||
// Remove handy fields after completing the concatenation.
|
||||
delete completeMessage.id;
|
||||
delete completeMessage.hash;
|
||||
delete completeMessage.receivedSegments;
|
||||
delete completeMessage.segments;
|
||||
}
|
||||
aCallback.notify(Cr.NS_OK, completeMessage);
|
||||
};
|
||||
|
||||
txn.onabort = function onerror(event) {
|
||||
if (DEBUG) debug("Caught error on transaction", event.target.errorCode);
|
||||
aCallback.notify(Cr.NS_ERROR_FAILURE, null, null);
|
||||
};
|
||||
|
||||
aSmsSegment.hash = aSmsSegment.sender + ":" +
|
||||
aSmsSegment.segmentRef + ":" +
|
||||
aSmsSegment.segmentMaxSeq + ":" +
|
||||
aSmsSegment.iccId;
|
||||
let seq = aSmsSegment.segmentSeq;
|
||||
if (DEBUG) {
|
||||
debug("Saving SMS Segment: " + aSmsSegment.hash + ", seq: " + seq);
|
||||
}
|
||||
let getRequest = segmentStore.index("hash").get(aSmsSegment.hash);
|
||||
getRequest.onsuccess = function(event) {
|
||||
let segmentRecord = event.target.result;
|
||||
if (!segmentRecord) {
|
||||
if (DEBUG) {
|
||||
debug("Not found! Create a new record to store the segments.");
|
||||
}
|
||||
aSmsSegment.receivedSegments = 1;
|
||||
aSmsSegment.segments = [];
|
||||
if (aSmsSegment.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
|
||||
aSmsSegment.segments[seq] = aSmsSegment.data;
|
||||
} else {
|
||||
aSmsSegment.segments[seq] = aSmsSegment.body;
|
||||
}
|
||||
|
||||
segmentStore.add(aSmsSegment);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("Append SMS Segment into existed message object: " + segmentRecord.id);
|
||||
}
|
||||
|
||||
if (segmentRecord.segments[seq]) {
|
||||
if (DEBUG) debug("Got duplicated segment no. " + seq);
|
||||
return;
|
||||
}
|
||||
|
||||
segmentRecord.timestamp = aSmsSegment.timestamp;
|
||||
|
||||
if (segmentRecord.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
|
||||
segmentRecord.segments[seq] = aSmsSegment.data;
|
||||
} else {
|
||||
segmentRecord.segments[seq] = aSmsSegment.body;
|
||||
}
|
||||
segmentRecord.receivedSegments++;
|
||||
|
||||
// The port information is only available in 1st segment for CDMA WAP Push.
|
||||
// If the segments of a WAP Push are not received in sequence
|
||||
// (e.g., SMS with seq == 1 is not the 1st segment received by the device),
|
||||
// we have to retrieve the port information from 1st segment and
|
||||
// save it into the segmentRecord.
|
||||
if (aSmsSegment.teleservice === RIL.PDU_CDMA_MSG_TELESERIVCIE_ID_WAP
|
||||
&& seq === 1) {
|
||||
if (aSmsSegment.originatorPort) {
|
||||
segmentRecord.originatorPort = aSmsSegment.originatorPort;
|
||||
}
|
||||
|
||||
if (aSmsSegment.destinationPort) {
|
||||
segmentRecord.destinationPort = aSmsSegment.destinationPort;
|
||||
}
|
||||
}
|
||||
|
||||
if (segmentRecord.receivedSegments < segmentRecord.segmentMaxSeq) {
|
||||
if (DEBUG) debug("Message is incomplete.");
|
||||
segmentStore.put(segmentRecord);
|
||||
return;
|
||||
}
|
||||
|
||||
completeMessage = segmentRecord;
|
||||
|
||||
// Delete Record in DB
|
||||
segmentStore.delete(segmentRecord.id);
|
||||
};
|
||||
}, [SMS_SEGMENT_STORE_NAME]);
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIMobileMessageDatabaseService API
|
||||
*/
|
||||
|
@ -99,6 +99,10 @@ MobileMessageDatabaseService.prototype = {
|
||||
return this.mmdb.translateCrErrorToMessageCallbackError(aCrError);
|
||||
},
|
||||
|
||||
saveSmsSegment: function(aSmsSegment, aCallback) {
|
||||
this.mmdb.saveSmsSegment(aSmsSegment, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIMobileMessageDatabaseService API
|
||||
*/
|
||||
|
@ -45,6 +45,31 @@ function ensureMobileMessage() {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for one named MobileMessageManager event.
|
||||
*
|
||||
* Resolve if that named event occurs. Never reject.
|
||||
*
|
||||
* Fulfill params: the DOMEvent passed.
|
||||
*
|
||||
* @param aEventName
|
||||
* A string event name.
|
||||
*
|
||||
* @return A deferred promise.
|
||||
*/
|
||||
function waitForManagerEvent(aEventName) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
manager.addEventListener(aEventName, function onevent(aEvent) {
|
||||
manager.removeEventListener(aEventName, onevent);
|
||||
|
||||
ok(true, "MobileMessageManager event '" + aEventName + "' got.");
|
||||
deferred.resolve(aEvent);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a SMS message to a single receiver. Resolve if it succeeds, reject
|
||||
* otherwise.
|
||||
@ -331,6 +356,9 @@ function sendTextSmsToEmulator(aFrom, aText) {
|
||||
/**
|
||||
* Send raw SMS TPDU to emulator.
|
||||
*
|
||||
* @param: aPdu
|
||||
* A hex string representing the whole SMS T-PDU.
|
||||
*
|
||||
* Fulfill params:
|
||||
* result -- an array of emulator response lines.
|
||||
*
|
||||
|
@ -44,3 +44,4 @@ qemu = true
|
||||
[test_mmdb_foreachmatchedmmsdeliveryinfo.js]
|
||||
[test_mmdb_full_storage.js]
|
||||
[test_replace_short_message_type.js]
|
||||
[test_mt_sms_concatenation.js]
|
||||
|
138
dom/mobilemessage/tests/marionette/test_mt_sms_concatenation.js
Normal file
138
dom/mobilemessage/tests/marionette/test_mt_sms_concatenation.js
Normal file
@ -0,0 +1,138 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = 'head.js';
|
||||
|
||||
const PDU_SMSC_NONE = "00"; // no SMSC Address
|
||||
|
||||
const PDU_FIRST_OCTET = "40"; // RP:no, UDHI:yes, SRI:no, MMS:no, MTI:SMS-DELIVER
|
||||
|
||||
const PDU_SENDER = "0A912143658709"; // +1234567890
|
||||
const SENDER = "+1234567890";
|
||||
|
||||
const PDU_PID_NORMAL = "00";
|
||||
|
||||
const PDU_DCS_NORMAL_UCS2 = "08";
|
||||
const PDU_DCS_CLASS0_UCS2 = "18";
|
||||
const PDU_DCS_NORMAL_8BIT = "04";
|
||||
const PDU_DCS_CLASS0_8BIT = "14";
|
||||
|
||||
const PDU_TIMESTAMP = "00101000000000"; // 2000/01/01
|
||||
|
||||
function byteValueToHexString(aValue) {
|
||||
let str = Number(aValue).toString(16).toUpperCase();
|
||||
return str.length == 1 ? "0" + str : str;
|
||||
}
|
||||
|
||||
let ref_num = 0;
|
||||
function buildTextPdus(aDcs) {
|
||||
ref_num++;
|
||||
|
||||
let IEI_CONCATE_1 = "0003" + byteValueToHexString(ref_num) + "0301";
|
||||
let IEI_CONCATE_2 = "0003" + byteValueToHexString(ref_num) + "0302";
|
||||
let IEI_CONCATE_3 = "0003" + byteValueToHexString(ref_num) + "0303";
|
||||
let PDU_UDL = "08"; // UDHL(1) + UDH(5) + UCS2 Char (2)
|
||||
let PDU_UDHL = "05";
|
||||
|
||||
let PDU_UD_A = "0041"; // "A"
|
||||
let PDU_UD_B = "0042"; // "B"
|
||||
let PDU_UD_C = "0043"; // "C"
|
||||
|
||||
let PDU_COMMON = PDU_SMSC_NONE + PDU_FIRST_OCTET + PDU_SENDER +
|
||||
PDU_PID_NORMAL + aDcs + PDU_TIMESTAMP + PDU_UDL + PDU_UDHL;
|
||||
|
||||
return [
|
||||
PDU_COMMON + IEI_CONCATE_1 + PDU_UD_A,
|
||||
PDU_COMMON + IEI_CONCATE_2 + PDU_UD_B,
|
||||
PDU_COMMON + IEI_CONCATE_3 + PDU_UD_C
|
||||
];
|
||||
}
|
||||
|
||||
function buildBinaryPdus(aDcs) {
|
||||
ref_num++;
|
||||
let IEI_PORT = "05040B8423F0";
|
||||
|
||||
let PDU_DATA1 = "C106316170706C69636174696F6E2F76" +
|
||||
"6E642E7761702E6D6D732D6D65737361" +
|
||||
"676500B131302E382E3133302E313800" +
|
||||
"AF84B4818C82986B4430595538595347" +
|
||||
"77464E446741416B4876736C58303141" +
|
||||
"41414141414141008D90890380310096" +
|
||||
"05EA4D4D53008A808E02024188058103" +
|
||||
"015F9083687474703A2F2F6D6D732E65";
|
||||
|
||||
let PDU_DATA2 = "6D6F6D652E6E65743A383030322F6B44" +
|
||||
"3059553859534777464E446741416B48" +
|
||||
"76736C583031414141414141414100";
|
||||
|
||||
let PDU_COMMON = PDU_SMSC_NONE + PDU_FIRST_OCTET + PDU_SENDER +
|
||||
PDU_PID_NORMAL + aDcs + PDU_TIMESTAMP;
|
||||
|
||||
function construstBinaryUserData(aBinaryData, aSeqNum) {
|
||||
let ieiConcat = "0003" + byteValueToHexString(ref_num) + "02" +
|
||||
byteValueToHexString(aSeqNum);
|
||||
|
||||
let udh = IEI_PORT + ieiConcat;
|
||||
let udhl = byteValueToHexString(udh.length / 2);
|
||||
let ud = udhl + udh + aBinaryData;
|
||||
let udl = byteValueToHexString(ud.length / 2);
|
||||
|
||||
return udl + ud;
|
||||
}
|
||||
|
||||
return [
|
||||
PDU_COMMON + construstBinaryUserData(PDU_DATA1, 1),
|
||||
PDU_COMMON + construstBinaryUserData(PDU_DATA2, 2)
|
||||
];
|
||||
}
|
||||
|
||||
function sendRawSmsAndWait(aPdus) {
|
||||
let promises = [];
|
||||
|
||||
promises.push(waitForManagerEvent("received"));
|
||||
for (let pdu of aPdus) {
|
||||
promises.push(sendRawSmsToEmulator(pdu));
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function verifyTextMessage(aMessage, aMessageClass) {
|
||||
is(aMessage.messageClass, aMessageClass, "SmsMessage class");
|
||||
is(aMessage.sender, SENDER, "SmsMessage sender");
|
||||
is(aMessage.body, "ABC", "SmsMessage body");
|
||||
}
|
||||
|
||||
function verifyBinaryMessage(aMessage) {
|
||||
is(aMessage.type, "mms", "MmsMessage type");
|
||||
is(aMessage.delivery, "not-downloaded", "MmsMessage delivery");
|
||||
|
||||
// remove duplicated M-Notification.ind for next test.
|
||||
return deleteMessagesById([aMessage.id]);
|
||||
}
|
||||
|
||||
function testText(aDcs, aClass) {
|
||||
log("testText(): aDcs = " + aDcs + ", aClass = " + aClass);
|
||||
return sendRawSmsAndWait(buildTextPdus(aDcs))
|
||||
.then((resolutions) => verifyTextMessage(resolutions[0].message, aClass));
|
||||
}
|
||||
|
||||
function testBinary(aDcs) {
|
||||
log("testBinary(): aDcs = " + aDcs);
|
||||
return sendRawSmsAndWait(buildBinaryPdus(aDcs))
|
||||
.then((resolutions) => verifyBinaryMessage(resolutions[0].message));
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{"set": [["dom.mms.retrieval_mode", "manual"]]},
|
||||
function startTest() {
|
||||
startTestCommon(function testCaseMain() {
|
||||
return Promise.resolve()
|
||||
.then(() => testText(PDU_DCS_NORMAL_UCS2, "normal"))
|
||||
.then(() => testText(PDU_DCS_CLASS0_UCS2, "class-0"))
|
||||
.then(() => testBinary(PDU_DCS_NORMAL_8BIT))
|
||||
.then(() => testBinary(PDU_DCS_CLASS0_8BIT));
|
||||
});
|
||||
}
|
||||
);
|
@ -1905,6 +1905,8 @@ function RadioInterface(aClientId, aWorkerMessenger) {
|
||||
this.portAddressedSmsApps = {};
|
||||
this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this);
|
||||
|
||||
this._receivedSmsSegmentsMap = {};
|
||||
|
||||
this._sntp = new Sntp(this.setClockBySntp.bind(this),
|
||||
Services.prefs.getIntPref("network.sntp.maxRetryCount"),
|
||||
Services.prefs.getIntPref("network.sntp.refreshPeriod"),
|
||||
@ -2207,13 +2209,8 @@ RadioInterface.prototype = {
|
||||
this.clientId, message);
|
||||
break;
|
||||
case "sms-received":
|
||||
let ackOk = this.handleSmsReceived(message);
|
||||
// Note: ACK has been done by modem for NEW_SMS_ON_SIM
|
||||
if (ackOk && message.simStatus === undefined) {
|
||||
this.workerMessenger.send("ackSMS", { result: RIL.PDU_FCS_OK });
|
||||
}
|
||||
return;
|
||||
case "broadcastsms-received":
|
||||
this.handleSmsMultipart(message);
|
||||
break;
|
||||
case "cellbroadcast-received":
|
||||
message.timestamp = Date.now();
|
||||
gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived",
|
||||
@ -2719,9 +2716,9 @@ RadioInterface.prototype = {
|
||||
let options = {
|
||||
bearer: WAP.WDP_BEARER_GSM_SMS_GSM_MSISDN,
|
||||
sourceAddress: message.sender,
|
||||
sourcePort: message.header.originatorPort,
|
||||
sourcePort: message.originatorPort,
|
||||
destinationAddress: this.rilContext.iccInfo.msisdn,
|
||||
destinationPort: message.header.destinationPort,
|
||||
destinationPort: message.destinationPort,
|
||||
serviceId: this.clientId
|
||||
};
|
||||
WAP.WapPushManager.receiveWdpPDU(message.fullData, message.fullData.length,
|
||||
@ -2767,23 +2764,7 @@ RadioInterface.prototype = {
|
||||
_smsHandledWakeLock: null,
|
||||
_smsHandledWakeLockTimer: null,
|
||||
|
||||
_releaseSmsHandledWakeLock: function() {
|
||||
if (DEBUG) this.debug("Releasing the CPU wake lock for handling SMS.");
|
||||
if (this._smsHandledWakeLockTimer) {
|
||||
this._smsHandledWakeLockTimer.cancel();
|
||||
}
|
||||
if (this._smsHandledWakeLock) {
|
||||
this._smsHandledWakeLock.unlock();
|
||||
this._smsHandledWakeLock = null;
|
||||
}
|
||||
},
|
||||
|
||||
portAddressedSmsApps: null,
|
||||
handleSmsReceived: function(message) {
|
||||
if (DEBUG) this.debug("handleSmsReceived: " + JSON.stringify(message));
|
||||
|
||||
// We need to acquire a CPU wake lock to avoid the system falling into
|
||||
// the sleep mode when the RIL handles the received SMS.
|
||||
_acquireSmsHandledWakeLock: function() {
|
||||
if (!this._smsHandledWakeLock) {
|
||||
if (DEBUG) this.debug("Acquiring a CPU wake lock for handling SMS.");
|
||||
this._smsHandledWakeLock = gPowerManagerService.newWakeLock("cpu");
|
||||
@ -2798,17 +2779,251 @@ RadioInterface.prototype = {
|
||||
.initWithCallback(this._releaseSmsHandledWakeLock.bind(this),
|
||||
SMS_HANDLED_WAKELOCK_TIMEOUT,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
},
|
||||
|
||||
// FIXME: Bug 737202 - Typed arrays become normal arrays when sent to/from workers
|
||||
if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
|
||||
message.fullData = new Uint8Array(message.fullData);
|
||||
_releaseSmsHandledWakeLock: function() {
|
||||
if (DEBUG) this.debug("Releasing the CPU wake lock for handling SMS.");
|
||||
if (this._smsHandledWakeLockTimer) {
|
||||
this._smsHandledWakeLockTimer.cancel();
|
||||
}
|
||||
if (this._smsHandledWakeLock) {
|
||||
this._smsHandledWakeLock.unlock();
|
||||
this._smsHandledWakeLock = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Hash map for received multipart sms fragments. Messages are hashed with
|
||||
* its sender address and concatenation reference number. Three additional
|
||||
* attributes `segmentMaxSeq`, `receivedSegments`, `segments` are inserted.
|
||||
*/
|
||||
_receivedSmsSegmentsMap: null,
|
||||
|
||||
/**
|
||||
* Helper for processing received multipart SMS.
|
||||
*
|
||||
* @return null for handled segments, and an object containing full message
|
||||
* body/data once all segments are received.
|
||||
*/
|
||||
_processReceivedSmsSegment: function(aSegment) {
|
||||
|
||||
// Directly replace full message body for single SMS.
|
||||
if (!(aSegment.segmentMaxSeq && (aSegment.segmentMaxSeq > 1))) {
|
||||
if (aSegment.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
|
||||
aSegment.fullData = aSegment.data;
|
||||
} else {
|
||||
aSegment.fullBody = aSegment.body;
|
||||
}
|
||||
return aSegment;
|
||||
}
|
||||
|
||||
// Handle Concatenation for Class 0 SMS
|
||||
let hash = aSegment.sender + ":" +
|
||||
aSegment.segmentRef + ":" +
|
||||
aSegment.segmentMaxSeq;
|
||||
let seq = aSegment.segmentSeq;
|
||||
|
||||
let options = this._receivedSmsSegmentsMap[hash];
|
||||
if (!options) {
|
||||
options = aSegment;
|
||||
this._receivedSmsSegmentsMap[hash] = options;
|
||||
|
||||
options.receivedSegments = 0;
|
||||
options.segments = [];
|
||||
} else if (options.segments[seq]) {
|
||||
// Duplicated segment?
|
||||
if (DEBUG) {
|
||||
this.debug("Got duplicated segment no." + seq +
|
||||
" of a multipart SMS: " + JSON.stringify(aSegment));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (options.receivedSegments > 0) {
|
||||
// Update received timestamp.
|
||||
options.timestamp = aSegment.timestamp;
|
||||
}
|
||||
|
||||
if (options.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
|
||||
options.segments[seq] = aSegment.data;
|
||||
} else {
|
||||
options.segments[seq] = aSegment.body;
|
||||
}
|
||||
options.receivedSegments++;
|
||||
|
||||
// The port information is only available in 1st segment for CDMA WAP Push.
|
||||
// If the segments of a WAP Push are not received in sequence
|
||||
// (e.g., SMS with seq == 1 is not the 1st segment received by the device),
|
||||
// we have to retrieve the port information from 1st segment and
|
||||
// save it into the cached options.
|
||||
if (aSegment.teleservice === RIL.PDU_CDMA_MSG_TELESERIVCIE_ID_WAP
|
||||
&& seq === 1) {
|
||||
if (!options.originatorPort && aSegment.originatorPort) {
|
||||
options.originatorPort = aSegment.originatorPort;
|
||||
}
|
||||
|
||||
if (!options.destinationPort && aSegment.destinationPort) {
|
||||
options.destinationPort = aSegment.destinationPort;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.receivedSegments < options.segmentMaxSeq) {
|
||||
if (DEBUG) {
|
||||
this.debug("Got segment no." + seq + " of a multipart SMS: " +
|
||||
JSON.stringify(options));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove from map
|
||||
delete this._receivedSmsSegmentsMap[hash];
|
||||
|
||||
// Rebuild full body
|
||||
if (options.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
|
||||
// Uint8Array doesn't have `concat`, so we have to merge all segements
|
||||
// by hand.
|
||||
let fullDataLen = 0;
|
||||
for (let i = 1; i <= options.segmentMaxSeq; i++) {
|
||||
fullDataLen += options.segments[i].length;
|
||||
}
|
||||
|
||||
options.fullData = new Uint8Array(fullDataLen);
|
||||
for (let d= 0, i = 1; i <= options.segmentMaxSeq; i++) {
|
||||
let data = options.segments[i];
|
||||
for (let j = 0; j < data.length; j++) {
|
||||
options.fullData[d++] = data[j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
options.fullBody = options.segments.join("");
|
||||
}
|
||||
|
||||
// Remove handy fields after completing the concatenation.
|
||||
delete options.receivedSegments;
|
||||
delete options.segments;
|
||||
|
||||
if (DEBUG) {
|
||||
this.debug("Got full multipart SMS: " + JSON.stringify(options));
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to create Savable SmsSegment.
|
||||
*/
|
||||
_createSavableSmsSegment: function(aMessage) {
|
||||
// We precisely define what data fields to be stored into
|
||||
// DB here for better data migration.
|
||||
let segment = {};
|
||||
segment.messageType = aMessage.messageType;
|
||||
segment.teleservice = aMessage.teleservice;
|
||||
segment.SMSC = aMessage.SMSC;
|
||||
segment.sentTimestamp = aMessage.sentTimestamp;
|
||||
segment.timestamp = Date.now();
|
||||
segment.sender = aMessage.sender;
|
||||
segment.pid = aMessage.pid;
|
||||
segment.encoding = aMessage.encoding;
|
||||
segment.messageClass = aMessage.messageClass;
|
||||
segment.iccId = this.getIccId();
|
||||
if (aMessage.header) {
|
||||
segment.segmentRef = aMessage.header.segmentRef;
|
||||
segment.segmentSeq = aMessage.header.segmentSeq;
|
||||
segment.segmentMaxSeq = aMessage.header.segmentMaxSeq;
|
||||
segment.originatorPort = aMessage.header.originatorPort;
|
||||
segment.destinationPort = aMessage.header.destinationPort;
|
||||
}
|
||||
segment.mwiPresent = (aMessage.mwi)? true: false;
|
||||
segment.mwiDiscard = (segment.mwiPresent)? aMessage.mwi.discard: false;
|
||||
segment.mwiMsgCount = (segment.mwiPresent)? aMessage.mwi.msgCount: 0;
|
||||
segment.mwiActive = (segment.mwiPresent)? aMessage.mwi.active: false;
|
||||
segment.serviceCategory = aMessage.serviceCategory;
|
||||
segment.language = aMessage.language;
|
||||
segment.data = aMessage.data;
|
||||
segment.body = aMessage.body;
|
||||
|
||||
return segment;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to purge complete message.
|
||||
*
|
||||
* We remove unnessary fields defined in _createSavableSmsSegment() after
|
||||
* completing the concatenation.
|
||||
*/
|
||||
_purgeCompleteSmsMessage: function(aMessage) {
|
||||
// Purge concatenation info
|
||||
delete aMessage.segmentRef;
|
||||
delete aMessage.segmentSeq;
|
||||
delete aMessage.segmentMaxSeq;
|
||||
|
||||
// Purge partial message body
|
||||
delete aMessage.data;
|
||||
delete aMessage.body;
|
||||
},
|
||||
|
||||
/**
|
||||
* handle concatenation of received SMS.
|
||||
*/
|
||||
handleSmsMultipart: function(aMessage) {
|
||||
if (DEBUG) this.debug("handleSmsMultipart: " + JSON.stringify(aMessage));
|
||||
|
||||
this._acquireSmsHandledWakeLock();
|
||||
|
||||
let segment = this._createSavableSmsSegment(aMessage);
|
||||
|
||||
let isMultipart = (segment.segmentMaxSeq && (segment.segmentMaxSeq > 1));
|
||||
let messageClass = segment.messageClass;
|
||||
|
||||
let handleReceivedAndAck = function(aRvOfIncompleteMsg, aCompleteMessage) {
|
||||
if (aCompleteMessage) {
|
||||
this._purgeCompleteSmsMessage(aCompleteMessage);
|
||||
if (this.handleSmsReceived(aCompleteMessage)) {
|
||||
this.sendAckSms(Cr.NS_OK, aCompleteMessage);
|
||||
}
|
||||
// else Ack will be sent after further process in handleSmsReceived.
|
||||
} else {
|
||||
this.sendAckSms(aRvOfIncompleteMsg, segment);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
// No need to access SmsSegmentStore for Class 0 SMS and Single SMS.
|
||||
if (!isMultipart ||
|
||||
(messageClass == RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0])) {
|
||||
// `When a mobile terminated message is class 0 and the MS has the
|
||||
// capability of displaying short messages, the MS shall display the
|
||||
// message immediately and send an acknowledgement to the SC when the
|
||||
// message has successfully reached the MS irrespective of whether
|
||||
// there is memory available in the (U)SIM or ME. The message shall
|
||||
// not be automatically stored in the (U)SIM or ME.`
|
||||
// ~ 3GPP 23.038 clause 4
|
||||
|
||||
handleReceivedAndAck(Cr.NS_OK, // ACK OK For Incomplete Class 0
|
||||
this._processReceivedSmsSegment(segment));
|
||||
} else {
|
||||
gMobileMessageDatabaseService
|
||||
.saveSmsSegment(segment, function notifyResult(aRv, aCompleteMessage) {
|
||||
handleReceivedAndAck(aRv, // Ack according to the result after saving
|
||||
aCompleteMessage);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
portAddressedSmsApps: null,
|
||||
handleSmsReceived: function(message) {
|
||||
if (DEBUG) this.debug("handleSmsReceived: " + JSON.stringify(message));
|
||||
|
||||
if (message.messageType == RIL.PDU_CDMA_MSG_TYPE_BROADCAST) {
|
||||
gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived",
|
||||
this.clientId, message);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Dispatch to registered handler if application port addressing is
|
||||
// available. Note that the destination port can possibly be zero when
|
||||
// representing a UDP/TCP port.
|
||||
if (message.header && message.header.destinationPort != null) {
|
||||
let handler = this.portAddressedSmsApps[message.header.destinationPort];
|
||||
if (message.destinationPort != null) {
|
||||
let handler = this.portAddressedSmsApps[message.destinationPort];
|
||||
if (handler) {
|
||||
handler(message);
|
||||
}
|
||||
@ -2824,8 +3039,6 @@ RadioInterface.prototype = {
|
||||
message.sender = message.sender || null;
|
||||
message.receiver = this.getPhoneNumber();
|
||||
message.body = message.fullBody = message.fullBody || null;
|
||||
message.timestamp = Date.now();
|
||||
message.iccId = this.getIccId();
|
||||
|
||||
if (gSmsService.isSilentNumber(message.sender)) {
|
||||
message.id = -1;
|
||||
@ -2855,8 +3068,14 @@ RadioInterface.prototype = {
|
||||
return true;
|
||||
}
|
||||
|
||||
let mwi = message.mwi;
|
||||
if (mwi) {
|
||||
if (message.mwiPresent) {
|
||||
let mwi = {
|
||||
discard: message.mwiDiscard,
|
||||
msgCount: message.mwiMsgCount,
|
||||
active: message.mwiActive
|
||||
};
|
||||
this.workerMessenger.send("updateMwis", { mwi: mwi });
|
||||
|
||||
mwi.returnNumber = message.sender;
|
||||
mwi.returnMessage = message.fullBody;
|
||||
gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification",
|
||||
@ -2864,7 +3083,7 @@ RadioInterface.prototype = {
|
||||
|
||||
// Dicarded MWI comes without text body.
|
||||
// Hence, we discard it here after notifying the MWI status.
|
||||
if (mwi.discard) {
|
||||
if (message.mwiDiscard) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2872,14 +3091,7 @@ RadioInterface.prototype = {
|
||||
let notifyReceived = function notifyReceived(rv, domMessage) {
|
||||
let success = Components.isSuccessCode(rv);
|
||||
|
||||
// Acknowledge the reception of the SMS.
|
||||
// Note: Ack has been done by modem for NEW_SMS_ON_SIM
|
||||
if (message.simStatus === undefined) {
|
||||
this.workerMessenger.send("ackSMS", {
|
||||
result: (success ? RIL.PDU_FCS_OK
|
||||
: RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED)
|
||||
});
|
||||
}
|
||||
this.sendAckSms(rv, message);
|
||||
|
||||
if (!success) {
|
||||
// At this point we could send a message to content to notify the user
|
||||
@ -2926,6 +3138,26 @@ RadioInterface.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle ACK response of received SMS.
|
||||
*/
|
||||
sendAckSms: function(aRv, aMessage) {
|
||||
if (aMessage.messageClass === RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_2]) {
|
||||
return;
|
||||
}
|
||||
|
||||
let result = RIL.PDU_FCS_OK;
|
||||
if (!Components.isSuccessCode(aRv)) {
|
||||
if (DEBUG) this.debug("Failed to handle received sms: " + aRv);
|
||||
result = (aRv === Cr.NS_ERROR_FILE_NO_DEVICE_SPACE)
|
||||
? RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED
|
||||
: RIL.PDU_FCS_UNSPECIFIED;
|
||||
}
|
||||
|
||||
this.workerMessenger.send("ackSMS", { result: result });
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the setting value of "time.clock.automatic-update.available".
|
||||
*/
|
||||
|
@ -213,7 +213,6 @@ function RilObject(aContext) {
|
||||
this.currentCalls = {};
|
||||
this.currentConference = {state: null, participants: {}};
|
||||
this.currentDataCalls = {};
|
||||
this._receivedSmsSegmentsMap = {};
|
||||
this._pendingSentSmsMap = {};
|
||||
this.pendingNetworkType = {};
|
||||
this._receivedSmsCbPagesMap = {};
|
||||
@ -243,13 +242,6 @@ RilObject.prototype = {
|
||||
*/
|
||||
currentDataCalls: null,
|
||||
|
||||
/**
|
||||
* Hash map for received multipart sms fragments. Messages are hashed with
|
||||
* its sender address and concatenation reference number. Three additional
|
||||
* attributes `segmentMaxSeq`, `receivedSegments`, `segments` are inserted.
|
||||
*/
|
||||
_receivedSmsSegmentsMap: null,
|
||||
|
||||
/**
|
||||
* Outgoing messages waiting for SMS-STATUS-REPORT.
|
||||
*/
|
||||
@ -378,7 +370,7 @@ RilObject.prototype = {
|
||||
this.deactivateDataCall(datacall);
|
||||
}
|
||||
|
||||
// Don't clean up this._receivedSmsSegmentsMap or this._pendingSentSmsMap
|
||||
// Don't clean up this._pendingSentSmsMap
|
||||
// because on rild restart: we may continue with the pending segments.
|
||||
|
||||
/**
|
||||
@ -1814,6 +1806,15 @@ RilObject.prototype = {
|
||||
Buf.sendParcel();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update received MWI into EF_MWIS.
|
||||
*/
|
||||
updateMwis: function(options) {
|
||||
if (this.context.ICCUtilsHelper.isICCServiceAvailable("MWIS")) {
|
||||
this.context.SimRecordHelper.updateMWIS(options.mwi);
|
||||
}
|
||||
},
|
||||
|
||||
setCellBroadcastDisabled: function(options) {
|
||||
this.cellBroadcastDisabled = options.disabled;
|
||||
|
||||
@ -4396,55 +4397,19 @@ RilObject.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper for processing multipart SMS.
|
||||
* Helper to delegate the received sms segment to RadioInterface to process.
|
||||
*
|
||||
* @param message
|
||||
* Received sms message.
|
||||
*
|
||||
* @return A failure cause defined in 3GPP 23.040 clause 9.2.3.22.
|
||||
* @return MOZ_FCS_WAIT_FOR_EXPLICIT_ACK
|
||||
*/
|
||||
_processSmsMultipart: function(message) {
|
||||
if (message.header && message.header.segmentMaxSeq &&
|
||||
(message.header.segmentMaxSeq > 1)) {
|
||||
message = this._processReceivedSmsSegment(message);
|
||||
} else {
|
||||
if (message.encoding == PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
|
||||
message.fullData = message.data;
|
||||
delete message.data;
|
||||
} else {
|
||||
message.fullBody = message.body;
|
||||
delete message.body;
|
||||
}
|
||||
}
|
||||
message.rilMessageType = "sms-received";
|
||||
|
||||
if (message) {
|
||||
message.result = PDU_FCS_OK;
|
||||
if (message.messageClass == GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_2]) {
|
||||
// `MS shall ensure that the message has been to the SMS data field in
|
||||
// the (U)SIM before sending an ACK to the SC.` ~ 3GPP 23.038 clause 4
|
||||
message.result = PDU_FCS_RESERVED;
|
||||
}
|
||||
this.sendChromeMessage(message);
|
||||
|
||||
if (message.messageType == PDU_CDMA_MSG_TYPE_BROADCAST) {
|
||||
message.rilMessageType = "broadcastsms-received";
|
||||
} else {
|
||||
message.rilMessageType = "sms-received";
|
||||
}
|
||||
|
||||
this.sendChromeMessage(message);
|
||||
|
||||
// Update MWI Status into ICC if present.
|
||||
if (message.mwi &&
|
||||
this.context.ICCUtilsHelper.isICCServiceAvailable("MWIS")) {
|
||||
this.context.SimRecordHelper.updateMWIS(message.mwi);
|
||||
}
|
||||
|
||||
// We will acknowledge receipt of the SMS after we try to store it
|
||||
// in the database.
|
||||
return MOZ_FCS_WAIT_FOR_EXPLICIT_ACK;
|
||||
}
|
||||
|
||||
return PDU_FCS_OK;
|
||||
return MOZ_FCS_WAIT_FOR_EXPLICIT_ACK;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -4603,95 +4568,6 @@ RilObject.prototype = {
|
||||
return this._processSmsMultipart(message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper for processing received multipart SMS.
|
||||
*
|
||||
* @return null for handled segments, and an object containing full message
|
||||
* body/data once all segments are received.
|
||||
*/
|
||||
_processReceivedSmsSegment: function(original) {
|
||||
let hash = original.sender + ":" + original.header.segmentRef;
|
||||
let seq = original.header.segmentSeq;
|
||||
|
||||
let options = this._receivedSmsSegmentsMap[hash];
|
||||
if (!options) {
|
||||
options = original;
|
||||
this._receivedSmsSegmentsMap[hash] = options;
|
||||
|
||||
options.segmentMaxSeq = original.header.segmentMaxSeq;
|
||||
options.receivedSegments = 0;
|
||||
options.segments = [];
|
||||
} else if (options.segments[seq]) {
|
||||
// Duplicated segment?
|
||||
if (DEBUG) {
|
||||
this.context.debug("Got duplicated segment no." + seq +
|
||||
" of a multipart SMS: " + JSON.stringify(original));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (options.encoding == PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
|
||||
options.segments[seq] = original.data;
|
||||
delete original.data;
|
||||
} else {
|
||||
options.segments[seq] = original.body;
|
||||
delete original.body;
|
||||
}
|
||||
options.receivedSegments++;
|
||||
|
||||
// The port information is only available in 1st segment for CDMA WAP Push.
|
||||
// If the segments of a WAP Push are not received in sequence
|
||||
// (e.g., SMS with seq == 1 is not the 1st segment received by the device),
|
||||
// we have to retrieve the port information from 1st segment and
|
||||
// save it into the cached options.header.
|
||||
if (original.teleservice === PDU_CDMA_MSG_TELESERIVCIE_ID_WAP && seq === 1) {
|
||||
if (!options.header.originatorPort && original.header.originatorPort) {
|
||||
options.header.originatorPort = original.header.originatorPort;
|
||||
}
|
||||
|
||||
if (!options.header.destinationPort && original.header.destinationPort) {
|
||||
options.header.destinationPort = original.header.destinationPort;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.receivedSegments < options.segmentMaxSeq) {
|
||||
if (DEBUG) {
|
||||
this.context.debug("Got segment no." + seq + " of a multipart SMS: " +
|
||||
JSON.stringify(options));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove from map
|
||||
delete this._receivedSmsSegmentsMap[hash];
|
||||
|
||||
// Rebuild full body
|
||||
if (options.encoding == PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
|
||||
// Uint8Array doesn't have `concat`, so we have to merge all segements
|
||||
// by hand.
|
||||
let fullDataLen = 0;
|
||||
for (let i = 1; i <= options.segmentMaxSeq; i++) {
|
||||
fullDataLen += options.segments[i].length;
|
||||
}
|
||||
|
||||
options.fullData = new Uint8Array(fullDataLen);
|
||||
for (let d= 0, i = 1; i <= options.segmentMaxSeq; i++) {
|
||||
let data = options.segments[i];
|
||||
for (let j = 0; j < data.length; j++) {
|
||||
options.fullData[d++] = data[j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
options.fullBody = options.segments.join("");
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
this.context.debug("Got full multipart SMS: " + JSON.stringify(options));
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper for processing sent multipart SMS.
|
||||
*/
|
||||
@ -7618,24 +7494,24 @@ GsmPDUHelperObject.prototype = {
|
||||
// D:DELIVER, DR:DELIVER-REPORT, S:SUBMIT, SR:SUBMIT-REPORT,
|
||||
// ST:STATUS-REPORT, C:COMMAND
|
||||
// M:Mandatory, O:Optional, X:Unavailable
|
||||
// D DR S SR ST C
|
||||
SMSC: null, // M M M M M M
|
||||
mti: null, // M M M M M M
|
||||
udhi: null, // M M O M M M
|
||||
sender: null, // M X X X X X
|
||||
recipient: null, // X X M X M M
|
||||
pid: null, // M O M O O M
|
||||
epid: null, // M O M O O M
|
||||
dcs: null, // M O M O O X
|
||||
mwi: null, // O O O O O O
|
||||
replace: false, // O O O O O O
|
||||
header: null, // M M O M M M
|
||||
body: null, // M O M O O O
|
||||
data: null, // M O M O O O
|
||||
timestamp: null, // M X X X X X
|
||||
status: null, // X X X X M X
|
||||
scts: null, // X X X M M X
|
||||
dt: null, // X X X X M X
|
||||
// D DR S SR ST C
|
||||
SMSC: null, // M M M M M M
|
||||
mti: null, // M M M M M M
|
||||
udhi: null, // M M O M M M
|
||||
sender: null, // M X X X X X
|
||||
recipient: null, // X X M X M M
|
||||
pid: null, // M O M O O M
|
||||
epid: null, // M O M O O M
|
||||
dcs: null, // M O M O O X
|
||||
mwi: null, // O O O O O O
|
||||
replace: false, // O O O O O O
|
||||
header: null, // M M O M M M
|
||||
body: null, // M O M O O O
|
||||
data: null, // M O M O O O
|
||||
sentTimestamp: null, // M X X X X X
|
||||
status: null, // X X X X M X
|
||||
scts: null, // X X X M M X
|
||||
dt: null, // X X X X M X
|
||||
};
|
||||
|
||||
// SMSC info
|
||||
@ -8904,7 +8780,7 @@ CdmaPDUHelperObject.prototype = {
|
||||
header: message.header,
|
||||
body: message.body,
|
||||
data: message.data,
|
||||
timestamp: message[PDU_CDMA_MSG_USERDATA_TIMESTAMP],
|
||||
sentTimestamp: message[PDU_CDMA_MSG_USERDATA_TIMESTAMP],
|
||||
language: message[PDU_CDMA_LANGUAGE_INDICATOR],
|
||||
status: null,
|
||||
scts: null,
|
||||
|
@ -2979,7 +2979,7 @@ add_test(function test_read_new_sms_on_sim() {
|
||||
do_check_eq("sms-received", postedMessage.rilMessageType);
|
||||
do_check_eq("+0123456789", postedMessage.SMSC);
|
||||
do_check_eq("+9876543210", postedMessage.sender);
|
||||
do_check_eq("How are you?", postedMessage.fullBody);
|
||||
do_check_eq("How are you?", postedMessage.body);
|
||||
}
|
||||
|
||||
do_test();
|
||||
|
@ -307,18 +307,11 @@ add_test(function test_processCdmaSmsWapPush() {
|
||||
do_check_eq(orig_address, postedMessage.sender);
|
||||
do_check_eq(0x23F0, postedMessage.header.originatorPort);
|
||||
do_check_eq(0x0B84, postedMessage.header.destinationPort);
|
||||
do_check_eq(fullDataHexString, bytesToHexString(postedMessage.fullData));
|
||||
do_check_eq(fullDataHexString, bytesToHexString(postedMessage.data));
|
||||
}
|
||||
|
||||
// Verify Single WAP PDU
|
||||
test_CdmaSmsWapPdu(["000102030405060708090A0B0C0D0E0F"]);
|
||||
|
||||
// Verify Concatenated WAP PDUs
|
||||
test_CdmaSmsWapPdu(["000102030405060708090A0B0C0D0E0F", "0F0E0D0C0B0A09080706050403020100"]);
|
||||
|
||||
// Verify Concatenated WAP PDUs received in reversed order.
|
||||
// Note: the port information is only available in 1st segment in CDMA WAP Push.
|
||||
test_CdmaSmsWapPdu(["000102030405060708090A0B0C0D0E0F", "0F0E0D0C0B0A09080706050403020100"], true);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -51,6 +51,8 @@
|
||||
#endif
|
||||
#include "GeckoProfiler.h"
|
||||
#include "mozilla/ipc/ProtocolTypes.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "mozilla/HalTypes.h"
|
||||
|
||||
using namespace base;
|
||||
using namespace mozilla;
|
||||
@ -109,6 +111,11 @@ static void ReleaseCompositorThread()
|
||||
}
|
||||
}
|
||||
|
||||
static void SetThreadPriority()
|
||||
{
|
||||
hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR);
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::StartUpWithExistingThread(MessageLoop* aMsgLoop,
|
||||
PlatformThreadId aThreadID)
|
||||
@ -162,6 +169,7 @@ bool CompositorParent::CreateThread()
|
||||
sCompositorThread = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -200,6 +208,8 @@ CompositorParent::CompositorParent(nsIWidget* aWidget,
|
||||
CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(&AddCompositor,
|
||||
this, &mCompositorID));
|
||||
|
||||
CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(SetThreadPriority));
|
||||
|
||||
mRootLayerTreeID = AllocateLayerTreeId();
|
||||
sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
|
||||
|
||||
|
18
hal/Hal.cpp
18
hal/Hal.cpp
@ -871,6 +871,12 @@ SetProcessPriority(int aPid,
|
||||
aBackgroundLRU));
|
||||
}
|
||||
|
||||
void
|
||||
SetCurrentThreadPriority(ThreadPriority aPriority)
|
||||
{
|
||||
PROXY_IF_SANDBOXED(SetCurrentThreadPriority(aPriority));
|
||||
}
|
||||
|
||||
// From HalTypes.h.
|
||||
const char*
|
||||
ProcessPriorityToString(ProcessPriority aPriority)
|
||||
@ -898,6 +904,18 @@ ProcessPriorityToString(ProcessPriority aPriority)
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
ThreadPriorityToString(ThreadPriority aPriority)
|
||||
{
|
||||
switch (aPriority) {
|
||||
case THREAD_PRIORITY_COMPOSITOR:
|
||||
return "COMPOSITOR";
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
// From HalTypes.h.
|
||||
const char*
|
||||
ProcessPriorityToString(ProcessPriority aPriority,
|
||||
|
@ -496,6 +496,13 @@ void SetProcessPriority(int aPid,
|
||||
hal::ProcessCPUPriority aCPUPriority,
|
||||
uint32_t aLRU = 0);
|
||||
|
||||
/**
|
||||
* Set the current thread's priority to appropriate platform-specific value for
|
||||
* given functionality. Instead of providing arbitrary priority numbers you
|
||||
* must specify a type of function like THREAD_PRIORITY_COMPOSITOR.
|
||||
*/
|
||||
void SetCurrentThreadPriority(hal::ThreadPriority aPriority);
|
||||
|
||||
/**
|
||||
* Register an observer for the FM radio.
|
||||
*/
|
||||
|
@ -99,6 +99,16 @@ enum ProcessCPUPriority {
|
||||
NUM_PROCESS_CPU_PRIORITY
|
||||
};
|
||||
|
||||
// Values that can be passed to hal::SetThreadPriority(). These should be
|
||||
// functional in nature, such as COMPOSITOR, instead of levels, like LOW/HIGH.
|
||||
// This allows us to tune our priority scheme for the system in one place such
|
||||
// that it makes sense holistically for the overall operating system. On gonk
|
||||
// or android we may want different priority schemes than on windows, etc.
|
||||
enum ThreadPriority {
|
||||
THREAD_PRIORITY_COMPOSITOR,
|
||||
NUM_THREAD_PRIORITY
|
||||
};
|
||||
|
||||
// Convert a ProcessPriority enum value (with an optional ProcessCPUPriority)
|
||||
// to a string. The strings returned by this function are statically
|
||||
// allocated; do not attempt to free one!
|
||||
@ -112,6 +122,14 @@ const char*
|
||||
ProcessPriorityToString(ProcessPriority aPriority,
|
||||
ProcessCPUPriority aCPUPriority);
|
||||
|
||||
// Convert a ThreadPriority enum value to a string. The strings returned by
|
||||
// this function are statically allocated; do not attempt to free one!
|
||||
//
|
||||
// If you pass an unknown process priority (or NUM_THREAD_PRIORITY), we
|
||||
// fatally assert in debug builds and otherwise return "???".
|
||||
const char *
|
||||
ThreadPriorityToString(ThreadPriority aPriority);
|
||||
|
||||
/**
|
||||
* Used by ModifyWakeLock
|
||||
*/
|
||||
|
20
hal/fallback/FallbackThreadPriority.cpp
Normal file
20
hal/fallback/FallbackThreadPriority.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "Hal.h"
|
||||
|
||||
using namespace mozilla::hal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
void
|
||||
SetCurrentThreadPriority(ThreadPriority aPriority)
|
||||
{
|
||||
HAL_LOG(("FallbackThreadPriority - SetCurrentThreadPriority(%d)\n",
|
||||
ThreadPriorityToString(aPriority)));
|
||||
}
|
||||
|
||||
} // hal_impl
|
||||
} // namespace mozilla
|
@ -27,6 +27,7 @@
|
||||
#include <sys/resource.h>
|
||||
#include <time.h>
|
||||
#include <asm/page.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
@ -38,6 +39,7 @@
|
||||
#include "hardware_legacy/vibrator.h"
|
||||
#include "hardware_legacy/power.h"
|
||||
#include "libdisplay/GonkDisplay.h"
|
||||
#include "utils/threads.h"
|
||||
|
||||
#include "base/message_loop.h"
|
||||
|
||||
@ -1348,6 +1350,14 @@ SetNiceForPid(int aPid, int aNice)
|
||||
|
||||
int tid = static_cast<int>(tidlong);
|
||||
|
||||
// Do not set the priority of threads running with a real-time policy
|
||||
// as part of the bulk process adjustment. These threads need to run
|
||||
// at their specified priority in order to meet timing guarantees.
|
||||
int schedPolicy = sched_getscheduler(tid);
|
||||
if (schedPolicy == SCHED_FIFO || schedPolicy == SCHED_RR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
// Get and set the task's new priority.
|
||||
int origtaskpriority = getpriority(PRIO_PROCESS, tid);
|
||||
@ -1360,6 +1370,15 @@ SetNiceForPid(int aPid, int aNice)
|
||||
|
||||
int newtaskpriority =
|
||||
std::max(origtaskpriority - origProcPriority + aNice, aNice);
|
||||
|
||||
// Do not reduce priority of threads already running at priorities greater
|
||||
// than normal. These threads are likely special service threads that need
|
||||
// elevated priorities to process audio, display composition, etc.
|
||||
if (newtaskpriority > origtaskpriority &&
|
||||
origtaskpriority < ANDROID_PRIORITY_NORMAL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rv = setpriority(PRIO_PROCESS, tid, newtaskpriority);
|
||||
|
||||
if (rv) {
|
||||
@ -1454,6 +1473,54 @@ SetProcessPriority(int aPid,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SetCurrentThreadPriority(ThreadPriority aPriority)
|
||||
{
|
||||
int policy = SCHED_OTHER;
|
||||
int priorityOrNice = ANDROID_PRIORITY_NORMAL;
|
||||
|
||||
switch(aPriority) {
|
||||
case THREAD_PRIORITY_COMPOSITOR:
|
||||
priorityOrNice = Preferences::GetInt("hal.gonk.compositor.rt_priority", 0);
|
||||
if (priorityOrNice >= sched_get_priority_min(SCHED_FIFO) &&
|
||||
priorityOrNice <= sched_get_priority_max(SCHED_FIFO)) {
|
||||
policy = SCHED_FIFO;
|
||||
} else {
|
||||
priorityOrNice = Preferences::GetInt("hal.gonk.compositor.nice",
|
||||
ANDROID_PRIORITY_URGENT_DISPLAY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG("Unrecognized thread priority %d; Doing nothing", aPriority);
|
||||
return;
|
||||
}
|
||||
|
||||
int tid = gettid();
|
||||
int rv = 0;
|
||||
|
||||
// If a RT scheduler policy is used, then we must set the priority using
|
||||
// sched_setscheduler() and the sched_param.sched_priority value.
|
||||
if (policy == SCHED_FIFO || policy == SCHED_RR) {
|
||||
LOG("Setting thread %d to priority level %s; RT priority %d",
|
||||
tid, ThreadPriorityToString(aPriority), priorityOrNice);
|
||||
sched_param schedParam;
|
||||
schedParam.sched_priority = priorityOrNice;
|
||||
rv = sched_setscheduler(tid, policy, &schedParam);
|
||||
|
||||
// Otherwise priority is solely defined by the nice level, so use the
|
||||
// setpriority() function.
|
||||
} else {
|
||||
LOG("Setting thread %d to priority level %s; nice level %d",
|
||||
tid, ThreadPriorityToString(aPriority), priorityOrNice);
|
||||
rv = setpriority(PRIO_PROCESS, tid, priorityOrNice);
|
||||
}
|
||||
|
||||
if (rv) {
|
||||
LOG("Failed to set thread %d to priority level %s; error code %d",
|
||||
tid, ThreadPriorityToString(aPriority), rv);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FactoryReset()
|
||||
{
|
||||
|
@ -151,6 +151,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
|
||||
'fallback/FallbackProcessPriority.cpp',
|
||||
'fallback/FallbackScreenPower.cpp',
|
||||
'fallback/FallbackSwitch.cpp',
|
||||
'fallback/FallbackThreadPriority.cpp',
|
||||
'fallback/FallbackTime.cpp',
|
||||
'fallback/FallbackWakeLocks.cpp',
|
||||
]
|
||||
|
@ -362,6 +362,12 @@ SetProcessPriority(int aPid,
|
||||
NS_RUNTIMEABORT("Only the main process may set processes' priorities.");
|
||||
}
|
||||
|
||||
void
|
||||
SetCurrentThreadPriority(ThreadPriority aPriority)
|
||||
{
|
||||
NS_RUNTIMEABORT("Only the main process may set thread priorities.");
|
||||
}
|
||||
|
||||
void
|
||||
EnableFMRadio(const hal::FMRadioSettings& aSettings)
|
||||
{
|
||||
|
@ -503,6 +503,9 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
break;
|
||||
|
||||
case SELECTED:
|
||||
flags.add(UpdateFlags.PRIVATE_MODE);
|
||||
setPrivateMode(tab.isPrivate());
|
||||
// Fall through.
|
||||
case LOAD_ERROR:
|
||||
flags.add(UpdateFlags.TITLE);
|
||||
// Fall through.
|
||||
@ -511,12 +514,9 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
// us of a title change, so we don't update the title here.
|
||||
flags.add(UpdateFlags.FAVICON);
|
||||
flags.add(UpdateFlags.SITE_IDENTITY);
|
||||
flags.add(UpdateFlags.PRIVATE_MODE);
|
||||
|
||||
updateBackButton(tab);
|
||||
updateForwardButton(tab);
|
||||
|
||||
setPrivateMode(tab.isPrivate());
|
||||
break;
|
||||
|
||||
case TITLE:
|
||||
|
@ -5,7 +5,7 @@
|
||||
MOZ_APP_BASENAME=Fennec
|
||||
MOZ_APP_VENDOR=Mozilla
|
||||
|
||||
MOZ_APP_VERSION=30.0a1
|
||||
MOZ_APP_VERSION=31.0a1
|
||||
MOZ_APP_UA_NAME=Firefox
|
||||
|
||||
MOZ_BRANDING_DIRECTORY=mobile/android/branding/unofficial
|
||||
|
@ -3,7 +3,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# Definitions used by constants.js.
|
||||
weave_version := 1.32.0
|
||||
weave_version := 1.33.0
|
||||
weave_id := {340c2bbc-ce74-4362-90b5-7c26312808ef}
|
||||
|
||||
# Preprocess files.
|
||||
|
@ -63,7 +63,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm")
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
@ -411,27 +411,48 @@ this.Download.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
// In case the download was restarted while cancellation was in progress,
|
||||
// but the previous attempt actually succeeded before cancellation could
|
||||
// be processed, it is possible that the download has already finished.
|
||||
if (this.succeeded) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Disallow download if parental controls service restricts it.
|
||||
if (yield DownloadIntegration.shouldBlockForParentalControls(this)) {
|
||||
throw new DownloadError({ becauseBlockedByParentalControls: true });
|
||||
}
|
||||
|
||||
// We should check if we have been canceled in the meantime, after all
|
||||
// the previous asynchronous operations have been executed and just
|
||||
// before we call the "execute" method of the saver.
|
||||
if (this._promiseCanceled) {
|
||||
// The exception will become a cancellation in the "catch" block.
|
||||
throw undefined;
|
||||
}
|
||||
|
||||
// Execute the actual download through the saver object.
|
||||
this._saverExecuting = true;
|
||||
yield this.saver.execute(DS_setProgressBytes.bind(this),
|
||||
DS_setProperties.bind(this));
|
||||
|
||||
// Check for application reputation, which requires the entire file to
|
||||
// be downloaded.
|
||||
if (yield DownloadIntegration.shouldBlockForReputationCheck(this)) {
|
||||
// Delete the target file that BackgroundFileSaver already moved
|
||||
// into place.
|
||||
// be downloaded. After that, check for the last time if the download
|
||||
// has been canceled. Both cases require the target file to be deleted,
|
||||
// thus we process both in the same block of code.
|
||||
if ((yield DownloadIntegration.shouldBlockForReputationCheck(this)) ||
|
||||
this._promiseCanceled) {
|
||||
try {
|
||||
yield OS.File.remove(this.target.path);
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
// If this is actually a cancellation, this exception will be changed
|
||||
// in the catch block below.
|
||||
throw new DownloadError({ becauseBlockedByReputationCheck: true });
|
||||
}
|
||||
|
||||
// Update the status properties for a successful download.
|
||||
this.progress = 100;
|
||||
this.succeeded = true;
|
||||
@ -461,6 +482,7 @@ this.Download.prototype = {
|
||||
throw ex;
|
||||
} finally {
|
||||
// Any cancellation request has now been processed.
|
||||
this._saverExecuting = false;
|
||||
this._promiseCanceled = null;
|
||||
|
||||
// Update the status properties, unless a new attempt already started.
|
||||
@ -546,6 +568,12 @@ this.Download.prototype = {
|
||||
*/
|
||||
_promiseCanceled: null,
|
||||
|
||||
/**
|
||||
* True between the call to the "execute" method of the saver and the
|
||||
* completion of the current download attempt.
|
||||
*/
|
||||
_saverExecuting: false,
|
||||
|
||||
/**
|
||||
* Cancels the download.
|
||||
*
|
||||
@ -589,8 +617,12 @@ this.Download.prototype = {
|
||||
this.canceled = true;
|
||||
this._notifyChange();
|
||||
|
||||
// Execute the actual cancellation through the saver object.
|
||||
this.saver.cancel();
|
||||
// Execute the actual cancellation through the saver object, in case it
|
||||
// has already started. Otherwise, the cancellation will be handled just
|
||||
// before the saver is started.
|
||||
if (this._saverExecuting) {
|
||||
this.saver.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
return this._promiseCanceled;
|
||||
@ -1363,31 +1395,6 @@ this.DownloadSaver.prototype = {
|
||||
targetUri);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true if the request's response has been blocked by Windows parental
|
||||
* controls with an HTTP 450 error code.
|
||||
*
|
||||
* @param aRequest
|
||||
* nsIRequest object
|
||||
* @return True if the response is blocked.
|
||||
*/
|
||||
isResponseParentalBlocked: function(aRequest)
|
||||
{
|
||||
// If the HTTP status is 450, then Windows Parental Controls have
|
||||
// blocked this download.
|
||||
if (aRequest instanceof Ci.nsIHttpChannel &&
|
||||
aRequest.responseStatus == 450) {
|
||||
// Cancel the request, but set a flag on the download that can be
|
||||
// retrieved later when handling the cancellation so that the proper
|
||||
// blocked by parental controls error can be thrown.
|
||||
this.download._blockedByParentalControls = true;
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a static representation of the current object state.
|
||||
*
|
||||
@ -1612,7 +1619,14 @@ this.DownloadCopySaver.prototype = {
|
||||
onStartRequest: function (aRequest, aContext) {
|
||||
backgroundFileSaver.onStartRequest(aRequest, aContext);
|
||||
|
||||
if (this.isResponseParentalBlocked(aRequest)) {
|
||||
// Check if the request's response has been blocked by Windows
|
||||
// Parental Controls with an HTTP 450 error code.
|
||||
if (aRequest instanceof Ci.nsIHttpChannel &&
|
||||
aRequest.responseStatus == 450) {
|
||||
// Set a flag that can be retrieved later when handling the
|
||||
// cancellation so that the proper error can be thrown.
|
||||
this.download._blockedByParentalControls = true;
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1707,6 +1721,13 @@ this.DownloadCopySaver.prototype = {
|
||||
}.bind(copySaver),
|
||||
}, null);
|
||||
|
||||
// We should check if we have been canceled in the meantime, after
|
||||
// all the previous asynchronous operations have been executed and
|
||||
// just before we set the _backgroundFileSaver property.
|
||||
if (this._canceled) {
|
||||
throw new DownloadError({ message: "Saver canceled." });
|
||||
}
|
||||
|
||||
// If the operation succeeded, store the object to allow cancellation.
|
||||
this._backgroundFileSaver = backgroundFileSaver;
|
||||
} catch (ex) {
|
||||
@ -1930,10 +1951,6 @@ this.DownloadLegacySaver.prototype = {
|
||||
ex.result == Cr.NS_ERROR_NOT_RESUMABLE) { }
|
||||
}
|
||||
|
||||
if (this.isResponseParentalBlocked(aRequest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For legacy downloads, we must update the referrer at this time.
|
||||
if (aRequest instanceof Ci.nsIHttpChannel && aRequest.referrer) {
|
||||
this.download.source.referrer = aRequest.referrer.spec;
|
||||
|
@ -46,7 +46,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
#endif
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
|
@ -29,7 +29,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadLegacyTransfer
|
||||
@ -89,9 +89,25 @@ DownloadLegacyTransfer.prototype = {
|
||||
|
||||
if ((aStateFlags & Ci.nsIWebProgressListener.STATE_START) &&
|
||||
(aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) {
|
||||
|
||||
// If the request's response has been blocked by Windows Parental Controls
|
||||
// with an HTTP 450 error code, we must cancel the request synchronously.
|
||||
let blockedByParentalControls = aRequest instanceof Ci.nsIHttpChannel &&
|
||||
aRequest.responseStatus == 450;
|
||||
if (blockedByParentalControls) {
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
}
|
||||
|
||||
// The main request has just started. Wait for the associated Download
|
||||
// object to be available before notifying.
|
||||
this._deferDownload.promise.then(download => {
|
||||
// If the request was blocked, now that we have the download object we
|
||||
// should set a flag that can be retrieved later when handling the
|
||||
// cancellation so that the proper error can be thrown.
|
||||
if (blockedByParentalControls) {
|
||||
download._blockedByParentalControls = true;
|
||||
}
|
||||
|
||||
download.saver.onTransferStarted(
|
||||
aRequest,
|
||||
this._cancelable instanceof Ci.nsIHelperAppLauncher);
|
||||
|
@ -37,7 +37,7 @@ const Cr = Components.results;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
|
@ -27,7 +27,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
|
@ -36,7 +36,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadSummary",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
|
||||
"resource://gre/modules/DownloadUIHelper.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
|
@ -806,7 +806,10 @@ add_task(function test_cancel_immediately_restart_immediately()
|
||||
continueResponses();
|
||||
try {
|
||||
yield promiseAttempt;
|
||||
do_throw("The download should have been canceled.");
|
||||
// If we get here, it means that the first attempt actually succeeded. In
|
||||
// fact, this could be a valid outcome, because the cancellation request may
|
||||
// not have been processed in time before the download finished.
|
||||
do_print("The download should have been canceled.");
|
||||
} catch (ex if ex instanceof Downloads.Error) {
|
||||
do_check_false(ex.becauseSourceFailed);
|
||||
do_check_false(ex.becauseTargetFailed);
|
||||
|
@ -34,7 +34,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
@ -731,6 +731,15 @@ add_task(function test_common_initialize()
|
||||
gHttpServer.registerDirectory("/", do_get_file("../data"));
|
||||
gHttpServer.start(-1);
|
||||
|
||||
// Cache locks might prevent concurrent requests to the same resource, and
|
||||
// this may block tests that use the interruptible handlers.
|
||||
Services.prefs.setBoolPref("browser.cache.disk.enable", false);
|
||||
Services.prefs.setBoolPref("browser.cache.memory.enable", false);
|
||||
do_register_cleanup(function () {
|
||||
Services.prefs.clearUserPref("browser.cache.disk.enable");
|
||||
Services.prefs.clearUserPref("browser.cache.memory.enable");
|
||||
});
|
||||
|
||||
registerInterruptibleHandler("/interruptible.txt",
|
||||
function firstPart(aRequest, aResponse) {
|
||||
aResponse.setHeader("Content-Type", "text/plain", false);
|
||||
|
@ -25,6 +25,15 @@ This is likely the same like id.heading in crashes.dtd. -->
|
||||
<!ENTITY aboutSupport.extensionVersion "Version">
|
||||
<!ENTITY aboutSupport.extensionId "ID">
|
||||
|
||||
<!ENTITY aboutSupport.experimentsTitle "Experimental Features">
|
||||
<!ENTITY aboutSupport.experimentName "Name">
|
||||
<!ENTITY aboutSupport.experimentId "ID">
|
||||
<!ENTITY aboutSupport.experimentDescription "Description">
|
||||
<!ENTITY aboutSupport.experimentActive "Active">
|
||||
<!ENTITY aboutSupport.experimentEndDate "End Date">
|
||||
<!ENTITY aboutSupport.experimentHomepage "Homepage">
|
||||
|
||||
|
||||
<!ENTITY aboutSupport.appBasicsTitle "Application Basics">
|
||||
<!ENTITY aboutSupport.appBasicsName "Name">
|
||||
<!ENTITY aboutSupport.appBasicsVersion "Version">
|
||||
|
@ -14,20 +14,21 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"nsIMessageSender");
|
||||
|
||||
function paymentSuccess(aRequestId) {
|
||||
return paymentCallback(aRequestId, "Payment:Success");
|
||||
return function(aResult) {
|
||||
closePaymentWindow(aRequestId, function() {
|
||||
cpmm.sendAsyncMessage("Payment:Success", { requestId: aRequestId,
|
||||
result: aResult });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function paymentFailed(aRequestId) {
|
||||
return paymentCallback(aRequestId, "Payment:Failed");
|
||||
}
|
||||
|
||||
function paymentCallback(aRequestId, aMsg) {
|
||||
return function(aResult) {
|
||||
closePaymentWindow(aRequestId, function() {
|
||||
cpmm.sendAsyncMessage(aMsg, { result: aResult,
|
||||
requestId: aRequestId });
|
||||
});
|
||||
};
|
||||
return function(aErrorMsg) {
|
||||
closePaymentWindow(aRequestId, function() {
|
||||
cpmm.sendAsyncMessage("Payment:Failed", { requestId: aRequestId,
|
||||
errorMsg: aErrorMsg });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
let payments = {};
|
||||
|
@ -1,11 +1,22 @@
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
let { PaymentManager } = Cu.import("resource://gre/modules/Payment.jsm", {});
|
||||
Cu.import("resource://webapprt/modules/WebappRT.jsm");
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let providerWindow = null;
|
||||
let providerUri = "https://example.com:443/webapprtChrome/webapprt/test/chrome/mozpay-success.html?req=";
|
||||
let curTest = 0;
|
||||
|
||||
let tests = [];
|
||||
tests.push({
|
||||
providerUri: "https://example.com:443/webapprtChrome/webapprt/test/chrome/mozpay-success.html?req=",
|
||||
message: "Success."
|
||||
});
|
||||
tests.push({
|
||||
providerUri: "https://example.com:443/webapprtChrome/webapprt/test/chrome/mozpay-failure.html?req=",
|
||||
message: "Chocolate rejected."
|
||||
});
|
||||
|
||||
let jwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJhdWQiOiAibW9j" +
|
||||
"a3BheXByb3ZpZGVyLnBocGZvZ2FwcC5jb20iLCAiaXNzIjogIkVudGVyI" +
|
||||
"HlvdSBhcHAga2V5IGhlcmUhIiwgInJlcXVlc3QiOiB7Im5hbWUiOiAiUG" +
|
||||
@ -21,17 +32,19 @@ function test() {
|
||||
PaymentManager.registeredProviders["mock/payments/inapp/v1"] = {
|
||||
name: "mockprovider",
|
||||
description: "Mock Payment Provider",
|
||||
uri: providerUri,
|
||||
uri: tests[curTest].providerUri,
|
||||
requestMethod: "GET"
|
||||
};
|
||||
|
||||
let providerWindow;
|
||||
|
||||
let winObserver = function(win, topic) {
|
||||
if (topic == "domwindowopened") {
|
||||
win.addEventListener("load", function onLoadWindow() {
|
||||
win.removeEventListener("load", onLoadWindow, false);
|
||||
|
||||
if (win.document.getElementById("content").getAttribute("src") ==
|
||||
(providerUri + jwt)) {
|
||||
(tests[curTest].providerUri + jwt)) {
|
||||
ok(true, "Payment provider window shown.");
|
||||
providerWindow = win;
|
||||
}
|
||||
@ -43,25 +56,45 @@ function test() {
|
||||
|
||||
let mutObserver = null;
|
||||
|
||||
loadWebapp("mozpay.webapp", undefined, function onLoad() {
|
||||
function onLoad() {
|
||||
let msg = gAppBrowser.contentDocument.getElementById("msg");
|
||||
mutObserver = new MutationObserver(function(mutations) {
|
||||
if (msg.textContent == "Success.") {
|
||||
ok(true, "Payment success.");
|
||||
} else {
|
||||
ok(false, "Payment success.");
|
||||
}
|
||||
is(msg.textContent, tests[curTest].message, "Got: " + tests[curTest].message);
|
||||
|
||||
if (providerWindow == null) {
|
||||
if (!providerWindow) {
|
||||
ok(false, "Payment provider window shown.");
|
||||
} else {
|
||||
providerWindow.close();
|
||||
providerWindow = null;
|
||||
}
|
||||
|
||||
finish();
|
||||
runNextTest();
|
||||
});
|
||||
mutObserver.observe(msg, { childList: true });
|
||||
});
|
||||
}
|
||||
|
||||
loadWebapp("mozpay.webapp", undefined, onLoad);
|
||||
|
||||
function runNextTest() {
|
||||
providerWindow = null;
|
||||
if (mutObserver) {
|
||||
mutObserver.disconnect();
|
||||
}
|
||||
|
||||
curTest++;
|
||||
|
||||
if (curTest < tests.length) {
|
||||
PaymentManager.registeredProviders["mock/payments/inapp/v1"].uri = tests[curTest].providerUri;
|
||||
|
||||
gAppBrowser.addEventListener("load", function onLoadH() {
|
||||
gAppBrowser.removeEventListener("load", onLoadH, true);
|
||||
onLoad();
|
||||
}, true);
|
||||
gAppBrowser.reload();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.ww.unregisterNotification(winObserver);
|
||||
|
12
webapprt/test/chrome/mozpay-failure.html
Normal file
12
webapprt/test/chrome/mozpay-failure.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
mozPaymentProvider.paymentFailed('Chocolate rejected.');
|
||||
</script>
|
||||
<p id="msg">Webapp waiting to pay...</p>
|
||||
</body>
|
||||
</html>
|
@ -37,7 +37,7 @@
|
||||
document.getElementById("msg").textContent = "Success.";
|
||||
};
|
||||
request.onerror = function onerror() {
|
||||
document.getElementById("msg").textContent = "Failure.";
|
||||
document.getElementById("msg").textContent = request.error.name;
|
||||
};
|
||||
</script>
|
||||
<p id="msg">Webapp waiting to be paid...</p>
|
||||
|
@ -26,6 +26,7 @@ support-files =
|
||||
mozpay.webapp^headers^
|
||||
mozpay.html
|
||||
mozpay-success.html
|
||||
mozpay-failure.html
|
||||
getUserMedia.webapp
|
||||
getUserMedia.webapp^headers^
|
||||
getUserMedia.html
|
||||
|
@ -21,7 +21,7 @@ namespace mozilla {
|
||||
*/
|
||||
struct Module
|
||||
{
|
||||
static const unsigned int kVersion = 30;
|
||||
static const unsigned int kVersion = 31;
|
||||
|
||||
struct CIDEntry;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user