зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c. a=merge
This commit is contained in:
Коммит
125759fa69
|
@ -32,9 +32,9 @@ let principaluri = Cc["@mozilla.org/network/io-service;1"].
|
|||
getService(Ci.nsIIOService).
|
||||
newURI(PSEUDOURI, null, null);
|
||||
|
||||
let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
|
||||
getService(Ci.nsIScriptSecurityManager).
|
||||
getCodebasePrincipal(principaluri);
|
||||
let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
let principal = ssm.createCodebasePrincipal(principaluri, {});
|
||||
|
||||
function toArray(args) {
|
||||
return Array.prototype.slice.call(args);
|
||||
|
|
|
@ -154,11 +154,10 @@ this.AboutServiceWorkers = {
|
|||
return;
|
||||
}
|
||||
|
||||
let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
|
||||
let principal = Services.scriptSecurityManager.createCodebasePrincipal(
|
||||
// TODO: Bug 1196652. use originNoSuffix
|
||||
Services.io.newURI(message.principal.origin, null, null),
|
||||
message.principal.originAttributes.appId,
|
||||
message.principal.originAttributes.inBrowser
|
||||
);
|
||||
message.principal.originAttributes);
|
||||
|
||||
if (!message.scope) {
|
||||
self.sendError(message.id, "MissingScope");
|
||||
|
|
|
@ -205,9 +205,9 @@ ContentPermissionPrompt.prototype = {
|
|||
// URL.
|
||||
let notDenyAppPrincipal = function(type) {
|
||||
let url = Services.io.newURI(app.origin, null, null);
|
||||
let principal = secMan.getAppCodebasePrincipal(url,
|
||||
request.principal.appId,
|
||||
/*mozbrowser*/false);
|
||||
let principal =
|
||||
secMan.createCodebasePrincipal(url,
|
||||
{appId: request.principal.appId});
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(principal,
|
||||
type.access);
|
||||
|
||||
|
|
|
@ -55,7 +55,8 @@
|
|||
// the error message.
|
||||
if (!config.origin) {
|
||||
let URI = Services.io.newURI(url, null, null);
|
||||
config.origin = Services.scriptSecurityManager.getNoAppCodebasePrincipal(URI).origin;
|
||||
config.origin =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(URI, {}).origin;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
|
|
|
@ -11,7 +11,7 @@ const URL = "http://mochi.test:8888/browser/browser/base/content/test/general/of
|
|||
registerCleanupFunction(function() {
|
||||
// Clean up after ourself
|
||||
let uri = Services.io.newURI(URL, null, null);
|
||||
var principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||
let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
||||
Services.perms.removeFromPrincipal(principal, "offline-app");
|
||||
Services.prefs.clearUserPref("offline-apps.quota.warn");
|
||||
Services.prefs.clearUserPref("offline-apps.allow_by_default");
|
||||
|
|
|
@ -564,7 +564,7 @@ var gAllTests = [
|
|||
|
||||
var sm = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
var principal = sm.getNoAppCodebasePrincipal(URI);
|
||||
var principal = sm.createCodebasePrincipal(URI, {});
|
||||
|
||||
// Give www.example.com privileges to store offline data
|
||||
var pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
|
@ -634,7 +634,7 @@ var gAllTests = [
|
|||
|
||||
var sm = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
var principal = sm.getNoAppCodebasePrincipal(URI);
|
||||
var principal = sm.createCodebasePrincipal(URI, {});
|
||||
|
||||
// Open the dialog
|
||||
let wh = new WindowHelper();
|
||||
|
|
|
@ -43,12 +43,10 @@ window.addEventListener("message", function(event) {
|
|||
var uri1 = ioService.newURI(frames.testFrame.location, null, null);
|
||||
var uri2 = ioService.newURI(frames.testFrame3.location, null, null);
|
||||
|
||||
var principal1 = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri1);
|
||||
var principal2 = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri2);
|
||||
var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIScriptSecurityManager);
|
||||
var principal1 = ssm.createCodebasePrincipal(uri1, {});
|
||||
var principal2 = ssm.createCodebasePrincipal(uri2, {});
|
||||
|
||||
pm.removeFromPrincipal(principal1, "offline-app");
|
||||
pm.removeFromPrincipal(principal2, "offline-app");
|
||||
|
|
|
@ -39,9 +39,9 @@ function finishTest() {
|
|||
|
||||
var uri = Cc["@mozilla.org/network/io-service;1"].getService(SpecialPowers.Ci.nsIIOService)
|
||||
.newURI(window.frames[0].location, null, null);
|
||||
var principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri);
|
||||
var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIScriptSecurityManager);
|
||||
var principal = ssm.createCodebasePrincipal(uri, {});
|
||||
|
||||
pm.removeFromPrincipal(principal, "offline-app");
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ Cu.import("resource://gre/modules/Timer.jsm", tmp);
|
|||
let {Promise, NewTabUtils, Sanitizer, clearTimeout, setTimeout, DirectoryLinksProvider, PlacesTestUtils} = tmp;
|
||||
|
||||
let uri = Services.io.newURI("about:newtab", null, null);
|
||||
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||
let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
||||
|
||||
let isMac = ("nsILocalFileMac" in Ci);
|
||||
let isLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc);
|
||||
|
|
|
@ -254,7 +254,7 @@ FeedConverter.prototype = {
|
|||
chromeChannel = ios.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo);
|
||||
chromeChannel.originalURI = result.uri;
|
||||
chromeChannel.owner =
|
||||
Services.scriptSecurityManager.getNoAppCodebasePrincipal(aboutFeedsURI);
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(aboutFeedsURI, {});
|
||||
} else {
|
||||
chromeChannel = ios.newChannelFromURIWithLoadInfo(result.uri, loadInfo);
|
||||
}
|
||||
|
|
|
@ -497,7 +497,7 @@ let AboutPermissions = {
|
|||
while (row = aResults.getNextRow()) {
|
||||
let spec = row.getResultByName("url");
|
||||
let uri = NetUtil.newURI(spec);
|
||||
let principal = gSecMan.getNoAppCodebasePrincipal(uri);
|
||||
let principal = gSecMan.createCodebasePrincipal(uri, {});
|
||||
|
||||
AboutPermissions.addPrincipal(principal);
|
||||
}
|
||||
|
@ -548,7 +548,7 @@ let AboutPermissions = {
|
|||
try {
|
||||
// aLogin.hostname is a string in origin URL format (e.g. "http://foo.com")
|
||||
let uri = NetUtil.newURI(aLogin.hostname);
|
||||
let principal = gSecMan.getNoAppCodebasePrincipal(uri);
|
||||
let principal = gSecMan.createCodebasePrincipal(uri, {});
|
||||
this.addPrincipal(principal);
|
||||
} catch (e) {
|
||||
// newURI will throw for add-ons logins stored in chrome:// URIs
|
||||
|
@ -564,7 +564,7 @@ let AboutPermissions = {
|
|||
try {
|
||||
// aHostname is a string in origin URL format (e.g. "http://foo.com")
|
||||
let uri = NetUtil.newURI(aHostname);
|
||||
let principal = gSecMan.getNoAppCodebasePrincipal(uri);
|
||||
let principal = gSecMan.createCodebasePrincipal(uri, {});
|
||||
this.addPrincipal(principal);
|
||||
} catch (e) {
|
||||
// newURI will throw for add-ons logins stored in chrome:// URIs
|
||||
|
|
|
@ -95,12 +95,12 @@ var gPermissionManager = {
|
|||
let uri;
|
||||
try {
|
||||
uri = Services.io.newURI(input_url, null, null);
|
||||
principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||
principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
||||
// If we have ended up with an unknown scheme, the following will throw.
|
||||
principal.origin;
|
||||
} catch(ex) {
|
||||
uri = Services.io.newURI("http://" + input_url, null, null);
|
||||
principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||
principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
||||
// If we have ended up with an unknown scheme, the following will throw.
|
||||
principal.origin;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,10 @@ const ABOUT_PERMISSIONS_SPEC = "about:permissions";
|
|||
const TEST_URI_1 = NetUtil.newURI("http://mozilla.com/");
|
||||
const TEST_URI_2 = NetUtil.newURI("http://mozilla.org/");
|
||||
|
||||
const TEST_PRINCIPAL_1 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_URI_1);
|
||||
const TEST_PRINCIPAL_2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_URI_2);
|
||||
const TEST_PRINCIPAL_1 =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(TEST_URI_1, {});
|
||||
const TEST_PRINCIPAL_2 =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(TEST_URI_2, {});
|
||||
|
||||
// values from DefaultPermissions object
|
||||
const PERM_UNKNOWN = 0;
|
||||
|
|
|
@ -15,9 +15,9 @@ const TEST_URI_XUL = TEST_URL_ROOT + "doc_content_stylesheet.xul";
|
|||
const XUL_URI = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(TEST_URI_XUL, null, null);
|
||||
const XUL_PRINCIPAL = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(XUL_URI);
|
||||
let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
const XUL_PRINCIPAL = ssm.createCodebasePrincipal(XUL_URI, {});
|
||||
|
||||
add_task(function*() {
|
||||
info("Checking stylesheets on HTML document");
|
||||
|
|
|
@ -995,14 +995,21 @@ PdfStreamConverter.prototype = {
|
|||
|
||||
// We can use resource principal when data is fetched by the chrome
|
||||
// e.g. useful for NoScript
|
||||
var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
var ssm = Cc['@mozilla.org/scriptsecuritymanager;1']
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
var uri = NetUtil.newURI(PDF_VIEWER_WEB_PAGE, null, null);
|
||||
// FF16 and below had getCodebasePrincipal, it was replaced by
|
||||
// getNoAppCodebasePrincipal (bug 758258).
|
||||
var resourcePrincipal = 'getNoAppCodebasePrincipal' in securityManager ?
|
||||
securityManager.getNoAppCodebasePrincipal(uri) :
|
||||
securityManager.getCodebasePrincipal(uri);
|
||||
// FF 43 added createCodebasePrincipal to replace getNoAppCodebasePrincipal
|
||||
// (bug 1165272).
|
||||
var resourcePrincipal
|
||||
if ('createCodebasePrincipal' in ssm) {
|
||||
resourcePrincipal = ssm.createCodebasePrincipal(uri, {});
|
||||
} else if ('getNoAppCodebasePrincipal' in ssm) {
|
||||
resourcePrincipal = ssm.getNoAppCodebasePrincipal(uri)
|
||||
} else {
|
||||
resourcePrincipal = ssm.getCodebasePrincipal(uri);
|
||||
}
|
||||
aRequest.owner = resourcePrincipal;
|
||||
channel.asyncOpen(proxy, aContext);
|
||||
},
|
||||
|
|
|
@ -22,9 +22,9 @@ var SpecialStorageUtils = {
|
|||
createWrappedSpecialStorage: function (sandbox, swfUrl, privateBrowsing) {
|
||||
// Creating internal localStorage object based on url and privateBrowsing setting.
|
||||
var uri = Services.io.newURI(swfUrl, null, null);
|
||||
var principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Components.interfaces.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri);
|
||||
var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Components.interfaces.nsIScriptSecurityManager);
|
||||
var principal = ssm.createCodebasePrincipal(uri, {});
|
||||
var dsm = Components.classes["@mozilla.org/dom/localStorage-manager;1"]
|
||||
.getService(Components.interfaces.nsIDOMStorageManager);
|
||||
var storage = dsm.createStorage(null, principal, privateBrowsing);
|
||||
|
|
|
@ -66,7 +66,8 @@ this.Feeds = {
|
|||
if (aIsFeed) {
|
||||
// re-create the principal as it may be a CPOW.
|
||||
let principalURI = BrowserUtils.makeURIFromCPOW(aPrincipal.URI);
|
||||
let principalToCheck = Services.scriptSecurityManager.getNoAppCodebasePrincipal(principalURI);
|
||||
let principalToCheck =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(principalURI, {});
|
||||
try {
|
||||
BrowserUtils.urlSecurityCheck(aLink.href, principalToCheck,
|
||||
Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
|
||||
|
|
|
@ -19,22 +19,6 @@ MOZ_ARG_WITH_BOOL(system-icu,
|
|||
if test -n "$MOZ_NATIVE_ICU"; then
|
||||
PKG_CHECK_MODULES(MOZ_ICU, icu-i18n >= 50.1)
|
||||
MOZ_SHARED_ICU=1
|
||||
elif test -n "$gonkdir" && test "$ANDROID_VERSION" -ge 18; then
|
||||
dnl Use system's ICU since version is 50.1+.
|
||||
if test -d "$gonkdir/external/icu/icu4c/source"; then
|
||||
dnl gonk-L (API version is 21)
|
||||
MOZ_ICU_GONK_PATH="$gonkdir/external/icu/icu4c/source"
|
||||
elif test -d "$gonkdir/external/icu4c"; then
|
||||
MOZ_ICU_GONK_PATH="$gonkdir/external/icu4c"
|
||||
else
|
||||
AC_MSG_ERROR([Cannot find ICU source code under gonk])
|
||||
fi
|
||||
MOZ_ICU_CFLAGS="-I$MOZ_ICU_GONK_PATH/common -I$MOZ_ICU_GONK_PATH/i18n"
|
||||
dnl icudata is a datafile under /usr/icu/icudt<version number>l.dat,
|
||||
dnl not shared library. So we don't link to icudata on B2G.
|
||||
MOZ_ICU_LIBS='-licui18n -licuuc'
|
||||
MOZ_NATIVE_ICU=1
|
||||
MOZ_SHARED_ICU=1
|
||||
else
|
||||
MOZ_ICU_CFLAGS='-I$(topsrcdir)/intl/icu/source/common -I$(topsrcdir)/intl/icu/source/i18n'
|
||||
AC_SUBST_LIST(MOZ_ICU_CFLAGS)
|
||||
|
|
|
@ -56,14 +56,15 @@ if __name__ == '__main__':
|
|||
parser.add_argument("--partial-mar-file", required=False,
|
||||
action="store", dest="partial_mar_file",
|
||||
help="Path to the partial MAR file, relative to the objdir.")
|
||||
parser.add_argument("--upload-properties", required=True,
|
||||
parser.add_argument("--upload-properties", required=False,
|
||||
action="store", dest="upload_properties",
|
||||
help="Path to the properties written by 'make upload'")
|
||||
args = parser.parse_args()
|
||||
|
||||
json_data = getMarProperties(args.complete_mar_file)
|
||||
with open(args.upload_properties) as f:
|
||||
json_data.update(json.load(f))
|
||||
if args.upload_properties:
|
||||
with open(args.upload_properties) as f:
|
||||
json_data.update(json.load(f))
|
||||
if args.partial_mar_file:
|
||||
json_data.update(getMarProperties(args.partial_mar_file, partial=True))
|
||||
|
||||
|
|
|
@ -17,8 +17,10 @@ include $(topsrcdir)/toolkit/mozapps/installer/upload-files.mk
|
|||
# Clear out DIST_FILES if it was set by upload-files.mk (for Android builds)
|
||||
DIST_FILES =
|
||||
|
||||
ifeq (1,$(MOZ_AUTOMATION_UPLOAD))
|
||||
# Properties from 'make upload' that are file URLs.
|
||||
AUTOMATION_UPLOAD_PROPERTIES = $(DIST)/upload-properties.json
|
||||
AUTOMATION_UPLOAD_PROPERTIES = --upload-properties $(DIST)/upload-properties.json
|
||||
endif
|
||||
|
||||
# Helper variables to convert from MOZ_AUTOMATION_* variables to the
|
||||
# corresponding the make target
|
||||
|
@ -98,7 +100,7 @@ automation/l10n-check: automation/pretty-l10n-check
|
|||
automation/update-packaging: automation/pretty-update-packaging
|
||||
|
||||
automation/build: $(addprefix automation/,$(MOZ_AUTOMATION_TIERS))
|
||||
$(PYTHON) $(topsrcdir)/build/gen_mach_buildprops.py --complete-mar-file $(DIST)/$(COMPLETE_MAR) $(addprefix --partial-mar-file ,$(wildcard $(DIST)/$(PARTIAL_MAR))) --upload-properties $(AUTOMATION_UPLOAD_PROPERTIES)
|
||||
$(PYTHON) $(topsrcdir)/build/gen_mach_buildprops.py --complete-mar-file $(DIST)/$(COMPLETE_MAR) $(addprefix --partial-mar-file ,$(wildcard $(DIST)/$(PARTIAL_MAR))) $(AUTOMATION_UPLOAD_PROPERTIES)
|
||||
|
||||
# Note: We have to force -j1 here, at least until bug 1036563 is fixed.
|
||||
AUTOMATION_EXTRA_CMDLINE-l10n-check = -j1
|
||||
|
|
|
@ -26,7 +26,7 @@ class DomainPolicyClone;
|
|||
[ptr] native JSObjectPtr(JSObject);
|
||||
[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
|
||||
|
||||
[scriptable, uuid(9a8f0b70-6b9f-4e19-8885-7cfe24f4a42d)]
|
||||
[scriptable, uuid(6e8a4d1e-d9c6-4d86-bf53-d73f58f36148)]
|
||||
interface nsIScriptSecurityManager : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -150,10 +150,12 @@ interface nsIScriptSecurityManager : nsISupports
|
|||
* @param appId is the app id of the principal. It can't be UNKNOWN_APP_ID.
|
||||
* @param inMozBrowser is true if the principal has to be considered as
|
||||
* inside a mozbrowser frame.
|
||||
*
|
||||
* @deprecated use createCodebasePrincipal instead.
|
||||
*/
|
||||
nsIPrincipal getAppCodebasePrincipal(in nsIURI uri,
|
||||
in unsigned long appId,
|
||||
in boolean inMozBrowser);
|
||||
[deprecated] nsIPrincipal getAppCodebasePrincipal(in nsIURI uri,
|
||||
in unsigned long appId,
|
||||
in boolean inMozBrowser);
|
||||
|
||||
/**
|
||||
* Returns a principal that has the appId and inMozBrowser of the load
|
||||
|
@ -175,8 +177,10 @@ interface nsIScriptSecurityManager : nsISupports
|
|||
* Returns a principal with that has the same origin as uri and is not part
|
||||
* of an appliction.
|
||||
* The returned principal will have appId = NO_APP_ID.
|
||||
*
|
||||
* @deprecated use createCodebasePrincipal instead.
|
||||
*/
|
||||
nsIPrincipal getNoAppCodebasePrincipal(in nsIURI uri);
|
||||
[deprecated] nsIPrincipal getNoAppCodebasePrincipal(in nsIURI uri);
|
||||
|
||||
/**
|
||||
* Legacy method for getting a principal with no origin attributes.
|
||||
|
|
|
@ -78,6 +78,7 @@ included_inclnames_to_ignore = set([
|
|||
'selfhosted.out.h', # generated in $OBJDIR
|
||||
'unicode/locid.h', # ICU
|
||||
'unicode/numsys.h', # ICU
|
||||
'unicode/timezone.h', # ICU
|
||||
'unicode/ucal.h', # ICU
|
||||
'unicode/uclean.h', # ICU
|
||||
'unicode/ucol.h', # ICU
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
@ -9360,9 +9361,6 @@ nsDocShell::CreatePrincipalFromReferrer(nsIURI* aReferrer,
|
|||
nsIPrincipal** aResult)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIScriptSecurityManager> secMan =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint32_t appId;
|
||||
rv = GetAppId(&appId);
|
||||
|
@ -9370,12 +9368,14 @@ nsDocShell::CreatePrincipalFromReferrer(nsIURI* aReferrer,
|
|||
bool isInBrowserElement;
|
||||
rv = GetIsInBrowserElement(&isInBrowserElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = secMan->GetAppCodebasePrincipal(aReferrer,
|
||||
appId,
|
||||
isInBrowserElement,
|
||||
aResult);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
|
||||
// TODO: Bug 1165466 - Pass mOriginAttributes directly.
|
||||
OriginAttributes attrs(appId, isInBrowserElement);
|
||||
nsCOMPtr<nsIPrincipal> prin =
|
||||
BasePrincipal::CreateCodebasePrincipal(aReferrer, attrs);
|
||||
prin.forget(aResult);
|
||||
|
||||
return *aResult ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -73,11 +73,9 @@ mozIApplication.prototype = {
|
|||
this._principal = null;
|
||||
|
||||
try {
|
||||
this._principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
|
||||
this._principal = Services.scriptSecurityManager.createCodebasePrincipal(
|
||||
Services.io.newURI(this.origin, null, null),
|
||||
this.localId,
|
||||
false /* mozbrowser */
|
||||
);
|
||||
{appId: this.localId});
|
||||
} catch(e) {
|
||||
dump("Could not create app principal " + e + "\n");
|
||||
}
|
||||
|
|
|
@ -228,8 +228,8 @@ function installCache(app) {
|
|||
if (!cacheManifest.exists())
|
||||
return;
|
||||
|
||||
let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
|
||||
app.origin, app.localId, false);
|
||||
let principal =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(app.origin, {appId: aApp.localId});
|
||||
|
||||
// If the build has been correctly configured, this should not happen!
|
||||
// If we install the cache anyway, it won't be updateable. If we don't install
|
||||
|
|
|
@ -40,7 +40,7 @@ this.ScriptPreloader = {
|
|||
let toLoad = aManifest.precompile.length;
|
||||
let principal =
|
||||
Services.scriptSecurityManager
|
||||
.getAppCodebasePrincipal(origin, aApp.localId, false);
|
||||
.createCodebasePrincipal(origin, {appId: aApp.localId});
|
||||
|
||||
aManifest.precompile.forEach((aPath) => {
|
||||
let uri = Services.io.newURI(aPath, null, origin);
|
||||
|
|
|
@ -820,8 +820,7 @@ this.DOMApplicationRegistry = {
|
|||
let uri = Services.io.newURI(aOrigin, null, null);
|
||||
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
let principal = secMan.getAppCodebasePrincipal(uri, aId,
|
||||
/*mozbrowser*/ false);
|
||||
let principal = secMan.createCodebasePrincipal(uri, {appId: aId});
|
||||
if (!dataStoreService.checkPermission(principal)) {
|
||||
return;
|
||||
}
|
||||
|
@ -3361,8 +3360,9 @@ this.DOMApplicationRegistry = {
|
|||
let requestChannel;
|
||||
|
||||
let appURI = NetUtil.newURI(aNewApp.origin, null, null);
|
||||
let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
|
||||
appURI, aNewApp.localId, false);
|
||||
let principal =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(appURI,
|
||||
{appId: aNewApp.localId});
|
||||
|
||||
if (aIsLocalFileInstall) {
|
||||
requestChannel = NetUtil.newChannel({
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* 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 "gfxPrefs.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/TextEventDispatcher.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
|
@ -284,8 +285,11 @@ TextInputProcessor::MaybeDispatchKeydownForComposition(
|
|||
return result;
|
||||
}
|
||||
|
||||
uint32_t consumedFlags = 0;
|
||||
|
||||
result.mResult = KeydownInternal(*aKeyboardEvent, aKeyFlags, false,
|
||||
result.mDoDefault);
|
||||
consumedFlags);
|
||||
result.mDoDefault = !consumedFlags;
|
||||
if (NS_WARN_IF(NS_FAILED(result.mResult))) {
|
||||
result.mCanContinue = false;
|
||||
return result;
|
||||
|
@ -754,9 +758,9 @@ NS_IMETHODIMP
|
|||
TextInputProcessor::Keydown(nsIDOMKeyEvent* aDOMKeyEvent,
|
||||
uint32_t aKeyFlags,
|
||||
uint8_t aOptionalArgc,
|
||||
bool* aDoDefault)
|
||||
uint32_t* aConsumedFlags)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aDoDefault, "aDoDefault must not be nullptr");
|
||||
MOZ_RELEASE_ASSERT(aConsumedFlags, "aConsumedFlags must not be nullptr");
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
if (!aOptionalArgc) {
|
||||
aKeyFlags = 0;
|
||||
|
@ -769,16 +773,33 @@ TextInputProcessor::Keydown(nsIDOMKeyEvent* aDOMKeyEvent,
|
|||
if (NS_WARN_IF(!originalKeyEvent)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aDoDefault);
|
||||
return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aConsumedFlags);
|
||||
}
|
||||
|
||||
TextEventDispatcher::DispatchTo
|
||||
TextInputProcessor::GetDispatchTo() const
|
||||
{
|
||||
// Support asynchronous tests.
|
||||
if (mForTests) {
|
||||
return gfxPrefs::TestEventsAsyncEnabled() ?
|
||||
TextEventDispatcher::eDispatchToParentProcess :
|
||||
TextEventDispatcher::eDispatchToCurrentProcess;
|
||||
}
|
||||
|
||||
// Otherwise, TextInputProcessor supports only keyboard apps on B2G.
|
||||
// Keyboard apps on B2G doesn't want to dispatch keyboard events to
|
||||
// chrome process. Therefore, this should dispatch key events only in
|
||||
// the current process.
|
||||
return TextEventDispatcher::eDispatchToCurrentProcess;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
uint32_t aKeyFlags,
|
||||
bool aAllowToDispatchKeypress,
|
||||
bool& aDoDefault)
|
||||
uint32_t& aConsumedFlags)
|
||||
{
|
||||
aDoDefault = false;
|
||||
aConsumedFlags = KEYEVENT_NOT_CONSUMED;
|
||||
|
||||
// We shouldn't modify the internal WidgetKeyboardEvent.
|
||||
WidgetKeyboardEvent keyEvent(aKeyboardEvent);
|
||||
|
@ -787,7 +808,8 @@ TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
|
|||
return rv;
|
||||
}
|
||||
|
||||
aDoDefault = !(aKeyFlags & KEY_DEFAULT_PREVENTED);
|
||||
aConsumedFlags = (aKeyFlags & KEY_DEFAULT_PREVENTED) ? KEYDOWN_IS_CONSUMED :
|
||||
KEYEVENT_NOT_CONSUMED;
|
||||
|
||||
if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
|
||||
ModifierKeyData modifierKeyData(keyEvent);
|
||||
|
@ -814,19 +836,27 @@ TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsEventStatus status = aDoDefault ? nsEventStatus_eIgnore :
|
||||
nsEventStatus_eConsumeNoDefault;
|
||||
if (!mDispatcher->DispatchKeyboardEvent(NS_KEY_DOWN, keyEvent, status)) {
|
||||
nsEventStatus status = aConsumedFlags ? nsEventStatus_eConsumeNoDefault :
|
||||
nsEventStatus_eIgnore;
|
||||
if (!mDispatcher->DispatchKeyboardEvent(NS_KEY_DOWN, keyEvent, status,
|
||||
GetDispatchTo())) {
|
||||
// If keydown event isn't dispatched, we don't need to dispatch keypress
|
||||
// events.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aAllowToDispatchKeypress) {
|
||||
mDispatcher->MaybeDispatchKeypressEvents(keyEvent, status);
|
||||
aConsumedFlags |=
|
||||
(status == nsEventStatus_eConsumeNoDefault) ? KEYDOWN_IS_CONSUMED :
|
||||
KEYEVENT_NOT_CONSUMED;
|
||||
|
||||
if (aAllowToDispatchKeypress &&
|
||||
mDispatcher->MaybeDispatchKeypressEvents(keyEvent, status,
|
||||
GetDispatchTo())) {
|
||||
aConsumedFlags |=
|
||||
(status == nsEventStatus_eConsumeNoDefault) ? KEYPRESS_IS_CONSUMED :
|
||||
KEYEVENT_NOT_CONSUMED;
|
||||
}
|
||||
|
||||
aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -890,7 +920,8 @@ TextInputProcessor::KeyupInternal(const WidgetKeyboardEvent& aKeyboardEvent,
|
|||
|
||||
nsEventStatus status = aDoDefault ? nsEventStatus_eIgnore :
|
||||
nsEventStatus_eConsumeNoDefault;
|
||||
mDispatcher->DispatchKeyboardEvent(NS_KEY_UP, keyEvent, status);
|
||||
mDispatcher->DispatchKeyboardEvent(NS_KEY_UP, keyEvent, status,
|
||||
GetDispatchTo());
|
||||
aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/TextEventDispatcher.h"
|
||||
#include "mozilla/TextEventDispatcherListener.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsITextInputProcessor.h"
|
||||
|
@ -17,10 +18,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
namespace widget{
|
||||
class TextEventDispatcher;
|
||||
} // namespace widget
|
||||
|
||||
class TextInputProcessor final : public nsITextInputProcessor
|
||||
, public widget::TextEventDispatcherListener
|
||||
{
|
||||
|
@ -60,10 +57,11 @@ private:
|
|||
nsresult KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
uint32_t aKeyFlags,
|
||||
bool aAllowToDispatchKeypress,
|
||||
bool& aDoDefault);
|
||||
uint32_t& aConsumedFlags);
|
||||
nsresult KeyupInternal(const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
uint32_t aKeyFlags,
|
||||
bool& aDoDefault);
|
||||
TextEventDispatcher::DispatchTo GetDispatchTo() const;
|
||||
nsresult IsValidStateForComposition();
|
||||
void UnlinkFromTextEventDispatcher();
|
||||
nsresult PrepareKeyboardEventToDispatch(WidgetKeyboardEvent& aKeyboardEvent,
|
||||
|
|
|
@ -7904,8 +7904,12 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
|
|||
nsPIDOMWindow* win = GetWindow();
|
||||
if (win && win->IsDesktopModeViewport())
|
||||
{
|
||||
return nsViewportInfo(aDisplaySize,
|
||||
defaultScale,
|
||||
float viewportWidth = gfxPrefs::DesktopViewportWidth() / fullZoom;
|
||||
float scaleToFit = aDisplaySize.width / viewportWidth;
|
||||
float aspectRatio = (float)aDisplaySize.height / aDisplaySize.width;
|
||||
ScreenSize viewportSize(viewportWidth, viewportWidth * aspectRatio);
|
||||
return nsViewportInfo(RoundedToInt(viewportSize),
|
||||
CSSToScreenScale(scaleToFit),
|
||||
/*allowZoom*/false,
|
||||
/*allowDoubleTapZoom*/ false);
|
||||
}
|
||||
|
@ -8060,8 +8064,7 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
|
|||
// Stretch CSS pixel size of viewport to keep device pixel size
|
||||
// unchanged after full zoom applied.
|
||||
// See bug 974242.
|
||||
size.width = Preferences::GetInt("browser.viewport.desktopWidth",
|
||||
kViewportDefaultScreenWidth) / fullZoom;
|
||||
size.width = gfxPrefs::DesktopViewportWidth() / fullZoom;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,6 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include "nsILoadContext.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsView.h"
|
||||
#include "nsViewManager.h"
|
||||
|
@ -192,6 +191,7 @@
|
|||
#include "nsRefreshDriver.h"
|
||||
|
||||
#include "mozilla/AddonPathService.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsLocation.h"
|
||||
|
@ -256,6 +256,8 @@ static const char kStorageEnabled[] = "dom.storage.enabled";
|
|||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::ipc;
|
||||
using mozilla::BasePrincipal;
|
||||
using mozilla::OriginAttributes;
|
||||
using mozilla::TimeStamp;
|
||||
using mozilla::TimeDuration;
|
||||
using mozilla::dom::cache::CacheStorage;
|
||||
|
@ -6524,7 +6526,15 @@ nsGlobalWindow::SetFullscreenInternal(FullscreenReason aReason,
|
|||
// consequential calls to this method, those calls will be skipped
|
||||
// at the condition above.
|
||||
if (aReason == eForFullscreenMode) {
|
||||
mFullscreenMode = aFullScreen;
|
||||
if (!aFullScreen && !mFullscreenMode) {
|
||||
// If we are exiting fullscreen mode, but we actually didn't
|
||||
// entered fullscreen mode, the fullscreen state was only for
|
||||
// the Fullscreen API. Change the reason here so that we can
|
||||
// perform transition for it.
|
||||
aReason = eForFullscreenAPI;
|
||||
} else {
|
||||
mFullscreenMode = aFullScreen;
|
||||
}
|
||||
} else {
|
||||
// If we are exiting from DOM fullscreen while we initially make
|
||||
// the window fullscreen because of fullscreen mode, don't restore
|
||||
|
@ -8579,21 +8589,14 @@ nsGlobalWindow::PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessa
|
|||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> ssm =
|
||||
nsContentUtils::GetSecurityManager();
|
||||
MOZ_ASSERT(ssm);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipal();
|
||||
MOZ_ASSERT(principal);
|
||||
|
||||
uint32_t appId = principal->GetAppId();
|
||||
bool isInBrowser = principal->GetIsInBrowserElement();
|
||||
|
||||
OriginAttributes attrs = BasePrincipal::Cast(principal)->OriginAttributesRef();
|
||||
// Create a nsIPrincipal inheriting the app/browser attributes from the
|
||||
// caller.
|
||||
nsresult rv = ssm->GetAppCodebasePrincipal(originURI, appId, isInBrowser,
|
||||
getter_AddRefs(providedPrincipal));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
providedPrincipal = BasePrincipal::CreateCodebasePrincipal(originURI, attrs);
|
||||
if (NS_WARN_IF(!providedPrincipal)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -307,6 +307,12 @@ nsJSUtils::GetScopeChainForElement(JSContext* aCx,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsJSUtils::ResetTimeZone()
|
||||
{
|
||||
JS::ResetTimeZone();
|
||||
}
|
||||
|
||||
//
|
||||
// nsDOMJSUtils.h
|
||||
|
|
|
@ -113,6 +113,9 @@ public:
|
|||
static bool GetScopeChainForElement(JSContext* aCx,
|
||||
mozilla::dom::Element* aElement,
|
||||
JS::AutoObjectVector& aScopeChain);
|
||||
|
||||
static void ResetTimeZone();
|
||||
|
||||
private:
|
||||
// Implementation for our EvaluateString bits
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
|
|
|
@ -16,7 +16,6 @@ static const mozilla::LayoutDeviceToScreenScale kViewportMinScale(0.0f);
|
|||
static const mozilla::LayoutDeviceToScreenScale kViewportMaxScale(10.0f);
|
||||
static const mozilla::CSSIntSize kViewportMinSize(200, 40);
|
||||
static const mozilla::CSSIntSize kViewportMaxSize(10000, 10000);
|
||||
static const int32_t kViewportDefaultScreenWidth = 980;
|
||||
|
||||
/**
|
||||
* Information retrieved from the <meta name="viewport"> tag. See
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
src="data:text/html,<textarea id='textarea' cols='20' rows='4'></textarea>"></iframe><br/>
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
@ -2340,8 +2340,8 @@ function runKeyTests()
|
|||
reset();
|
||||
var doDefaultKeydown = TIP.keydown(keyA);
|
||||
|
||||
ok(!doDefaultKeydown,
|
||||
description + "TIP.keydown(keyA) should return false because the keypress event should be consumed by the input element");
|
||||
is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED,
|
||||
description + "TIP.keydown(keyA) should return 0x02 because the keypress event should be consumed by the input element");
|
||||
is(events.length, 2,
|
||||
description + "TIP.keydown(keyA) should cause keydown and keypress event");
|
||||
checkKeyAttrs("TIP.keydown(keyA)", events[0],
|
||||
|
@ -2371,8 +2371,8 @@ function runKeyTests()
|
|||
reset();
|
||||
doDefaultKeydown = TIP.keydown(keyEnter);
|
||||
|
||||
ok(doDefaultKeydown,
|
||||
description + "TIP.keydown(keyEnter) should return true");
|
||||
is(doDefaultKeydown, 0,
|
||||
description + "TIP.keydown(keyEnter) should return 0");
|
||||
is(events.length, 2,
|
||||
description + "TIP.keydown(keyEnter) should cause keydown and keypress event");
|
||||
checkKeyAttrs("TIP.keydown(keyEnter)", events[0],
|
||||
|
@ -2402,8 +2402,8 @@ function runKeyTests()
|
|||
doDefaultKeydown = TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED);
|
||||
doDefaultKeyup = TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED);
|
||||
|
||||
ok(!doDefaultKeydown,
|
||||
description + "TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) should return false because it's marked as consumed at dispatching the event");
|
||||
is(doDefaultKeydown, TIP.KEYDOWN_IS_CONSUMED,
|
||||
description + "TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) should return 0x01 because it's marked as consumed at dispatching the event");
|
||||
ok(!doDefaultKeyup,
|
||||
description + "TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED) should return false because it's marked as consumed at dispatching the event");
|
||||
is(events.length, 2,
|
||||
|
@ -2423,7 +2423,7 @@ function runKeyTests()
|
|||
doDefaultKeydown = TIP.keydown(keyABC);
|
||||
doDefaultKeyup = TIP.keyup(keyABC);
|
||||
|
||||
ok(!doDefaultKeydown,
|
||||
is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED,
|
||||
description + "TIP.keydown(keyABC) should return false because the keypress events should be consumed by the input element");
|
||||
ok(doDefaultKeyup,
|
||||
description + "TIP.keyup(keyABC) should return true");
|
||||
|
@ -2450,8 +2450,8 @@ function runKeyTests()
|
|||
doDefaultKeydown = TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY);
|
||||
doDefaultKeyup = TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY);
|
||||
|
||||
ok(!doDefaultKeydown,
|
||||
description + "TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) should return false because the keypress events should be consumed by the input element");
|
||||
is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED,
|
||||
description + "TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) should return 0x02 because the keypress events should be consumed by the input element");
|
||||
ok(doDefaultKeyup,
|
||||
description + "TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) should return true");
|
||||
is(events.length, 7,
|
||||
|
@ -2480,8 +2480,8 @@ function runKeyTests()
|
|||
doDefaultKeydown = TIP.keydown(keyWithModifiers);
|
||||
doDefaultKeyup = TIP.keyup(keyWithModifiers);
|
||||
|
||||
ok(doDefaultKeydown,
|
||||
description + "TIP.keydown(keyWithModifiers) should return true");
|
||||
is(doDefaultKeydown, 0,
|
||||
description + "TIP.keydown(keyWithModifiers) should return 0");
|
||||
ok(doDefaultKeyup,
|
||||
description + "TIP.keyup(keyWithModifiers) should return true");
|
||||
is(events.length, 3,
|
||||
|
@ -2502,8 +2502,8 @@ function runKeyTests()
|
|||
doDefaultKeydown = TIP.keydown(keyA);
|
||||
doDefaultKeyup = TIP.keyup(keyA);
|
||||
|
||||
ok(!doDefaultKeydown,
|
||||
description + "TIP.keydown(keyA) should return false because keydown event's preventDefault should be called");
|
||||
is(doDefaultKeydown, TIP.KEYDOWN_IS_CONSUMED,
|
||||
description + "TIP.keydown(keyA) should return 0x01 because keydown event's preventDefault should be called");
|
||||
ok(doDefaultKeyup,
|
||||
description + "TIP.keyup(keyA) should return true");
|
||||
is(events.length, 2,
|
||||
|
@ -2521,8 +2521,8 @@ function runKeyTests()
|
|||
doDefaultKeydown = TIP.keydown(keyA);
|
||||
doDefaultKeyup = TIP.keyup(keyA);
|
||||
|
||||
ok(!doDefaultKeydown,
|
||||
description + "TIP.keydown(keyA) should return false because keypress event's preventDefault should be called");
|
||||
is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED,
|
||||
description + "TIP.keydown(keyA) should return 0x02 because keypress event's preventDefault should be called");
|
||||
ok(doDefaultKeyup,
|
||||
description + "TIP.keyup(keyA) should return true");
|
||||
is(events.length, 3,
|
||||
|
@ -2543,8 +2543,8 @@ function runKeyTests()
|
|||
doDefaultKeydown = TIP.keydown(keyA);
|
||||
doDefaultKeyup = TIP.keyup(keyA);
|
||||
|
||||
ok(!doDefaultKeydown,
|
||||
description + "TIP.keydown(keyA) should return false because the key event should be consumed by the input element");
|
||||
is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED,
|
||||
description + "TIP.keydown(keyA) should return 0x02 because the key event should be consumed by the input element");
|
||||
ok(!doDefaultKeyup,
|
||||
description + "TIP.keyup(keyA) should return false because keyup event's preventDefault should be called");
|
||||
is(events.length, 3,
|
||||
|
|
|
@ -833,14 +833,16 @@ BrowserElementParent.prototype = {
|
|||
catch(e) {
|
||||
debug('Malformed referrer -- ' + e);
|
||||
}
|
||||
|
||||
// TODO Bug 1165466: use originAttributes from nsILoadContext.
|
||||
let attrs = {appId: this._frameLoader.loadContext.appId,
|
||||
inBrowser: this._frameLoader.loadContext.isInBrowserElement};
|
||||
// This simply returns null if there is no principal available
|
||||
// for the requested uri. This is an acceptable fallback when
|
||||
// calling newChannelFromURI2.
|
||||
principal =
|
||||
Services.scriptSecurityManager.getAppCodebasePrincipal(
|
||||
referrer,
|
||||
this._frameLoader.loadContext.appId,
|
||||
this._frameLoader.loadContext.isInBrowserElement);
|
||||
principal =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(
|
||||
referrer, attrs);
|
||||
}
|
||||
|
||||
debug('Using principal? ' + !!principal);
|
||||
|
|
|
@ -158,15 +158,17 @@ function testAuthJarNoInterfere(e) {
|
|||
|
||||
// Set a bunch of auth data that should not conflict with the correct auth data already
|
||||
// stored in the cache.
|
||||
var principal = secMan.getAppCodebasePrincipal(uri, 1, false);
|
||||
var attrs = {appId: 1};
|
||||
var principal = secMan.createCodebasePrincipal(uri, attrs);
|
||||
authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
|
||||
'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
|
||||
'', 'httpuser', 'wrongpass', false, principal);
|
||||
principal = secMan.getAppCodebasePrincipal(uri, 1, true);
|
||||
attrs = {appId: 1, inBrowser: true};
|
||||
principal = secMan.createCodebasePrincipal(uri, attrs);
|
||||
authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
|
||||
'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
|
||||
'', 'httpuser', 'wrongpass', false, principal);
|
||||
principal = secMan.getAppCodebasePrincipal(uri, secMan.NO_APP_ID, false);
|
||||
principal = secMan.createCodebasePrincipal(uri, {});
|
||||
authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
|
||||
'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
|
||||
'', 'httpuser', 'wrongpass', false, principal);
|
||||
|
@ -196,7 +198,7 @@ function testAuthJarInterfere(e) {
|
|||
var uri = ioService.newURI("http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs", null, null);
|
||||
|
||||
// Set some auth data that should overwrite the successful stored details.
|
||||
var principal = secMan.getAppCodebasePrincipal(uri, secMan.NO_APP_ID, true);
|
||||
var principal = secMan.createCodebasePrincipal(uri, {inBrowser: true});
|
||||
authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
|
||||
'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
|
||||
'', 'httpuser', 'wrongpass', false, principal);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
// attach to a different origin's CacheStorage
|
||||
var url = 'http://example.com/';
|
||||
var uri = Services.io.newURI(url, null, null);
|
||||
var principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||
var principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
||||
var storage = new CacheStorage('content', principal);
|
||||
|
||||
// verify we can use the other origin's CacheStorage as normal
|
||||
|
|
|
@ -1728,7 +1728,11 @@ CanvasRenderingContext2D::Scale(double x, double y, ErrorResult& error)
|
|||
}
|
||||
|
||||
Matrix newMatrix = mTarget->GetTransform();
|
||||
mTarget->SetTransform(newMatrix.PreScale(x, y));
|
||||
newMatrix.PreScale(x, y);
|
||||
if (!newMatrix.IsFinite()) {
|
||||
return;
|
||||
}
|
||||
mTarget->SetTransform(newMatrix);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1740,8 +1744,11 @@ CanvasRenderingContext2D::Rotate(double angle, ErrorResult& error)
|
|||
return;
|
||||
}
|
||||
|
||||
Matrix rotation = Matrix::Rotation(angle);
|
||||
mTarget->SetTransform(rotation * mTarget->GetTransform());
|
||||
Matrix newMatrix = Matrix::Rotation(angle) * mTarget->GetTransform();
|
||||
if (!newMatrix.IsFinite()) {
|
||||
return;
|
||||
}
|
||||
mTarget->SetTransform(newMatrix);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1753,7 +1760,12 @@ CanvasRenderingContext2D::Translate(double x, double y, ErrorResult& error)
|
|||
return;
|
||||
}
|
||||
|
||||
mTarget->SetTransform(Matrix(mTarget->GetTransform()).PreTranslate(x, y));
|
||||
Matrix newMatrix = mTarget->GetTransform();
|
||||
newMatrix.PreTranslate(x, y);
|
||||
if (!newMatrix.IsFinite()) {
|
||||
return;
|
||||
}
|
||||
mTarget->SetTransform(newMatrix);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1767,8 +1779,12 @@ CanvasRenderingContext2D::Transform(double m11, double m12, double m21,
|
|||
return;
|
||||
}
|
||||
|
||||
Matrix matrix(m11, m12, m21, m22, dx, dy);
|
||||
mTarget->SetTransform(matrix * mTarget->GetTransform());
|
||||
Matrix newMatrix(m11, m12, m21, m22, dx, dy);
|
||||
newMatrix *= mTarget->GetTransform();
|
||||
if (!newMatrix.IsFinite()) {
|
||||
return;
|
||||
}
|
||||
mTarget->SetTransform(newMatrix);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1784,6 +1800,9 @@ CanvasRenderingContext2D::SetTransform(double m11, double m12,
|
|||
}
|
||||
|
||||
Matrix matrix(m11, m12, m21, m22, dx, dy);
|
||||
if (!matrix.IsFinite()) {
|
||||
return;
|
||||
}
|
||||
mTarget->SetTransform(matrix);
|
||||
}
|
||||
|
||||
|
|
|
@ -209,12 +209,23 @@ StartsWith(const std::string& haystack, const char (&needle)[N])
|
|||
bool
|
||||
ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const
|
||||
{
|
||||
{
|
||||
const std::vector<sh::Uniform>& vertList = *ShGetUniforms(prev->mHandle);
|
||||
const std::vector<sh::Uniform>& fragList = *ShGetUniforms(mHandle);
|
||||
if (!prev) {
|
||||
nsPrintfCString error("Passed in NULL prev ShaderValidator.");
|
||||
*out_log = error;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto itrFrag = fragList.begin(); itrFrag != fragList.end(); ++itrFrag) {
|
||||
for (auto itrVert = vertList.begin(); itrVert != vertList.end(); ++itrVert) {
|
||||
{
|
||||
const std::vector<sh::Uniform>* vertPtr = ShGetUniforms(prev->mHandle);
|
||||
const std::vector<sh::Uniform>* fragPtr = ShGetUniforms(mHandle);
|
||||
if (!vertPtr || !fragPtr) {
|
||||
nsPrintfCString error("Could not create uniform list.");
|
||||
*out_log = error;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto itrFrag = fragPtr->begin(); itrFrag != fragPtr->end(); ++itrFrag) {
|
||||
for (auto itrVert = vertPtr->begin(); itrVert != vertPtr->end(); ++itrVert) {
|
||||
if (itrVert->name != itrFrag->name)
|
||||
continue;
|
||||
|
||||
|
@ -231,12 +242,17 @@ ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log
|
|||
}
|
||||
}
|
||||
{
|
||||
const std::vector<sh::Varying>& vertList = *ShGetVaryings(prev->mHandle);
|
||||
const std::vector<sh::Varying>& fragList = *ShGetVaryings(mHandle);
|
||||
const std::vector<sh::Varying>* vertPtr = ShGetVaryings(prev->mHandle);
|
||||
const std::vector<sh::Varying>* fragPtr = ShGetVaryings(mHandle);
|
||||
if (!vertPtr || !fragPtr) {
|
||||
nsPrintfCString error("Could not create varying list.");
|
||||
*out_log = error;
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<ShVariableInfo> staticUseVaryingList;
|
||||
|
||||
for (auto itrFrag = fragList.begin(); itrFrag != fragList.end(); ++itrFrag) {
|
||||
for (auto itrFrag = fragPtr->begin(); itrFrag != fragPtr->end(); ++itrFrag) {
|
||||
const ShVariableInfo varInfo = { itrFrag->type,
|
||||
(int)itrFrag->elementCount() };
|
||||
|
||||
|
@ -251,7 +267,7 @@ ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log
|
|||
bool definedInVertShader = false;
|
||||
bool staticVertUse = false;
|
||||
|
||||
for (auto itrVert = vertList.begin(); itrVert != vertList.end(); ++itrVert) {
|
||||
for (auto itrVert = vertPtr->begin(); itrVert != vertPtr->end(); ++itrVert) {
|
||||
if (itrVert->name != itrFrag->name)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
|
||||
function boom()
|
||||
{
|
||||
var canvas = document.createElement('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.rotate(1e308);
|
||||
ctx.fillText("A", 1, 1);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();"></body>
|
||||
</html>
|
|
@ -23,3 +23,5 @@ load 916128-1.html
|
|||
load 934939-1.html
|
||||
load 1099143-1.html
|
||||
load 1183363.html
|
||||
load 1190705.html
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/dom/DataStoreImplBinding.h"
|
||||
#include "nsIDataStore.h"
|
||||
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
@ -56,6 +57,9 @@
|
|||
return NS_ERROR_FAILURE; \
|
||||
}
|
||||
|
||||
using mozilla::BasePrincipal;
|
||||
using mozilla::OriginAttributes;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -213,17 +217,10 @@ ResetPermission(uint32_t aAppId, const nsAString& aOriginURL,
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
if (!ssm) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = ssm->GetAppCodebasePrincipal(uri, aAppId, false,
|
||||
getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
OriginAttributes attrs(aAppId, false);
|
||||
nsCOMPtr<nsIPrincipal> principal =
|
||||
BasePrincipal::CreateCodebasePrincipal(uri, attrs);
|
||||
NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> pm =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
|
|
|
@ -18519,12 +18519,6 @@ FactoryOp::CheckAtLeastOneAppHasPermission(ContentParent* aContentParent,
|
|||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> secMan =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
if (NS_WARN_IF(!secMan)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMan =
|
||||
mozilla::services::GetPermissionManager();
|
||||
if (NS_WARN_IF(!permMan)) {
|
||||
|
@ -18548,24 +18542,9 @@ FactoryOp::CheckAtLeastOneAppHasPermission(ContentParent* aContentParent,
|
|||
return false;
|
||||
}
|
||||
|
||||
nsString origin;
|
||||
rv = app->GetOrigin(origin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), origin, nullptr, nullptr, ioService);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = secMan->GetAppCodebasePrincipal(uri, appId, false,
|
||||
getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
app->GetPrincipal(getter_AddRefs(principal));
|
||||
NS_ENSURE_TRUE(principal, false);
|
||||
|
||||
uint32_t permission;
|
||||
rv = permMan->TestExactPermissionFromPrincipal(principal,
|
||||
|
|
|
@ -116,9 +116,9 @@ function setPermission(url, permission)
|
|||
let uri = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService)
|
||||
.newURI(url, null, null);
|
||||
let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri);
|
||||
let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
let principal = ssm.createCodebasePrincipal(uri, {});
|
||||
|
||||
Components.classes["@mozilla.org/permissionmanager;1"]
|
||||
.getService(nsIPermissionManager)
|
||||
|
@ -131,9 +131,9 @@ function removePermission(url, permission)
|
|||
let uri = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService)
|
||||
.newURI(url, null, null);
|
||||
let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri);
|
||||
let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
let principal = ssm.createCodebasePrincipal(uri, {});
|
||||
|
||||
Components.classes["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Components.interfaces.nsIPermissionManager)
|
||||
|
@ -145,9 +145,9 @@ function getPermission(url, permission)
|
|||
let uri = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService)
|
||||
.newURI(url, null, null);
|
||||
let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri);
|
||||
let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
let principal = ssm.createCodebasePrincipal(uri, {});
|
||||
|
||||
return Components.classes["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Components.interfaces.nsIPermissionManager)
|
||||
|
|
|
@ -90,13 +90,10 @@ function testSteps()
|
|||
let request;
|
||||
if ("url" in params) {
|
||||
let uri = ios.newURI(params.url, null, null);
|
||||
let principal;
|
||||
if ("appId" in params) {
|
||||
principal = ssm.getAppCodebasePrincipal(uri, params.appId,
|
||||
params.inMozBrowser);
|
||||
} else {
|
||||
principal = ssm.getNoAppCodebasePrincipal(uri);
|
||||
}
|
||||
let principal =
|
||||
ssm.createCodebasePrincipal(uri,
|
||||
{appId: params.appId || ssm.NO_APPID,
|
||||
inBrowser: params.inMozBrowser});
|
||||
if ("dbVersion" in params) {
|
||||
request = indexedDB.openForPrincipal(principal, params.dbName,
|
||||
params.dbVersion);
|
||||
|
|
|
@ -10,9 +10,9 @@ function testSteps()
|
|||
let uri = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService).
|
||||
newURI("https://www.example.com", null, null);
|
||||
let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
|
||||
getService(Ci.nsIScriptSecurityManager).
|
||||
getNoAppCodebasePrincipal(uri);
|
||||
let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
let principal = ssm.createCodebasePrincipal(uri, {});
|
||||
|
||||
info("Setting permissions");
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ function testSteps()
|
|||
let request;
|
||||
if ("url" in params) {
|
||||
let uri = ios.newURI(params.url, null, null);
|
||||
let principal = ssm.getNoAppCodebasePrincipal(uri);
|
||||
let principal = ssm.createCodebasePrincipal(uri, {});
|
||||
request = indexedDB.openForPrincipal(principal, params.dbName,
|
||||
params.dbOptions);
|
||||
} else {
|
||||
|
|
|
@ -48,9 +48,9 @@ function testSteps()
|
|||
let uri = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService)
|
||||
.newURI("http://appdata.example.com", null, null);
|
||||
let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Components.interfaces.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri);
|
||||
let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Components.interfaces.nsIScriptSecurityManager);
|
||||
let principal = ssm.createCodebasePrincipal(uri, {});
|
||||
|
||||
request = indexedDB.openForPrincipal(principal, name, 1);
|
||||
request.onerror = errorHandler;
|
||||
|
|
|
@ -34,9 +34,9 @@ function testSteps()
|
|||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(url, null, null);
|
||||
return Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri);
|
||||
let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
return ssm.createCodebasePrincipal(uri, {});
|
||||
}
|
||||
|
||||
for (let temporary of [true, false]) {
|
||||
|
|
|
@ -381,7 +381,7 @@ MozInputMethod.prototype = {
|
|||
},
|
||||
|
||||
addInput: function(inputId, inputManifest) {
|
||||
return this._sendPromise(function(resolverId) {
|
||||
return this.createPromiseWithId(function(resolverId) {
|
||||
let appId = this._window.document.nodePrincipal.appId;
|
||||
|
||||
cpmm.sendAsyncMessage('InputRegistry:Add', {
|
||||
|
@ -573,7 +573,7 @@ MozInputContext.prototype = {
|
|||
|
||||
switch (msg.name) {
|
||||
case "Keyboard:SendKey:Result:OK":
|
||||
resolver.resolve();
|
||||
resolver.resolve(true);
|
||||
break;
|
||||
case "Keyboard:SendKey:Result:Error":
|
||||
resolver.reject(json.error);
|
||||
|
@ -596,7 +596,7 @@ MozInputContext.prototype = {
|
|||
break;
|
||||
case "Keyboard:SetComposition:Result:OK": // Fall through.
|
||||
case "Keyboard:EndComposition:Result:OK":
|
||||
resolver.resolve();
|
||||
resolver.resolve(true);
|
||||
break;
|
||||
default:
|
||||
dump("Could not find a handler for " + msg.name);
|
||||
|
@ -738,40 +738,79 @@ MozInputContext.prototype = {
|
|||
return this.replaceSurroundingText(null, offset, length);
|
||||
},
|
||||
|
||||
sendKey: function ic_sendKey(keyCode, charCode, modifiers, repeat) {
|
||||
let self = this;
|
||||
return this._sendPromise(function(resolverId) {
|
||||
cpmmSendAsyncMessageWithKbID(self, 'Keyboard:SendKey', {
|
||||
contextId: self._contextId,
|
||||
sendKey: function ic_sendKey(dictOrKeyCode, charCode, modifiers, repeat) {
|
||||
if (typeof dictOrKeyCode === 'number') {
|
||||
// XXX: modifiers are ignored in this API method.
|
||||
|
||||
return this._sendPromise((resolverId) => {
|
||||
cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
|
||||
contextId: this._contextId,
|
||||
requestId: resolverId,
|
||||
method: 'sendKey',
|
||||
keyCode: dictOrKeyCode,
|
||||
charCode: charCode,
|
||||
repeat: repeat
|
||||
});
|
||||
});
|
||||
} else if (typeof dictOrKeyCode === 'object') {
|
||||
return this._sendPromise((resolverId) => {
|
||||
cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
|
||||
contextId: this._contextId,
|
||||
requestId: resolverId,
|
||||
method: 'sendKey',
|
||||
keyboardEventDict: this._getkeyboardEventDict(dictOrKeyCode)
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// XXX: Should not reach here; implies WebIDL binding error.
|
||||
throw new TypeError('Unknown argument passed.');
|
||||
}
|
||||
},
|
||||
|
||||
keydown: function ic_keydown(dict) {
|
||||
return this._sendPromise((resolverId) => {
|
||||
cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
|
||||
contextId: this._contextId,
|
||||
requestId: resolverId,
|
||||
method: 'keydown',
|
||||
keyboardEventDict: this._getkeyboardEventDict(dict)
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
keyup: function ic_keyup(dict) {
|
||||
return this._sendPromise((resolverId) => {
|
||||
cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
|
||||
contextId: this._contextId,
|
||||
requestId: resolverId,
|
||||
keyCode: keyCode,
|
||||
charCode: charCode,
|
||||
modifiers: modifiers,
|
||||
repeat: repeat
|
||||
method: 'keyup',
|
||||
keyboardEventDict: this._getkeyboardEventDict(dict)
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
setComposition: function ic_setComposition(text, cursor, clauses) {
|
||||
setComposition: function ic_setComposition(text, cursor, clauses, dict) {
|
||||
let self = this;
|
||||
return this._sendPromise(function(resolverId) {
|
||||
return this._sendPromise((resolverId) => {
|
||||
cpmmSendAsyncMessageWithKbID(self, 'Keyboard:SetComposition', {
|
||||
contextId: self._contextId,
|
||||
requestId: resolverId,
|
||||
text: text,
|
||||
cursor: (typeof cursor !== 'undefined') ? cursor : text.length,
|
||||
clauses: clauses || null
|
||||
clauses: clauses || null,
|
||||
keyboardEventDict: this._getkeyboardEventDict(dict)
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
endComposition: function ic_endComposition(text) {
|
||||
endComposition: function ic_endComposition(text, dict) {
|
||||
let self = this;
|
||||
return this._sendPromise(function(resolverId) {
|
||||
return this._sendPromise((resolverId) => {
|
||||
cpmmSendAsyncMessageWithKbID(self, 'Keyboard:EndComposition', {
|
||||
contextId: self._contextId,
|
||||
requestId: resolverId,
|
||||
text: text || ''
|
||||
text: text || '',
|
||||
keyboardEventDict: this._getkeyboardEventDict(dict)
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -786,6 +825,43 @@ MozInputContext.prototype = {
|
|||
}
|
||||
callback(aResolverId);
|
||||
});
|
||||
},
|
||||
|
||||
// Take a MozInputMethodKeyboardEventDict dict, creates a keyboardEventDict
|
||||
// object that can be sent to forms.js
|
||||
_getkeyboardEventDict: function(dict) {
|
||||
if (typeof dict !== 'object' || !dict.key) {
|
||||
return;
|
||||
}
|
||||
|
||||
var keyboardEventDict = {
|
||||
key: dict.key,
|
||||
code: dict.code,
|
||||
repeat: dict.repeat,
|
||||
flags: 0
|
||||
};
|
||||
|
||||
if (dict.printable) {
|
||||
keyboardEventDict.flags |=
|
||||
Ci.nsITextInputProcessor.KEY_FORCE_PRINTABLE_KEY;
|
||||
}
|
||||
|
||||
if (/^[a-zA-Z0-9]$/.test(dict.key)) {
|
||||
// keyCode must follow the key value in this range;
|
||||
// disregard the keyCode from content.
|
||||
keyboardEventDict.keyCode = dict.key.toUpperCase().charCodeAt(0);
|
||||
} else if (typeof dict.keyCode === 'number') {
|
||||
// Allow keyCode to be specified for other key values.
|
||||
keyboardEventDict.keyCode = dict.keyCode;
|
||||
|
||||
// Allow keyCode to be explicitly set to zero.
|
||||
if (dict.keyCode === 0) {
|
||||
keyboardEventDict.flags |=
|
||||
Ci.nsITextInputProcessor.KEY_KEEP_KEYCODE_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
return keyboardEventDict;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ dump("###################################### forms.js loaded\n");
|
|||
let Ci = Components.interfaces;
|
||||
let Cc = Components.classes;
|
||||
let Cu = Components.utils;
|
||||
let Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
@ -18,10 +19,50 @@ XPCOMUtils.defineLazyServiceGetter(Services, "fm",
|
|||
"@mozilla.org/focus-manager;1",
|
||||
"nsIFocusManager");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "domWindowUtils", function () {
|
||||
return content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
});
|
||||
/*
|
||||
* A WeakMap to map window to objects keeping it's TextInputProcessor instance.
|
||||
*/
|
||||
let WindowMap = {
|
||||
// WeakMap of <window, object> pairs.
|
||||
_map: null,
|
||||
|
||||
/*
|
||||
* Set the object associated to the window and return it.
|
||||
*/
|
||||
_getObjForWin: function(win) {
|
||||
if (!this._map) {
|
||||
this._map = new WeakMap();
|
||||
}
|
||||
if (this._map.has(win)) {
|
||||
return this._map.get(win);
|
||||
} else {
|
||||
let obj = {
|
||||
tip: null
|
||||
};
|
||||
this._map.set(win, obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
},
|
||||
|
||||
getTextInputProcessor: function(win) {
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
let obj = this._getObjForWin(win);
|
||||
let tip = obj.tip
|
||||
|
||||
if (!tip) {
|
||||
tip = obj.tip = Cc["@mozilla.org/text-input-processor;1"]
|
||||
.createInstance(Ci.nsITextInputProcessor);
|
||||
}
|
||||
|
||||
if (!tip.beginInputTransaction(win, textInputProcessorCallback)) {
|
||||
tip = obj.tip = null;
|
||||
}
|
||||
return tip;
|
||||
}
|
||||
};
|
||||
|
||||
const RESIZE_SCROLL_DELAY = 20;
|
||||
// In content editable node, when there are hidden elements such as <br>, it
|
||||
|
@ -41,6 +82,151 @@ let HTMLSelectElement = Ci.nsIDOMHTMLSelectElement;
|
|||
let HTMLOptGroupElement = Ci.nsIDOMHTMLOptGroupElement;
|
||||
let HTMLOptionElement = Ci.nsIDOMHTMLOptionElement;
|
||||
|
||||
function guessKeyNameFromKeyCode(KeyboardEvent, aKeyCode) {
|
||||
switch (aKeyCode) {
|
||||
case KeyboardEvent.DOM_VK_CANCEL:
|
||||
return "Cancel";
|
||||
case KeyboardEvent.DOM_VK_HELP:
|
||||
return "Help";
|
||||
case KeyboardEvent.DOM_VK_BACK_SPACE:
|
||||
return "Backspace";
|
||||
case KeyboardEvent.DOM_VK_TAB:
|
||||
return "Tab";
|
||||
case KeyboardEvent.DOM_VK_CLEAR:
|
||||
return "Clear";
|
||||
case KeyboardEvent.DOM_VK_RETURN:
|
||||
return "Enter";
|
||||
case KeyboardEvent.DOM_VK_SHIFT:
|
||||
return "Shift";
|
||||
case KeyboardEvent.DOM_VK_CONTROL:
|
||||
return "Control";
|
||||
case KeyboardEvent.DOM_VK_ALT:
|
||||
return "Alt";
|
||||
case KeyboardEvent.DOM_VK_PAUSE:
|
||||
return "Pause";
|
||||
case KeyboardEvent.DOM_VK_EISU:
|
||||
return "Eisu";
|
||||
case KeyboardEvent.DOM_VK_ESCAPE:
|
||||
return "Escape";
|
||||
case KeyboardEvent.DOM_VK_CONVERT:
|
||||
return "Convert";
|
||||
case KeyboardEvent.DOM_VK_NONCONVERT:
|
||||
return "NonConvert";
|
||||
case KeyboardEvent.DOM_VK_ACCEPT:
|
||||
return "Accept";
|
||||
case KeyboardEvent.DOM_VK_MODECHANGE:
|
||||
return "ModeChange";
|
||||
case KeyboardEvent.DOM_VK_PAGE_UP:
|
||||
return "PageUp";
|
||||
case KeyboardEvent.DOM_VK_PAGE_DOWN:
|
||||
return "PageDown";
|
||||
case KeyboardEvent.DOM_VK_END:
|
||||
return "End";
|
||||
case KeyboardEvent.DOM_VK_HOME:
|
||||
return "Home";
|
||||
case KeyboardEvent.DOM_VK_LEFT:
|
||||
return "ArrowLeft";
|
||||
case KeyboardEvent.DOM_VK_UP:
|
||||
return "ArrowUp";
|
||||
case KeyboardEvent.DOM_VK_RIGHT:
|
||||
return "ArrowRight";
|
||||
case KeyboardEvent.DOM_VK_DOWN:
|
||||
return "ArrowDown";
|
||||
case KeyboardEvent.DOM_VK_SELECT:
|
||||
return "Select";
|
||||
case KeyboardEvent.DOM_VK_PRINT:
|
||||
return "Print";
|
||||
case KeyboardEvent.DOM_VK_EXECUTE:
|
||||
return "Execute";
|
||||
case KeyboardEvent.DOM_VK_PRINTSCREEN:
|
||||
return "PrintScreen";
|
||||
case KeyboardEvent.DOM_VK_INSERT:
|
||||
return "Insert";
|
||||
case KeyboardEvent.DOM_VK_DELETE:
|
||||
return "Delete";
|
||||
case KeyboardEvent.DOM_VK_WIN:
|
||||
return "OS";
|
||||
case KeyboardEvent.DOM_VK_CONTEXT_MENU:
|
||||
return "ContextMenu";
|
||||
case KeyboardEvent.DOM_VK_SLEEP:
|
||||
return "Standby";
|
||||
case KeyboardEvent.DOM_VK_F1:
|
||||
return "F1";
|
||||
case KeyboardEvent.DOM_VK_F2:
|
||||
return "F2";
|
||||
case KeyboardEvent.DOM_VK_F3:
|
||||
return "F3";
|
||||
case KeyboardEvent.DOM_VK_F4:
|
||||
return "F4";
|
||||
case KeyboardEvent.DOM_VK_F5:
|
||||
return "F5";
|
||||
case KeyboardEvent.DOM_VK_F6:
|
||||
return "F6";
|
||||
case KeyboardEvent.DOM_VK_F7:
|
||||
return "F7";
|
||||
case KeyboardEvent.DOM_VK_F8:
|
||||
return "F8";
|
||||
case KeyboardEvent.DOM_VK_F9:
|
||||
return "F9";
|
||||
case KeyboardEvent.DOM_VK_F10:
|
||||
return "F10";
|
||||
case KeyboardEvent.DOM_VK_F11:
|
||||
return "F11";
|
||||
case KeyboardEvent.DOM_VK_F12:
|
||||
return "F12";
|
||||
case KeyboardEvent.DOM_VK_F13:
|
||||
return "F13";
|
||||
case KeyboardEvent.DOM_VK_F14:
|
||||
return "F14";
|
||||
case KeyboardEvent.DOM_VK_F15:
|
||||
return "F15";
|
||||
case KeyboardEvent.DOM_VK_F16:
|
||||
return "F16";
|
||||
case KeyboardEvent.DOM_VK_F17:
|
||||
return "F17";
|
||||
case KeyboardEvent.DOM_VK_F18:
|
||||
return "F18";
|
||||
case KeyboardEvent.DOM_VK_F19:
|
||||
return "F19";
|
||||
case KeyboardEvent.DOM_VK_F20:
|
||||
return "F20";
|
||||
case KeyboardEvent.DOM_VK_F21:
|
||||
return "F21";
|
||||
case KeyboardEvent.DOM_VK_F22:
|
||||
return "F22";
|
||||
case KeyboardEvent.DOM_VK_F23:
|
||||
return "F23";
|
||||
case KeyboardEvent.DOM_VK_F24:
|
||||
return "F24";
|
||||
case KeyboardEvent.DOM_VK_NUM_LOCK:
|
||||
return "NumLock";
|
||||
case KeyboardEvent.DOM_VK_SCROLL_LOCK:
|
||||
return "ScrollLock";
|
||||
case KeyboardEvent.DOM_VK_VOLUME_MUTE:
|
||||
return "VolumeMute";
|
||||
case KeyboardEvent.DOM_VK_VOLUME_DOWN:
|
||||
return "VolumeDown";
|
||||
case KeyboardEvent.DOM_VK_VOLUME_UP:
|
||||
return "VolumeUp";
|
||||
case KeyboardEvent.DOM_VK_META:
|
||||
return "Meta";
|
||||
case KeyboardEvent.DOM_VK_ALTGR:
|
||||
return "AltGraph";
|
||||
case KeyboardEvent.DOM_VK_ATTN:
|
||||
return "Attn";
|
||||
case KeyboardEvent.DOM_VK_CRSEL:
|
||||
return "CrSel";
|
||||
case KeyboardEvent.DOM_VK_EXSEL:
|
||||
return "ExSel";
|
||||
case KeyboardEvent.DOM_VK_EREOF:
|
||||
return "EraseEof";
|
||||
case KeyboardEvent.DOM_VK_PLAY:
|
||||
return "Play";
|
||||
default:
|
||||
return "Unidentified";
|
||||
}
|
||||
}
|
||||
|
||||
let FormVisibility = {
|
||||
/**
|
||||
* Searches upwards in the DOM for an element that has been scrolled.
|
||||
|
@ -184,6 +370,41 @@ let FormVisibility = {
|
|||
}
|
||||
};
|
||||
|
||||
// This object implements nsITextInputProcessorCallback
|
||||
let textInputProcessorCallback = {
|
||||
onNotify: function(aTextInputProcessor, aNotification) {
|
||||
try {
|
||||
switch (aNotification.type) {
|
||||
case "request-to-commit":
|
||||
// TODO: Send a notification through asyncMessage to the keyboard here.
|
||||
aTextInputProcessor.commitComposition();
|
||||
|
||||
break;
|
||||
case "request-to-cancel":
|
||||
// TODO: Send a notification through asyncMessage to the keyboard here.
|
||||
aTextInputProcessor.cancelComposition();
|
||||
|
||||
break;
|
||||
|
||||
case "notify-detached":
|
||||
// TODO: Send a notification through asyncMessage to the keyboard here.
|
||||
break;
|
||||
|
||||
// TODO: Manage _focusedElement for text input from here instead.
|
||||
// (except for <select> which will be need to handled elsewhere)
|
||||
case "notify-focus":
|
||||
break;
|
||||
|
||||
case "notify-blur":
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
let FormAssistant = {
|
||||
init: function fa_init() {
|
||||
addEventListener("focus", this, true, false);
|
||||
|
@ -432,7 +653,7 @@ let FormAssistant = {
|
|||
break;
|
||||
|
||||
case "keydown":
|
||||
if (!this.focusedElement) {
|
||||
if (!this.focusedElement || this._editing) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -440,7 +661,7 @@ let FormAssistant = {
|
|||
break;
|
||||
|
||||
case "keyup":
|
||||
if (!this.focusedElement) {
|
||||
if (!this.focusedElement || this._editing) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -502,34 +723,132 @@ let FormAssistant = {
|
|||
case "Forms:Input:SendKey":
|
||||
CompositionManager.endComposition('');
|
||||
|
||||
let flags = domWindowUtils.KEY_FLAG_NOT_SYNTHESIZED_FOR_TESTS;
|
||||
this._editing = true;
|
||||
let doKeypress = domWindowUtils.sendKeyEvent('keydown', json.keyCode,
|
||||
json.charCode, json.modifiers, flags);
|
||||
if (doKeypress) {
|
||||
domWindowUtils.sendKeyEvent('keypress', json.keyCode,
|
||||
json.charCode, json.modifiers, flags);
|
||||
let win = target.ownerDocument.defaultView;
|
||||
let tip = WindowMap.getTextInputProcessor(win);
|
||||
if (!tip) {
|
||||
if (json.requestId) {
|
||||
sendAsyncMessage("Forms:SendKey:Result:Error", {
|
||||
requestId: json.requestId,
|
||||
error: "Unable to start input transaction."
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(!json.repeat) {
|
||||
domWindowUtils.sendKeyEvent('keyup', json.keyCode,
|
||||
json.charCode, json.modifiers, flags);
|
||||
// If we receive a keyboardEventDict from json, that means the user
|
||||
// is calling the method with the new arguments.
|
||||
// Otherwise, we would have to construct our own keyboardEventDict
|
||||
// based on legacy values we have received.
|
||||
let keyboardEventDict = json.keyboardEventDict;
|
||||
let flags = 0;
|
||||
|
||||
if (keyboardEventDict) {
|
||||
if ('flags' in keyboardEventDict) {
|
||||
flags = keyboardEventDict.flags;
|
||||
}
|
||||
} else {
|
||||
// The naive way to figure out if the key to dispatch is printable.
|
||||
let printable = !!json.charCode;
|
||||
|
||||
// For printable keys, the value should be the actual character.
|
||||
// For non-printable keys, it should be a value in the D3E spec.
|
||||
// Here we make some educated guess for it.
|
||||
let key = printable ?
|
||||
String.fromCharCode(json.charCode) :
|
||||
guessKeyNameFromKeyCode(win.KeyboardEvent, json.keyCode);
|
||||
|
||||
// keyCode from content is only respected when the key is not an
|
||||
// an alphanumeric character. We also ask TextInputProcessor not to
|
||||
// infer this value for non-printable keys to keep the original
|
||||
// behavior.
|
||||
let keyCode = (printable && /^[a-zA-Z0-9]$/.test(key)) ?
|
||||
key.toUpperCase().charCodeAt(0) :
|
||||
json.keyCode;
|
||||
|
||||
keyboardEventDict = {
|
||||
key: key,
|
||||
keyCode: keyCode,
|
||||
// We don't have any information to tell the virtual key the
|
||||
// user have interacted with.
|
||||
code: "",
|
||||
// We do not have the information to infer location of the virtual key
|
||||
// either (and we would need TextInputProcessor not to compute it).
|
||||
location: 0,
|
||||
// This indicates the key is triggered for repeats.
|
||||
repeat: json.repeat
|
||||
};
|
||||
|
||||
flags = tip.KEY_KEEP_KEY_LOCATION_STANDARD;
|
||||
if (!printable) {
|
||||
flags |= tip.KEY_NON_PRINTABLE_KEY;
|
||||
}
|
||||
if (!keyboardEventDict.keyCode) {
|
||||
flags |= tip.KEY_KEEP_KEYCODE_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
this._editing = false;
|
||||
let keyboardEvent = new win.KeyboardEvent("", keyboardEventDict);
|
||||
|
||||
if (json.requestId && doKeypress) {
|
||||
sendAsyncMessage("Forms:SendKey:Result:OK", {
|
||||
requestId: json.requestId,
|
||||
selectioninfo: this.getSelectionInfo()
|
||||
});
|
||||
let keydownDefaultPrevented = false;
|
||||
try {
|
||||
switch (json.method) {
|
||||
case 'sendKey': {
|
||||
let consumedFlags = tip.keydown(keyboardEvent, flags);
|
||||
keydownDefaultPrevented =
|
||||
!!(tip.KEYDOWN_IS_CONSUMED & consumedFlags);
|
||||
if (!keyboardEventDict.repeat) {
|
||||
tip.keyup(keyboardEvent, flags);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'keydown': {
|
||||
let consumedFlags = tip.keydown(keyboardEvent, flags);
|
||||
keydownDefaultPrevented =
|
||||
!!(tip.KEYDOWN_IS_CONSUMED & consumedFlags);
|
||||
break;
|
||||
}
|
||||
case 'keyup': {
|
||||
tip.keyup(keyboardEvent, flags);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
dump("forms.js:" + err.toString() + "\n");
|
||||
|
||||
if (json.requestId) {
|
||||
if (err instanceof Ci.nsIException &&
|
||||
err.result == Cr.NS_ERROR_ILLEGAL_VALUE) {
|
||||
sendAsyncMessage("Forms:SendKey:Result:Error", {
|
||||
requestId: json.requestId,
|
||||
error: "The values specified are illegal."
|
||||
});
|
||||
} else {
|
||||
sendAsyncMessage("Forms:SendKey:Result:Error", {
|
||||
requestId: json.requestId,
|
||||
error: "Unable to type into destroyed input."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if (json.requestId && !doKeypress) {
|
||||
sendAsyncMessage("Forms:SendKey:Result:Error", {
|
||||
requestId: json.requestId,
|
||||
error: "Keydown event got canceled"
|
||||
});
|
||||
|
||||
if (json.requestId) {
|
||||
if (keydownDefaultPrevented) {
|
||||
sendAsyncMessage("Forms:SendKey:Result:Error", {
|
||||
requestId: json.requestId,
|
||||
error: "Key event(s) was cancelled."
|
||||
});
|
||||
} else {
|
||||
sendAsyncMessage("Forms:SendKey:Result:OK", {
|
||||
requestId: json.requestId,
|
||||
selectioninfo: this.getSelectionInfo()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "Forms:Select:Choice":
|
||||
|
@ -642,7 +961,7 @@ let FormAssistant = {
|
|||
|
||||
case "Forms:SetComposition": {
|
||||
CompositionManager.setComposition(target, json.text, json.cursor,
|
||||
json.clauses);
|
||||
json.clauses, json.keyboardEventDict);
|
||||
sendAsyncMessage("Forms:SetComposition:Result:OK", {
|
||||
requestId: json.requestId,
|
||||
selectioninfo: this.getSelectionInfo()
|
||||
|
@ -651,7 +970,7 @@ let FormAssistant = {
|
|||
}
|
||||
|
||||
case "Forms:EndComposition": {
|
||||
CompositionManager.endComposition(json.text);
|
||||
CompositionManager.endComposition(json.text, json.keyboardEventDict);
|
||||
sendAsyncMessage("Forms:EndComposition:Result:OK", {
|
||||
requestId: json.requestId,
|
||||
selectioninfo: this.getSelectionInfo()
|
||||
|
@ -1170,7 +1489,8 @@ function replaceSurroundingText(element, text, offset, length) {
|
|||
|
||||
let CompositionManager = {
|
||||
_isStarted: false,
|
||||
_textInputProcessor: null,
|
||||
_tip: null,
|
||||
_KeyboardEventForWin: null,
|
||||
_clauseAttrMap: {
|
||||
'raw-input':
|
||||
Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE,
|
||||
|
@ -1182,35 +1502,7 @@ let CompositionManager = {
|
|||
Ci.nsITextInputProcessor.ATTR_SELECTED_CLAUSE
|
||||
},
|
||||
|
||||
_callback: function cm_callback(aTIP, aNotification)
|
||||
{
|
||||
try {
|
||||
switch (aNotification.type) {
|
||||
case "request-to-commit":
|
||||
aTIP.commitComposition();
|
||||
break;
|
||||
case "request-to-cancel":
|
||||
aTIP.cancelComposition();
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
_prepareTextInputProcessor: function cm_prepareTextInputProcessor(aWindow)
|
||||
{
|
||||
if (!this._textInputProcessor) {
|
||||
this._textInputProcessor =
|
||||
Cc["@mozilla.org/text-input-processor;1"].
|
||||
createInstance(Ci.nsITextInputProcessor);
|
||||
}
|
||||
return this._textInputProcessor.beginInputTransaction(aWindow,
|
||||
this._callback);
|
||||
},
|
||||
|
||||
setComposition: function cm_setComposition(element, text, cursor, clauses) {
|
||||
setComposition: function cm_setComposition(element, text, cursor, clauses, dict) {
|
||||
// Check parameters.
|
||||
if (!element) {
|
||||
return;
|
||||
|
@ -1248,30 +1540,57 @@ let CompositionManager = {
|
|||
}
|
||||
|
||||
let win = element.ownerDocument.defaultView;
|
||||
if (!this._prepareTextInputProcessor(win)) {
|
||||
let tip = WindowMap.getTextInputProcessor(win);
|
||||
if (!tip) {
|
||||
return;
|
||||
}
|
||||
// Update the composing text.
|
||||
this._textInputProcessor.setPendingCompositionString(text);
|
||||
tip.setPendingCompositionString(text);
|
||||
for (var i = 0; i < clauseLens.length; i++) {
|
||||
if (!clauseLens[i]) {
|
||||
continue;
|
||||
}
|
||||
this._textInputProcessor.appendClauseToPendingComposition(clauseLens[i],
|
||||
clauseAttrs[i]);
|
||||
tip.appendClauseToPendingComposition(clauseLens[i], clauseAttrs[i]);
|
||||
}
|
||||
if (cursor >= 0) {
|
||||
this._textInputProcessor.setCaretInPendingComposition(cursor);
|
||||
tip.setCaretInPendingComposition(cursor);
|
||||
}
|
||||
|
||||
if (!dict) {
|
||||
this._isStarted = tip.flushPendingComposition();
|
||||
} else {
|
||||
let keyboardEvent = new win.KeyboardEvent("", dict);
|
||||
let flags = dict.flags;
|
||||
this._isStarted = tip.flushPendingComposition(keyboardEvent, flags);
|
||||
}
|
||||
|
||||
if (this._isStarted) {
|
||||
this._tip = tip;
|
||||
this._KeyboardEventForWin = win.KeyboardEvent;
|
||||
}
|
||||
this._isStarted = this._textInputProcessor.flushPendingComposition();
|
||||
},
|
||||
|
||||
endComposition: function cm_endComposition(text) {
|
||||
endComposition: function cm_endComposition(text, dict) {
|
||||
if (!this._isStarted) {
|
||||
return;
|
||||
}
|
||||
this._textInputProcessor.commitCompositionWith(text ? text : "");
|
||||
let tip = this._tip;
|
||||
if (!tip) {
|
||||
return;
|
||||
}
|
||||
|
||||
text = text || "";
|
||||
if (!dict) {
|
||||
tip.commitCompositionWith(text);
|
||||
} else {
|
||||
let keyboardEvent = new this._KeyboardEventForWin("", dict);
|
||||
let flags = dict.flags;
|
||||
tip.commitCompositionWith(text, keyboardEvent, flags);
|
||||
}
|
||||
|
||||
this._isStarted = false;
|
||||
this._tip = null;
|
||||
this._KeyboardEventForWin = null;
|
||||
},
|
||||
|
||||
// Composition ends due to external actions.
|
||||
|
@ -1281,5 +1600,7 @@ let CompositionManager = {
|
|||
}
|
||||
|
||||
this._isStarted = false;
|
||||
this._tip = null;
|
||||
this._KeyboardEventForWin = null;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -26,3 +26,4 @@ support-files =
|
|||
[test_two_inputs.html]
|
||||
[test_two_selects.html]
|
||||
[test_unload.html]
|
||||
[test_bug1137557.html]
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -254,7 +254,7 @@ interface nsITextInputProcessorCallback;
|
|||
* TIP.keyup(leftShift);
|
||||
*/
|
||||
|
||||
[scriptable, builtinclass, uuid(6617a9f6-3e16-4086-9e1e-c8a6c5d505c7)]
|
||||
[scriptable, builtinclass, uuid(581f6619-76ed-478c-905d-b8e6553a714a)]
|
||||
interface nsITextInputProcessor : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -489,6 +489,12 @@ interface nsITextInputProcessor : nsISupports
|
|||
// or emulating legacy API behavior.
|
||||
const unsigned long KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT = 0x00000020;
|
||||
|
||||
// These values can be used to do bitwise operation with the return value of
|
||||
// the keydown() method.
|
||||
const unsigned long KEYEVENT_NOT_CONSUMED = 0x00000000;
|
||||
const unsigned long KEYDOWN_IS_CONSUMED = 0x00000001;
|
||||
const unsigned long KEYPRESS_IS_CONSUMED = 0x00000002;
|
||||
|
||||
/**
|
||||
* keydown() may dispatch a keydown event and some keypress events if
|
||||
* preceding keydown event isn't consumed and they are necessary.
|
||||
|
@ -529,13 +535,20 @@ interface nsITextInputProcessor : nsISupports
|
|||
* each instance and set automatically.
|
||||
* @param aKeyFlags Special flags. The values can be some of KEY_*
|
||||
* constants.
|
||||
* @return true if neither the keydown event or following
|
||||
* keypress events is *not* consumed.
|
||||
* Otherwise, i.e., preventDefault() is called, false.
|
||||
* @return KEYEVENT_NOT_CONSUMED, if the keydown event nor
|
||||
* the following keypress event(s) are consumed.
|
||||
* KEYDOWN_IS_CONSUMED, if the keydown event is
|
||||
* consumed. No keypress event will be dispatched in
|
||||
* this case.
|
||||
* KEYPRESS_IS_CONSUMED, if the keypress event(s) is
|
||||
* consumed when dispatched.
|
||||
* Note that keypress event is always consumed by
|
||||
* native code for the printable keys (indicating the
|
||||
* default action has been taken).
|
||||
*/
|
||||
[optional_argc]
|
||||
boolean keydown(in nsIDOMKeyEvent aKeyboardEvent,
|
||||
[optional] in unsigned long aKeyFlags);
|
||||
unsigned long keydown(in nsIDOMKeyEvent aKeyboardEvent,
|
||||
[optional] in unsigned long aKeyFlags);
|
||||
|
||||
/**
|
||||
* Similar to keydown(), but this dispatches only a keyup event.
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "mozilla/hal_sandbox/PHalParent.h"
|
||||
#include "nsIAppsService.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
@ -232,21 +231,10 @@ GetAppPrincipal(uint32_t aAppId)
|
|||
nsresult rv = appsService->GetAppByLocalId(aAppId, getter_AddRefs(app));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
nsString origin;
|
||||
rv = app->GetOrigin(origin);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
app->GetPrincipal(getter_AddRefs(principal));
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
NS_NewURI(getter_AddRefs(uri), origin);
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> secMan =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> appPrincipal;
|
||||
rv = secMan->GetAppCodebasePrincipal(uri, aAppId, false,
|
||||
getter_AddRefs(appPrincipal));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
return appPrincipal.forget();
|
||||
return principal.forget();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
|
|
|
@ -1562,23 +1562,15 @@ TabChild::MaybeRequestPreinitCamera()
|
|||
return;
|
||||
}
|
||||
|
||||
nsString manifestUrl = EmptyString();
|
||||
appsService->GetManifestURLByLocalId(OwnAppId(), manifestUrl);
|
||||
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
|
||||
if (NS_WARN_IF(!secMan)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), manifestUrl);
|
||||
nsCOMPtr<mozIApplication> app;
|
||||
nsresult rv = appsService->GetAppByLocalId(OwnAppId(), getter_AddRefs(app));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = secMan->GetAppCodebasePrincipal(uri, OwnAppId(), false,
|
||||
getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
app->GetPrincipal(getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(!principal)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -354,9 +354,11 @@ OutputStreamManager::Disconnect()
|
|||
}
|
||||
}
|
||||
|
||||
DecodedStream::DecodedStream(MediaQueue<MediaData>& aAudioQueue,
|
||||
DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
|
||||
MediaQueue<MediaData>& aAudioQueue,
|
||||
MediaQueue<MediaData>& aVideoQueue)
|
||||
: mMonitor("DecodedStream::mMonitor")
|
||||
: mOwnerThread(aOwnerThread)
|
||||
, mMonitor("DecodedStream::mMonitor")
|
||||
, mPlaying(false)
|
||||
, mVolume(1.0)
|
||||
, mAudioQueue(aAudioQueue)
|
||||
|
@ -372,11 +374,13 @@ DecodedStream::~DecodedStream()
|
|||
nsRefPtr<GenericPromise>
|
||||
DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
MOZ_ASSERT(mStartTime.isNothing(), "playback already started.");
|
||||
|
||||
mStartTime.emplace(aStartTime);
|
||||
mInfo = aInfo;
|
||||
ConnectListener();
|
||||
|
||||
class R : public nsRunnable {
|
||||
typedef MozPromiseHolder<GenericPromise> Promise;
|
||||
|
@ -408,12 +412,15 @@ DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo)
|
|||
|
||||
void DecodedStream::StopPlayback()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
// Playback didn't even start at all.
|
||||
if (mStartTime.isNothing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mStartTime.reset();
|
||||
DisconnectListener();
|
||||
|
||||
// Clear mData immediately when this playback session ends so we won't
|
||||
// send data to the wrong stream in SendData() in next playback session.
|
||||
|
@ -451,6 +458,19 @@ DecodedStream::CreateData(MozPromiseHolder<GenericPromise>&& aPromise)
|
|||
auto source = mOutputStreamManager.Graph()->CreateSourceStream(nullptr);
|
||||
mData.reset(new DecodedStreamData(source, mPlaying, Move(aPromise)));
|
||||
mOutputStreamManager.Connect(mData->mStream);
|
||||
|
||||
// Start to send data to the stream immediately
|
||||
nsRefPtr<DecodedStream> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
|
||||
ReentrantMonitorAutoEnter mon(self->GetReentrantMonitor());
|
||||
// Don't send data if playback has ended.
|
||||
if (self->mStartTime.isSome()) {
|
||||
self->SendData();
|
||||
}
|
||||
});
|
||||
// Don't assert success because the owner thread might have begun shutdown
|
||||
// while we are still dealing with jobs on the main thread.
|
||||
mOwnerThread->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -480,6 +500,7 @@ DecodedStream::RemoveOutput(MediaStream* aStream)
|
|||
void
|
||||
DecodedStream::SetPlaying(bool aPlaying)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
mPlaying = aPlaying;
|
||||
if (mData) {
|
||||
|
@ -490,6 +511,7 @@ DecodedStream::SetPlaying(bool aPlaying)
|
|||
void
|
||||
DecodedStream::SetVolume(double aVolume)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
mVolume = aVolume;
|
||||
}
|
||||
|
@ -497,6 +519,7 @@ DecodedStream::SetVolume(double aVolume)
|
|||
void
|
||||
DecodedStream::SetSameOrigin(bool aSameOrigin)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
mSameOrigin = aSameOrigin;
|
||||
}
|
||||
|
@ -504,6 +527,7 @@ DecodedStream::SetSameOrigin(bool aSameOrigin)
|
|||
void
|
||||
DecodedStream::InitTracks()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
if (mData->mStreamInitialized) {
|
||||
|
@ -584,6 +608,7 @@ SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime,
|
|||
void
|
||||
DecodedStream::SendAudio(double aVolume, bool aIsSameOrigin)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
if (!mInfo.HasAudio()) {
|
||||
|
@ -649,6 +674,7 @@ ZeroDurationAtLastChunk(VideoSegment& aInput)
|
|||
void
|
||||
DecodedStream::SendVideo(bool aIsSameOrigin)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
if (!mInfo.HasVideo()) {
|
||||
|
@ -727,6 +753,7 @@ DecodedStream::SendVideo(bool aIsSameOrigin)
|
|||
void
|
||||
DecodedStream::AdvanceTracks()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
StreamTime endPosition = 0;
|
||||
|
@ -751,6 +778,7 @@ DecodedStream::AdvanceTracks()
|
|||
void
|
||||
DecodedStream::SendData()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
MOZ_ASSERT(mStartTime.isSome(), "Must be called after StartPlayback()");
|
||||
|
||||
|
@ -781,6 +809,7 @@ DecodedStream::SendData()
|
|||
int64_t
|
||||
DecodedStream::AudioEndTime() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
if (mStartTime.isSome() && mInfo.HasAudio() && mData) {
|
||||
CheckedInt64 t = mStartTime.ref() +
|
||||
|
@ -795,6 +824,7 @@ DecodedStream::AudioEndTime() const
|
|||
int64_t
|
||||
DecodedStream::GetPosition() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
// This is only called after MDSM starts playback. So mStartTime is
|
||||
// guaranteed to be something.
|
||||
|
@ -805,8 +835,37 @@ DecodedStream::GetPosition() const
|
|||
bool
|
||||
DecodedStream::IsFinished() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
return mData && mData->IsFinished();
|
||||
}
|
||||
|
||||
void
|
||||
DecodedStream::ConnectListener()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
|
||||
mAudioPushListener = mAudioQueue.PushEvent().Connect(
|
||||
mOwnerThread, this, &DecodedStream::SendData);
|
||||
mAudioFinishListener = mAudioQueue.FinishEvent().Connect(
|
||||
mOwnerThread, this, &DecodedStream::SendData);
|
||||
mVideoPushListener = mVideoQueue.PushEvent().Connect(
|
||||
mOwnerThread, this, &DecodedStream::SendData);
|
||||
mVideoFinishListener = mVideoQueue.FinishEvent().Connect(
|
||||
mOwnerThread, this, &DecodedStream::SendData);
|
||||
}
|
||||
|
||||
void
|
||||
DecodedStream::DisconnectListener()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
|
||||
mAudioPushListener.Disconnect();
|
||||
mVideoPushListener.Disconnect();
|
||||
mAudioFinishListener.Disconnect();
|
||||
mVideoFinishListener.Disconnect();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
#define DecodedStream_h_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "MediaInfo.h"
|
||||
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
|
@ -100,7 +102,8 @@ private:
|
|||
class DecodedStream {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream);
|
||||
public:
|
||||
DecodedStream(MediaQueue<MediaData>& aAudioQueue,
|
||||
DecodedStream(AbstractThread* aOwnerThread,
|
||||
MediaQueue<MediaData>& aAudioQueue,
|
||||
MediaQueue<MediaData>& aVideoQueue);
|
||||
|
||||
// Mimic MDSM::StartAudioThread.
|
||||
|
@ -125,8 +128,6 @@ public:
|
|||
bool IsFinished() const;
|
||||
bool HasConsumers() const;
|
||||
|
||||
void SendData();
|
||||
|
||||
protected:
|
||||
virtual ~DecodedStream();
|
||||
|
||||
|
@ -137,6 +138,16 @@ private:
|
|||
void AdvanceTracks();
|
||||
void SendAudio(double aVolume, bool aIsSameOrigin);
|
||||
void SendVideo(bool aIsSameOrigin);
|
||||
void SendData();
|
||||
|
||||
void AssertOwnerThread() const {
|
||||
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
|
||||
}
|
||||
|
||||
void ConnectListener();
|
||||
void DisconnectListener();
|
||||
|
||||
const nsRefPtr<AbstractThread> mOwnerThread;
|
||||
|
||||
UniquePtr<DecodedStreamData> mData;
|
||||
// Data about MediaStreams that are being fed by the decoder.
|
||||
|
@ -160,6 +171,11 @@ private:
|
|||
|
||||
MediaQueue<MediaData>& mAudioQueue;
|
||||
MediaQueue<MediaData>& mVideoQueue;
|
||||
|
||||
MediaEventListener mAudioPushListener;
|
||||
MediaEventListener mVideoPushListener;
|
||||
MediaEventListener mAudioFinishListener;
|
||||
MediaEventListener mVideoFinishListener;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -221,7 +221,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||
mSentLoadedMetadataEvent(false),
|
||||
mSentFirstFrameLoadedEvent(false),
|
||||
mSentPlaybackEndedEvent(false),
|
||||
mDecodedStream(new DecodedStream(mAudioQueue, mVideoQueue)),
|
||||
mDecodedStream(new DecodedStream(mTaskQueue, mAudioQueue, mVideoQueue)),
|
||||
mResource(aDecoder->GetResource()),
|
||||
mBuffered(mTaskQueue, TimeIntervals(),
|
||||
"MediaDecoderStateMachine::mBuffered (Mirror)"),
|
||||
|
@ -369,14 +369,12 @@ int64_t MediaDecoderStateMachine::GetDecodedAudioDuration()
|
|||
return audioDecoded;
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::SendStreamData()
|
||||
void MediaDecoderStateMachine::DiscardStreamData()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
|
||||
|
||||
mDecodedStream->SendData();
|
||||
|
||||
const auto clockTime = GetClock();
|
||||
while (true) {
|
||||
const MediaData* a = AudioQueue().PeekFront();
|
||||
|
@ -568,10 +566,6 @@ MediaDecoderStateMachine::OnAudioDecoded(AudioData* aAudioSample)
|
|||
if (mIsAudioPrerolling && DonePrerollingAudio()) {
|
||||
StopPrerollingAudio();
|
||||
}
|
||||
// Schedule the state machine to send stream data as soon as possible.
|
||||
if (mAudioCaptured) {
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -767,10 +761,6 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
|
|||
return;
|
||||
}
|
||||
CheckIfDecodeComplete();
|
||||
// Schedule the state machine to notify track ended as soon as possible.
|
||||
if (mAudioCaptured) {
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
return;
|
||||
}
|
||||
case DECODER_STATE_SEEKING: {
|
||||
|
@ -857,7 +847,7 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
|
|||
// frame pushed in the queue, schedule the state machine as soon as
|
||||
// possible to render the video frame or delay the state machine thread
|
||||
// accurately.
|
||||
if (mAudioCaptured || VideoQueue().GetSize() == 1) {
|
||||
if (VideoQueue().GetSize() == 1) {
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
|
@ -2651,7 +2641,7 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
|
|||
}
|
||||
|
||||
if (mAudioCaptured) {
|
||||
SendStreamData();
|
||||
DiscardStreamData();
|
||||
}
|
||||
|
||||
TimeStamp nowTime;
|
||||
|
|
|
@ -323,10 +323,8 @@ public:
|
|||
mDecoder = nullptr;
|
||||
}
|
||||
|
||||
// Copy queued audio/video data in the reader to any output MediaStreams that
|
||||
// need it.
|
||||
void SendStreamData();
|
||||
void FinishStreamData();
|
||||
// Discard audio/video data that are already played by MSG.
|
||||
void DiscardStreamData();
|
||||
bool HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs);
|
||||
bool HaveEnoughDecodedVideo();
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ struct EventTypeTraits<void> {
|
|||
* Test if a method function or lambda accepts one or more arguments.
|
||||
*/
|
||||
template <typename T>
|
||||
class TakeArgs {
|
||||
class TakeArgsHelper {
|
||||
template <typename C> static FalseType test(void(C::*)(), int);
|
||||
template <typename C> static FalseType test(void(C::*)() const, int);
|
||||
template <typename C> static FalseType test(void(C::*)() volatile, int);
|
||||
|
@ -87,6 +87,9 @@ public:
|
|||
typedef decltype(test(DeclVal<T>(), 0)) Type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct TakeArgs : public TakeArgsHelper<T>::Type {};
|
||||
|
||||
template <typename T> struct EventTarget;
|
||||
|
||||
template <>
|
||||
|
@ -118,6 +121,193 @@ private:
|
|||
T* const mPtr;
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper class to pass event data to the listeners. Optimized to save
|
||||
* copy when Move is possible or |Function| takes no arguments.
|
||||
*/
|
||||
template<typename Target, typename Function>
|
||||
class ListenerHelper {
|
||||
// Define our custom runnable to minimize copy of the event data.
|
||||
// NS_NewRunnableFunction will result in 2 copies of the event data.
|
||||
// One is captured by the lambda and the other is the copy of the lambda.
|
||||
template <typename T>
|
||||
class R : public nsRunnable {
|
||||
typedef typename RemoveCV<typename RemoveReference<T>::Type>::Type ArgType;
|
||||
public:
|
||||
template <typename U>
|
||||
R(RevocableToken* aToken, const Function& aFunction, U&& aEvent)
|
||||
: mToken(aToken), mFunction(aFunction), mEvent(Forward<U>(aEvent)) {}
|
||||
|
||||
NS_IMETHOD Run() override {
|
||||
// Don't call the listener if it is disconnected.
|
||||
if (!mToken->IsRevoked()) {
|
||||
// Enable move whenever possible since mEvent won't be used anymore.
|
||||
mFunction(Move(mEvent));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<RevocableToken> mToken;
|
||||
Function mFunction;
|
||||
ArgType mEvent;
|
||||
};
|
||||
|
||||
public:
|
||||
ListenerHelper(RevocableToken* aToken, Target* aTarget, const Function& aFunc)
|
||||
: mToken(aToken), mTarget(aTarget), mFunction(aFunc) {}
|
||||
|
||||
// |F| takes one argument.
|
||||
template <typename F, typename T>
|
||||
typename EnableIf<TakeArgs<F>::value, void>::Type
|
||||
Dispatch(const F& aFunc, T&& aEvent) {
|
||||
nsCOMPtr<nsIRunnable> r = new R<T>(mToken, aFunc, Forward<T>(aEvent));
|
||||
EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
|
||||
}
|
||||
|
||||
// |F| takes no arguments. Don't bother passing aEvent.
|
||||
template <typename F, typename T>
|
||||
typename EnableIf<!TakeArgs<F>::value, void>::Type
|
||||
Dispatch(const F& aFunc, T&&) {
|
||||
const nsRefPtr<RevocableToken>& token = mToken;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
|
||||
// Don't call the listener if it is disconnected.
|
||||
if (!token->IsRevoked()) {
|
||||
aFunc();
|
||||
}
|
||||
});
|
||||
EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Dispatch(T&& aEvent) {
|
||||
Dispatch(mFunction, Forward<T>(aEvent));
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<RevocableToken> mToken;
|
||||
const nsRefPtr<Target> mTarget;
|
||||
Function mFunction;
|
||||
};
|
||||
|
||||
/**
|
||||
* Define whether an event data should be copied or moved to the listeners.
|
||||
*
|
||||
* @Copy Data will always be copied. Each listener gets a copy.
|
||||
* @Move Data will always be moved.
|
||||
* @Both Data will be moved when possible or copied when necessary.
|
||||
*/
|
||||
enum class EventPassMode : int8_t {
|
||||
Copy,
|
||||
Move,
|
||||
Both
|
||||
};
|
||||
|
||||
class ListenerBase {
|
||||
public:
|
||||
ListenerBase() : mToken(new RevocableToken()) {}
|
||||
~ListenerBase() {
|
||||
MOZ_ASSERT(Token()->IsRevoked(), "Must disconnect the listener.");
|
||||
}
|
||||
RevocableToken* Token() const {
|
||||
return mToken;
|
||||
}
|
||||
private:
|
||||
const nsRefPtr<RevocableToken> mToken;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stored by MediaEventSource to send notifications to the listener.
|
||||
* Since virtual methods can not be templated, this class is specialized
|
||||
* to provide different Dispatch() overloads depending on EventPassMode.
|
||||
*/
|
||||
template <typename ArgType, EventPassMode Mode = EventPassMode::Copy>
|
||||
class Listener : public ListenerBase {
|
||||
public:
|
||||
virtual ~Listener() {}
|
||||
virtual void Dispatch(const ArgType& aEvent) = 0;
|
||||
};
|
||||
|
||||
template <typename ArgType>
|
||||
class Listener<ArgType, EventPassMode::Both> : public ListenerBase {
|
||||
public:
|
||||
virtual ~Listener() {}
|
||||
virtual void Dispatch(const ArgType& aEvent) = 0;
|
||||
virtual void Dispatch(ArgType&& aEvent) = 0;
|
||||
};
|
||||
|
||||
template <typename ArgType>
|
||||
class Listener<ArgType, EventPassMode::Move> : public ListenerBase {
|
||||
public:
|
||||
virtual ~Listener() {}
|
||||
virtual void Dispatch(ArgType&& aEvent) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the registered target thread and function so it knows where and to
|
||||
* whom to send the event data.
|
||||
*/
|
||||
template <typename Target, typename Function, typename ArgType, EventPassMode>
|
||||
class ListenerImpl : public Listener<ArgType, EventPassMode::Copy> {
|
||||
public:
|
||||
ListenerImpl(Target* aTarget, const Function& aFunction)
|
||||
: mHelper(ListenerBase::Token(), aTarget, aFunction) {}
|
||||
void Dispatch(const ArgType& aEvent) override {
|
||||
mHelper.Dispatch(aEvent);
|
||||
}
|
||||
private:
|
||||
ListenerHelper<Target, Function> mHelper;
|
||||
};
|
||||
|
||||
template <typename Target, typename Function, typename ArgType>
|
||||
class ListenerImpl<Target, Function, ArgType, EventPassMode::Both>
|
||||
: public Listener<ArgType, EventPassMode::Both> {
|
||||
public:
|
||||
ListenerImpl(Target* aTarget, const Function& aFunction)
|
||||
: mHelper(ListenerBase::Token(), aTarget, aFunction) {}
|
||||
void Dispatch(const ArgType& aEvent) override {
|
||||
mHelper.Dispatch(aEvent);
|
||||
}
|
||||
void Dispatch(ArgType&& aEvent) override {
|
||||
mHelper.Dispatch(Move(aEvent));
|
||||
}
|
||||
private:
|
||||
ListenerHelper<Target, Function> mHelper;
|
||||
};
|
||||
|
||||
template <typename Target, typename Function, typename ArgType>
|
||||
class ListenerImpl<Target, Function, ArgType, EventPassMode::Move>
|
||||
: public Listener<ArgType, EventPassMode::Move> {
|
||||
public:
|
||||
ListenerImpl(Target* aTarget, const Function& aFunction)
|
||||
: mHelper(ListenerBase::Token(), aTarget, aFunction) {}
|
||||
void Dispatch(ArgType&& aEvent) override {
|
||||
mHelper.Dispatch(Move(aEvent));
|
||||
}
|
||||
private:
|
||||
ListenerHelper<Target, Function> mHelper;
|
||||
};
|
||||
|
||||
/**
|
||||
* Select EventPassMode based on ListenerMode and if the type is copyable.
|
||||
*
|
||||
* @Copy Selected when ListenerMode is NonExclusive because each listener
|
||||
* must get a copy.
|
||||
*
|
||||
* @Move Selected when ListenerMode is Exclusive and the type is move-only.
|
||||
*
|
||||
* @Both Selected when ListenerMode is Exclusive and the type is copyable.
|
||||
* The data will be moved when possible and copied when necessary.
|
||||
*/
|
||||
template <typename ArgType, ListenerMode Mode>
|
||||
struct PassModePicker {
|
||||
// TODO: pick EventPassMode::Both when we can detect if a type is
|
||||
// copy-constructible to allow copy-only types in Exclusive mode.
|
||||
static const EventPassMode Value =
|
||||
Mode == ListenerMode::NonExclusive ?
|
||||
EventPassMode::Copy : EventPassMode::Move;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, ListenerMode> class MediaEventSource;
|
||||
|
@ -167,89 +357,15 @@ template <typename EventType, ListenerMode Mode = ListenerMode::NonExclusive>
|
|||
class MediaEventSource {
|
||||
static_assert(!IsReference<EventType>::value, "Ref-type not supported!");
|
||||
typedef typename detail::EventTypeTraits<EventType>::ArgType ArgType;
|
||||
static const detail::EventPassMode PassMode
|
||||
= detail::PassModePicker<ArgType, Mode>::Value;
|
||||
typedef detail::Listener<ArgType, PassMode> Listener;
|
||||
|
||||
/**
|
||||
* Stored by MediaEventSource to send notifications to the listener.
|
||||
*/
|
||||
class Listener {
|
||||
public:
|
||||
Listener() : mToken(new RevocableToken()) {}
|
||||
template<typename Target, typename Func>
|
||||
using ListenerImpl = detail::ListenerImpl<Target, Func, ArgType, PassMode>;
|
||||
|
||||
virtual ~Listener() {
|
||||
MOZ_ASSERT(Token()->IsRevoked(), "Must disconnect the listener.");
|
||||
}
|
||||
|
||||
virtual void Dispatch(const ArgType& aEvent) = 0;
|
||||
|
||||
RevocableToken* Token() const {
|
||||
return mToken;
|
||||
}
|
||||
|
||||
private:
|
||||
const nsRefPtr<RevocableToken> mToken;
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the registered target thread and function so it knows where and to
|
||||
* whom to send the event data.
|
||||
*/
|
||||
template<typename Target, typename Function>
|
||||
class ListenerImpl : public Listener {
|
||||
public:
|
||||
explicit ListenerImpl(Target* aTarget, const Function& aFunction)
|
||||
: mTarget(aTarget), mFunction(aFunction) {}
|
||||
|
||||
// |Function| takes one argument.
|
||||
void Dispatch(const ArgType& aEvent, TrueType) {
|
||||
// Define our custom runnable to minimize copy of the event data.
|
||||
// NS_NewRunnableFunction will result in 2 copies of the event data.
|
||||
// One is captured by the lambda and the other is the copy of the lambda.
|
||||
class R : public nsRunnable {
|
||||
public:
|
||||
R(RevocableToken* aToken,
|
||||
const Function& aFunction, const ArgType& aEvent)
|
||||
: mToken(aToken), mFunction(aFunction), mEvent(aEvent) {}
|
||||
|
||||
NS_IMETHOD Run() override {
|
||||
// Don't call the listener if it is disconnected.
|
||||
if (!mToken->IsRevoked()) {
|
||||
// Enable move whenever possible since mEvent won't be used anymore.
|
||||
mFunction(Move(mEvent));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<RevocableToken> mToken;
|
||||
Function mFunction;
|
||||
ArgType mEvent;
|
||||
};
|
||||
|
||||
nsCOMPtr<nsIRunnable> r = new R(this->Token(), mFunction, aEvent);
|
||||
detail::EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
|
||||
}
|
||||
|
||||
// |Function| takes no arguments. Don't bother passing aEvent.
|
||||
void Dispatch(const ArgType& aEvent, FalseType) {
|
||||
nsRefPtr<RevocableToken> token = this->Token();
|
||||
const Function& function = mFunction;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
|
||||
// Don't call the listener if it is disconnected.
|
||||
if (!token->IsRevoked()) {
|
||||
function();
|
||||
}
|
||||
});
|
||||
detail::EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
|
||||
}
|
||||
|
||||
void Dispatch(const ArgType& aEvent) override {
|
||||
Dispatch(aEvent, typename detail::TakeArgs<Function>::Type());
|
||||
}
|
||||
|
||||
private:
|
||||
const nsRefPtr<Target> mTarget;
|
||||
Function mFunction;
|
||||
};
|
||||
template <typename Method>
|
||||
using TakeArgs = detail::TakeArgs<Method>;
|
||||
|
||||
template<typename Target, typename Function>
|
||||
MediaEventListener
|
||||
|
@ -263,8 +379,8 @@ class MediaEventSource {
|
|||
|
||||
// |Method| takes one argument.
|
||||
template <typename Target, typename This, typename Method>
|
||||
MediaEventListener
|
||||
ConnectInternal(Target* aTarget, This* aThis, Method aMethod, TrueType) {
|
||||
typename EnableIf<TakeArgs<Method>::value, MediaEventListener>::Type
|
||||
ConnectInternal(Target* aTarget, This* aThis, Method aMethod) {
|
||||
detail::RawPtr<This> thiz(aThis);
|
||||
auto f = [=] (ArgType&& aEvent) {
|
||||
(thiz.get()->*aMethod)(Move(aEvent));
|
||||
|
@ -274,8 +390,8 @@ class MediaEventSource {
|
|||
|
||||
// |Method| takes no arguments. Don't bother passing the event data.
|
||||
template <typename Target, typename This, typename Method>
|
||||
MediaEventListener
|
||||
ConnectInternal(Target* aTarget, This* aThis, Method aMethod, FalseType) {
|
||||
typename EnableIf<!TakeArgs<Method>::value, MediaEventListener>::Type
|
||||
ConnectInternal(Target* aTarget, This* aThis, Method aMethod) {
|
||||
detail::RawPtr<This> thiz(aThis);
|
||||
auto f = [=] () {
|
||||
(thiz.get()->*aMethod)();
|
||||
|
@ -318,21 +434,20 @@ public:
|
|||
template <typename This, typename Method>
|
||||
MediaEventListener
|
||||
Connect(AbstractThread* aTarget, This* aThis, Method aMethod) {
|
||||
return ConnectInternal(aTarget, aThis, aMethod,
|
||||
typename detail::TakeArgs<Method>::Type());
|
||||
return ConnectInternal(aTarget, aThis, aMethod);
|
||||
}
|
||||
|
||||
template <typename This, typename Method>
|
||||
MediaEventListener
|
||||
Connect(nsIEventTarget* aTarget, This* aThis, Method aMethod) {
|
||||
return ConnectInternal(aTarget, aThis, aMethod,
|
||||
typename detail::TakeArgs<Method>::Type());
|
||||
return ConnectInternal(aTarget, aThis, aMethod);
|
||||
}
|
||||
|
||||
protected:
|
||||
MediaEventSource() : mMutex("MediaEventSource::mMutex") {}
|
||||
|
||||
void NotifyInternal(const ArgType& aEvent) {
|
||||
template <typename T>
|
||||
void NotifyInternal(T&& aEvent) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
|
||||
auto&& l = mListeners[i];
|
||||
|
@ -342,7 +457,7 @@ protected:
|
|||
mListeners.RemoveElementAt(i);
|
||||
continue;
|
||||
}
|
||||
l->Dispatch(aEvent);
|
||||
l->Dispatch(Forward<T>(aEvent));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,8 +474,9 @@ private:
|
|||
template <typename EventType, ListenerMode Mode = ListenerMode::NonExclusive>
|
||||
class MediaEventProducer : public MediaEventSource<EventType, Mode> {
|
||||
public:
|
||||
void Notify(const EventType& aEvent) {
|
||||
this->NotifyInternal(aEvent);
|
||||
template <typename T>
|
||||
void Notify(T&& aEvent) {
|
||||
this->NotifyInternal(Forward<T>(aEvent));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -783,6 +783,7 @@ MediaFormatReader::NotifyNewOutput(TrackType aTrack, MediaData* aSample)
|
|||
}
|
||||
decoder.mOutput.AppendElement(aSample);
|
||||
decoder.mNumSamplesOutput++;
|
||||
decoder.mNumSamplesOutputTotal++;
|
||||
ScheduleUpdate(aTrack);
|
||||
}
|
||||
|
||||
|
@ -1120,9 +1121,9 @@ MediaFormatReader::Update(TrackType aTrack)
|
|||
|
||||
if (aTrack == TrackInfo::kVideoTrack) {
|
||||
uint64_t delta =
|
||||
decoder.mNumSamplesOutput - mLastReportedNumDecodedFrames;
|
||||
decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
|
||||
a.mDecoded = static_cast<uint32_t>(delta);
|
||||
mLastReportedNumDecodedFrames = decoder.mNumSamplesOutput;
|
||||
mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
|
||||
}
|
||||
|
||||
if (decoder.HasPromise()) {
|
||||
|
|
|
@ -206,6 +206,7 @@ private:
|
|||
, mDrainComplete(false)
|
||||
, mNumSamplesInput(0)
|
||||
, mNumSamplesOutput(0)
|
||||
, mNumSamplesOutputTotal(0)
|
||||
, mSizeOfQueue(0)
|
||||
, mIsHardwareAccelerated(false)
|
||||
, mLastStreamSourceID(UINT32_MAX)
|
||||
|
@ -263,6 +264,7 @@ private:
|
|||
nsTArray<nsRefPtr<MediaData>> mOutput;
|
||||
uint64_t mNumSamplesInput;
|
||||
uint64_t mNumSamplesOutput;
|
||||
uint64_t mNumSamplesOutputTotal;
|
||||
|
||||
// These get overriden in the templated concrete class.
|
||||
// Indicate if we have a pending promise for decoded frame.
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "XiphExtradata.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
bool XiphHeadersToExtradata(MediaByteBuffer* aCodecSpecificConfig,
|
||||
const nsTArray<const unsigned char*>& aHeaders,
|
||||
const nsTArray<size_t>& aHeaderLens)
|
||||
{
|
||||
size_t nheaders = aHeaders.Length();
|
||||
if (!nheaders || nheaders > 255) return false;
|
||||
aCodecSpecificConfig->AppendElement(nheaders - 1);
|
||||
for (size_t i = 0; i < nheaders - 1; i++) {
|
||||
size_t headerLen;
|
||||
for (headerLen = aHeaderLens[i]; headerLen >= 255; headerLen -= 255) {
|
||||
aCodecSpecificConfig->AppendElement(255);
|
||||
}
|
||||
aCodecSpecificConfig->AppendElement(headerLen);
|
||||
}
|
||||
for (size_t i = 0; i < nheaders; i++) {
|
||||
aCodecSpecificConfig->AppendElements(aHeaders[i], aHeaderLens[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XiphExtradataToHeaders(nsTArray<unsigned char*>& aHeaders,
|
||||
nsTArray<size_t>& aHeaderLens,
|
||||
unsigned char* aData,
|
||||
size_t aAvailable)
|
||||
{
|
||||
size_t total = 0;
|
||||
if (aAvailable < 1) {
|
||||
return false;
|
||||
}
|
||||
aAvailable--;
|
||||
int nHeaders = *aData++ + 1;
|
||||
for (int i = 0; i < nHeaders - 1; i++) {
|
||||
size_t headerLen = 0;
|
||||
for (;;) {
|
||||
// After this test, we know that (aAvailable - total > headerLen) and
|
||||
// (headerLen >= 0) so (aAvailable - total > 0). The loop decrements
|
||||
// aAvailable by 1 and total remains fixed, so we know that in the next
|
||||
// iteration (aAvailable - total >= 0). Thus (aAvailable - total) can
|
||||
// never underflow.
|
||||
if (aAvailable - total <= headerLen) {
|
||||
return false;
|
||||
}
|
||||
// Since we know (aAvailable > total + headerLen), this can't overflow
|
||||
// unless total is near 0 and both aAvailable and headerLen are within
|
||||
// 255 bytes of the maximum representable size. However, that is
|
||||
// impossible, since we would have had to have gone through this loop
|
||||
// more than 255 times to make headerLen that large, and thus decremented
|
||||
// aAvailable more than 255 times.
|
||||
headerLen += *aData;
|
||||
aAvailable--;
|
||||
if (*aData++ != 255) break;
|
||||
}
|
||||
// And this check ensures updating total won't cause (aAvailable - total)
|
||||
// to underflow.
|
||||
if (aAvailable - total < headerLen) {
|
||||
return false;
|
||||
}
|
||||
aHeaderLens.AppendElement(headerLen);
|
||||
// Since we know aAvailable >= total + headerLen, this can't overflow.
|
||||
total += headerLen;
|
||||
}
|
||||
aHeaderLens.AppendElement(aAvailable);
|
||||
for (int i = 0; i < nHeaders; i++) {
|
||||
aHeaders.AppendElement(aData);
|
||||
aData += aHeaderLens[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,28 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
#if !defined(XiphExtradata_h)
|
||||
#define XiphExtradata_h
|
||||
|
||||
#include "MediaData.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/* This converts a list of headers to the canonical form of extradata for Xiph
|
||||
codecs in non-Ogg containers. We use it to pass those headers from demuxer
|
||||
to decoder even when demuxing from an Ogg cotainer. */
|
||||
bool XiphHeadersToExtradata(MediaByteBuffer* aCodecSpecificConfig,
|
||||
const nsTArray<const unsigned char*>& aHeaders,
|
||||
const nsTArray<size_t>& aHeaderLens);
|
||||
|
||||
/* This converts a set of extradata back into a list of headers. */
|
||||
bool XiphExtradataToHeaders(nsTArray<unsigned char*>& aHeaders,
|
||||
nsTArray<size_t>& aHeaderLens,
|
||||
unsigned char* aData,
|
||||
size_t aAvailable);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // XiphExtradata_h
|
|
@ -6,6 +6,7 @@
|
|||
#include "gtest/gtest.h"
|
||||
|
||||
#include "mozilla/TaskQueue.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
|
@ -294,3 +295,29 @@ TEST(MediaEventSource, CopyEvent2)
|
|||
listener1.Disconnect();
|
||||
listener2.Disconnect();
|
||||
}
|
||||
|
||||
/*
|
||||
* Test move-only types.
|
||||
*/
|
||||
TEST(MediaEventSource, MoveOnly)
|
||||
{
|
||||
nsRefPtr<TaskQueue> queue = new TaskQueue(
|
||||
GetMediaThreadPool(MediaThreadType::PLAYBACK));
|
||||
|
||||
MediaEventProducer<UniquePtr<int>, ListenerMode::Exclusive> source;
|
||||
|
||||
auto func = [] (UniquePtr<int>&& aEvent) {
|
||||
EXPECT_EQ(*aEvent, 20);
|
||||
};
|
||||
MediaEventListener listener = source.Connect(queue, func);
|
||||
|
||||
// It is OK to pass an rvalue which is move-only.
|
||||
source.Notify(UniquePtr<int>(new int(20)));
|
||||
// It is an error to pass an lvalue which is move-only.
|
||||
// UniquePtr<int> event(new int(30));
|
||||
// source.Notify(event);
|
||||
|
||||
queue->BeginShutdown();
|
||||
queue->AwaitShutdownAndIdle();
|
||||
listener.Disconnect();
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/ErrorResult.h"
|
||||
#include "mp4_demuxer/MoofParser.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "MediaData.h"
|
||||
#ifdef MOZ_FMP4
|
||||
#include "MP4Stream.h"
|
||||
|
@ -177,16 +178,6 @@ public:
|
|||
(*aData)[3] == 0x6b) {
|
||||
return true;
|
||||
}
|
||||
// 0xa3 // SimpleBlock
|
||||
if (aData->Length() >= 1 &&
|
||||
(*aData)[0] == 0xa3) {
|
||||
return true;
|
||||
}
|
||||
// 0xa1 // Block
|
||||
if (aData->Length() >= 1 &&
|
||||
(*aData)[0] == 0xa1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -194,6 +185,19 @@ public:
|
|||
int64_t& aStart, int64_t& aEnd) override
|
||||
{
|
||||
bool initSegment = IsInitSegmentPresent(aData);
|
||||
|
||||
if (mLastMapping && (initSegment || IsMediaSegmentPresent(aData))) {
|
||||
// The last data contained a complete cluster but we can only detect it
|
||||
// now that a new one is starting.
|
||||
// We use mOffset as end position to ensure that any blocks not reported
|
||||
// by WebMBufferParser are properly skipped.
|
||||
mCompleteMediaSegmentRange = MediaByteRange(mLastMapping.ref().mSyncOffset,
|
||||
mOffset);
|
||||
mLastMapping.reset();
|
||||
MSE_DEBUG(WebMContainerParser, "New cluster found at start, ending previous one");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (initSegment) {
|
||||
mOffset = 0;
|
||||
mParser = WebMBufferedParser(0);
|
||||
|
@ -242,38 +246,66 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
// Calculate media range for first media segment.
|
||||
|
||||
// Check if we have a cluster finishing in the current data.
|
||||
uint32_t endIdx = mapping.Length() - 1;
|
||||
|
||||
// Calculate media range for first media segment
|
||||
uint32_t segmentEndIdx = endIdx;
|
||||
while (mapping[0].mSyncOffset != mapping[segmentEndIdx].mSyncOffset) {
|
||||
segmentEndIdx -= 1;
|
||||
}
|
||||
if (segmentEndIdx > 0 && mOffset >= mapping[segmentEndIdx].mEndOffset) {
|
||||
mCompleteMediaHeaderRange = MediaByteRange(mParser.mInitEndOffset,
|
||||
mapping[0].mEndOffset);
|
||||
mCompleteMediaSegmentRange = MediaByteRange(mParser.mInitEndOffset,
|
||||
mapping[segmentEndIdx].mEndOffset);
|
||||
}
|
||||
|
||||
// Exclude frames that we don't have enough data to cover the end of.
|
||||
while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) {
|
||||
bool foundNewCluster = false;
|
||||
while (mapping[0].mSyncOffset != mapping[endIdx].mSyncOffset) {
|
||||
endIdx -= 1;
|
||||
foundNewCluster = true;
|
||||
}
|
||||
|
||||
if (endIdx == 0) {
|
||||
int32_t completeIdx = endIdx;
|
||||
while (completeIdx >= 0 && mOffset < mapping[completeIdx].mEndOffset) {
|
||||
MSE_DEBUG(WebMContainerParser, "block is incomplete, missing: %lld",
|
||||
mapping[completeIdx].mEndOffset - mOffset);
|
||||
completeIdx -= 1;
|
||||
}
|
||||
|
||||
// Save parsed blocks for which we do not have all data yet.
|
||||
mOverlappedMapping.AppendElements(mapping.Elements() + completeIdx + 1,
|
||||
mapping.Length() - completeIdx - 1);
|
||||
|
||||
if (completeIdx < 0) {
|
||||
mLastMapping.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode;
|
||||
if (mCompleteMediaHeaderRange.IsNull()) {
|
||||
mCompleteMediaHeaderRange = MediaByteRange(mapping[0].mSyncOffset,
|
||||
mapping[0].mEndOffset);
|
||||
}
|
||||
mLastMapping = Some(mapping[completeIdx]);
|
||||
|
||||
if (foundNewCluster && mOffset >= mapping[endIdx].mEndOffset) {
|
||||
// We now have all information required to delimit a complete cluster.
|
||||
int64_t endOffset = mapping[endIdx+1].mSyncOffset;
|
||||
if (mapping[endIdx+1].mInitOffset > mapping[endIdx].mInitOffset) {
|
||||
// We have a new init segment before this cluster.
|
||||
endOffset = mapping[endIdx+1].mInitOffset;
|
||||
}
|
||||
mCompleteMediaSegmentRange = MediaByteRange(mapping[endIdx].mSyncOffset,
|
||||
endOffset);
|
||||
} else if (mapping[endIdx].mClusterEndOffset >= 0 &&
|
||||
mOffset >= mapping[endIdx].mClusterEndOffset) {
|
||||
mCompleteMediaSegmentRange = MediaByteRange(mapping[endIdx].mSyncOffset,
|
||||
mParser.EndSegmentOffset(mapping[endIdx].mClusterEndOffset));
|
||||
}
|
||||
|
||||
if (!completeIdx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t frameDuration =
|
||||
mapping[completeIdx].mTimecode - mapping[completeIdx - 1].mTimecode;
|
||||
aStart = mapping[0].mTimecode / NS_PER_USEC;
|
||||
aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC;
|
||||
aEnd = (mapping[completeIdx].mTimecode + frameDuration) / NS_PER_USEC;
|
||||
|
||||
MSE_DEBUG(WebMContainerParser, "[%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
|
||||
aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
|
||||
|
||||
mapping.RemoveElementsAt(0, endIdx + 1);
|
||||
mOverlappedMapping.AppendElements(mapping);
|
||||
MSE_DEBUG(WebMContainerParser, "[%lld, %lld] [fso=%lld, leo=%lld, l=%u processedIdx=%u fs=%lld]",
|
||||
aStart, aEnd, mapping[0].mSyncOffset,
|
||||
mapping[completeIdx].mEndOffset, mapping.Length(), completeIdx,
|
||||
mCompleteMediaSegmentRange.mEnd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -288,6 +320,7 @@ private:
|
|||
WebMBufferedParser mParser;
|
||||
nsTArray<WebMTimeDataOffset> mOverlappedMapping;
|
||||
int64_t mOffset;
|
||||
Maybe<WebMTimeDataOffset> mLastMapping;
|
||||
};
|
||||
|
||||
#ifdef MOZ_FMP4
|
||||
|
|
|
@ -100,6 +100,11 @@ public:
|
|||
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
virtual bool IsExpectingMoreData() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Used by SourceBuffer.
|
||||
void AppendData(MediaByteBuffer* aData);
|
||||
void Ended();
|
||||
|
|
|
@ -97,6 +97,7 @@ TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttribute
|
|||
, mAppendState(AppendState::WAITING_FOR_SEGMENT)
|
||||
, mBufferFull(false)
|
||||
, mFirstInitializationSegmentReceived(false)
|
||||
, mNewSegmentStarted(false)
|
||||
, mActiveTrack(false)
|
||||
, mType(aType)
|
||||
, mParser(ContainerParser::CreateForMIMEType(aType))
|
||||
|
@ -655,10 +656,12 @@ TrackBuffersManager::SegmentParserLoop()
|
|||
// This is a new initialization segment. Obsolete the old one.
|
||||
RecreateParser(false);
|
||||
}
|
||||
mNewSegmentStarted = true;
|
||||
continue;
|
||||
}
|
||||
if (mParser->IsMediaSegmentPresent(mInputBuffer)) {
|
||||
SetAppendState(AppendState::PARSING_MEDIA_SEGMENT);
|
||||
mNewSegmentStarted = true;
|
||||
continue;
|
||||
}
|
||||
// We have neither an init segment nor a media segment, this is either
|
||||
|
@ -669,7 +672,7 @@ TrackBuffersManager::SegmentParserLoop()
|
|||
}
|
||||
|
||||
int64_t start, end;
|
||||
mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
|
||||
bool newData = mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
|
||||
mProcessedInput += mInputBuffer->Length();
|
||||
|
||||
// 5. If the append state equals PARSING_INIT_SEGMENT, then run the
|
||||
|
@ -696,6 +699,22 @@ TrackBuffersManager::SegmentParserLoop()
|
|||
NeedMoreData();
|
||||
return;
|
||||
}
|
||||
|
||||
// We can't feed some demuxers (WebMDemuxer) with data that do not have
|
||||
// monotonizally increasing timestamps. So we check if we have a
|
||||
// discontinuity from the previous segment parsed.
|
||||
// If so, recreate a new demuxer to ensure that the demuxer is only fed
|
||||
// monotonically increasing data.
|
||||
if (newData) {
|
||||
if (mNewSegmentStarted && mLastParsedEndTime.isSome() &&
|
||||
start < mLastParsedEndTime.ref().ToMicroseconds()) {
|
||||
ResetDemuxingState();
|
||||
return;
|
||||
}
|
||||
mNewSegmentStarted = false;
|
||||
mLastParsedEndTime = Some(TimeUnit::FromMicroseconds(end));
|
||||
}
|
||||
|
||||
// 3. If the input buffer contains one or more complete coded frames, then run the coded frame processing algorithm.
|
||||
nsRefPtr<TrackBuffersManager> self = this;
|
||||
mProcessingRequest.Begin(CodedFrameProcessing()
|
||||
|
@ -756,6 +775,7 @@ TrackBuffersManager::ShutdownDemuxers()
|
|||
mAudioTracks.mDemuxer = nullptr;
|
||||
}
|
||||
mInputDemuxer = nullptr;
|
||||
mLastParsedEndTime.reset();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -780,6 +800,58 @@ TrackBuffersManager::CreateDemuxerforMIMEType()
|
|||
return;
|
||||
}
|
||||
|
||||
// We reset the demuxer by creating a new one and initializing it.
|
||||
void
|
||||
TrackBuffersManager::ResetDemuxingState()
|
||||
{
|
||||
MOZ_ASSERT(mParser && mParser->HasInitData());
|
||||
RecreateParser(true);
|
||||
mCurrentInputBuffer = new SourceBufferResource(mType);
|
||||
// The demuxer isn't initialized yet ; we don't want to notify it
|
||||
// that data has been appended yet ; so we simply append the init segment
|
||||
// to the resource.
|
||||
mCurrentInputBuffer->AppendData(mParser->InitData());
|
||||
CreateDemuxerforMIMEType();
|
||||
if (!mInputDemuxer) {
|
||||
RejectAppend(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
mDemuxerInitRequest.Begin(mInputDemuxer->Init()
|
||||
->Then(GetTaskQueue(), __func__,
|
||||
this,
|
||||
&TrackBuffersManager::OnDemuxerResetDone,
|
||||
&TrackBuffersManager::OnDemuxerInitFailed));
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffersManager::OnDemuxerResetDone(nsresult)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
mDemuxerInitRequest.Complete();
|
||||
if (mAbort) {
|
||||
RejectAppend(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Recreate track demuxers.
|
||||
uint32_t numVideos = mInputDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
|
||||
if (numVideos) {
|
||||
// We currently only handle the first video track.
|
||||
mVideoTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
|
||||
MOZ_ASSERT(mVideoTracks.mDemuxer);
|
||||
}
|
||||
|
||||
uint32_t numAudios = mInputDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
|
||||
if (numAudios) {
|
||||
// We currently only handle the first audio track.
|
||||
mAudioTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
|
||||
MOZ_ASSERT(mAudioTracks.mDemuxer);
|
||||
}
|
||||
|
||||
SegmentParserLoop();
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffersManager::AppendDataToCurrentInputBuffer(MediaByteBuffer* aData)
|
||||
{
|
||||
|
@ -1056,6 +1128,14 @@ TrackBuffersManager::CodedFrameProcessing()
|
|||
// The mediaRange is offset by the init segment position previously added.
|
||||
uint32_t length =
|
||||
mediaRange.mEnd - (mProcessedInput - mInputBuffer->Length());
|
||||
if (!length) {
|
||||
// We've completed our earlier media segment and no new data is to be
|
||||
// processed. This happens with some containers that can't detect that a
|
||||
// media segment is ending until a new one starts.
|
||||
nsRefPtr<CodedFrameProcessingPromise> p = mProcessingPromise.Ensure(__func__);
|
||||
CompleteCodedFrameProcessing();
|
||||
return p;
|
||||
}
|
||||
nsRefPtr<MediaByteBuffer> segment = new MediaByteBuffer;
|
||||
if (!segment->AppendElements(mInputBuffer->Elements(), length, fallible)) {
|
||||
return CodedFrameProcessingPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||
|
|
|
@ -113,6 +113,7 @@ private:
|
|||
void InitializationSegmentReceived();
|
||||
void ShutdownDemuxers();
|
||||
void CreateDemuxerforMIMEType();
|
||||
void ResetDemuxingState();
|
||||
void NeedMoreData();
|
||||
void RejectAppend(nsresult aRejectValue, const char* aName);
|
||||
// Will return a promise that will be resolved once all frames of the current
|
||||
|
@ -151,6 +152,8 @@ private:
|
|||
// TODO: Unused for now.
|
||||
Atomic<bool> mBufferFull;
|
||||
bool mFirstInitializationSegmentReceived;
|
||||
// Set to true once a new segment is started.
|
||||
bool mNewSegmentStarted;
|
||||
bool mActiveTrack;
|
||||
Maybe<media::TimeUnit> mGroupStartTimestamp;
|
||||
media::TimeUnit mGroupEndTimestamp;
|
||||
|
@ -171,9 +174,11 @@ private:
|
|||
nsRefPtr<MediaDataDemuxer> mInputDemuxer;
|
||||
// Length already processed in current media segment.
|
||||
uint32_t mProcessedInput;
|
||||
Maybe<media::TimeUnit> mLastParsedEndTime;
|
||||
|
||||
void OnDemuxerInitDone(nsresult);
|
||||
void OnDemuxerInitFailed(DemuxerFailureReason aFailure);
|
||||
void OnDemuxerResetDone(nsresult);
|
||||
MozPromiseRequestHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
|
||||
bool mEncrypted;
|
||||
|
||||
|
|
|
@ -149,6 +149,7 @@ EXPORTS += [
|
|||
'VideoSegment.h',
|
||||
'VideoUtils.h',
|
||||
'VorbisUtils.h',
|
||||
'XiphExtradata.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
|
@ -245,6 +246,7 @@ UNIFIED_SOURCES += [
|
|||
'VideoTrackList.cpp',
|
||||
'VideoUtils.cpp',
|
||||
'WebVTTListener.cpp',
|
||||
'XiphExtradata.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'WINNT':
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "VorbisDecoder.h"
|
||||
#include "VorbisUtils.h"
|
||||
#include "XiphExtradata.h"
|
||||
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
@ -71,23 +72,17 @@ VorbisDataDecoder::Init()
|
|||
PodZero(&mVorbisDsp);
|
||||
PodZero(&mVorbisBlock);
|
||||
|
||||
size_t available = mInfo.mCodecSpecificConfig->Length();
|
||||
uint8_t *p = mInfo.mCodecSpecificConfig->Elements();
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if (available < 2) {
|
||||
nsAutoTArray<unsigned char*,4> headers;
|
||||
nsAutoTArray<size_t,4> headerLens;
|
||||
if (!XiphExtradataToHeaders(headers, headerLens,
|
||||
mInfo.mCodecSpecificConfig->Elements(),
|
||||
mInfo.mCodecSpecificConfig->Length())) {
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
for (size_t i = 0; i < headers.Length(); i++) {
|
||||
if (NS_FAILED(DecodeHeader(headers[i], headerLens[i]))) {
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
available -= 2;
|
||||
size_t length = BigEndian::readUint16(p);
|
||||
p += 2;
|
||||
if (available < length) {
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
available -= length;
|
||||
if (NS_FAILED(DecodeHeader((const unsigned char*)p, length))) {
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
}
|
||||
p += length;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mPacketCount == 3);
|
||||
|
|
|
@ -89,7 +89,7 @@ GonkVideoDecoderManager::Init(MediaDataDecoderCallback* aCallback)
|
|||
mReaderCallback = aCallback;
|
||||
|
||||
mReaderTaskQueue = AbstractThread::GetCurrent()->AsTaskQueue();
|
||||
MOZ_ASSERT(!mReaderTaskQueue);
|
||||
MOZ_ASSERT(mReaderTaskQueue);
|
||||
|
||||
if (mLooper.get() != nullptr) {
|
||||
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
|
||||
|
|
|
@ -34,6 +34,7 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
|||
nsTArray<WebMTimeDataOffset>& aMapping,
|
||||
ReentrantMonitor& aReentrantMonitor)
|
||||
{
|
||||
static const uint32_t EBML_ID = 0x1a45dfa3;
|
||||
static const uint32_t SEGMENT_ID = 0x18538067;
|
||||
static const uint32_t SEGINFO_ID = 0x1549a966;
|
||||
static const uint32_t TRACKS_ID = 0x1654AE6B;
|
||||
|
@ -102,6 +103,12 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
|||
case CLUSTER_ID:
|
||||
mClusterOffset = mCurrentOffset + (p - aBuffer) -
|
||||
(mElement.mID.mLength + mElement.mSize.mLength);
|
||||
// Handle "unknown" length;
|
||||
if (mElement.mSize.mValue + 1 != uint64_t(1) << (mElement.mSize.mLength * 7)) {
|
||||
mClusterEndOffset = mClusterOffset + mElement.mID.mLength + mElement.mSize.mLength + mElement.mSize.mValue;
|
||||
} else {
|
||||
mClusterEndOffset = -1;
|
||||
}
|
||||
mState = READ_ELEMENT_ID;
|
||||
break;
|
||||
case SIMPLEBLOCK_ID:
|
||||
|
@ -119,6 +126,10 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
|||
mSkipBytes = mElement.mSize.mValue;
|
||||
mState = CHECK_INIT_FOUND;
|
||||
break;
|
||||
case EBML_ID:
|
||||
mLastInitStartOffset = mCurrentOffset + (p - aBuffer) -
|
||||
(mElement.mID.mLength + mElement.mSize.mLength);
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
mSkipBytes = mElement.mSize.mValue;
|
||||
mState = SKIP_DATA;
|
||||
|
@ -172,7 +183,8 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
|||
MOZ_ASSERT(mGotTimecodeScale);
|
||||
uint64_t absTimecode = mClusterTimecode + mBlockTimecode;
|
||||
absTimecode *= mTimecodeScale;
|
||||
WebMTimeDataOffset entry(endOffset, absTimecode, mClusterOffset);
|
||||
WebMTimeDataOffset entry(endOffset, absTimecode, mLastInitStartOffset,
|
||||
mClusterOffset, mClusterEndOffset);
|
||||
aMapping.InsertElementAt(idx, entry);
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +204,9 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
|||
left = std::min(left, mSkipBytes);
|
||||
p += left;
|
||||
mSkipBytes -= left;
|
||||
} else {
|
||||
}
|
||||
if (!mSkipBytes) {
|
||||
mBlockEndOffset = mCurrentOffset + (p - aBuffer);
|
||||
mState = mNextState;
|
||||
}
|
||||
break;
|
||||
|
@ -206,6 +220,7 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
|||
if (!mSkipBytes) {
|
||||
if (mInitEndOffset < 0) {
|
||||
mInitEndOffset = mCurrentOffset + (p - aBuffer);
|
||||
mBlockEndOffset = mCurrentOffset + (p - aBuffer);
|
||||
}
|
||||
mState = READ_ELEMENT_ID;
|
||||
}
|
||||
|
@ -217,6 +232,16 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
|||
mCurrentOffset += aLength;
|
||||
}
|
||||
|
||||
int64_t
|
||||
WebMBufferedParser::EndSegmentOffset(int64_t aOffset)
|
||||
{
|
||||
if (mLastInitStartOffset > aOffset || mClusterOffset > aOffset) {
|
||||
return std::min(mLastInitStartOffset >= 0 ? mLastInitStartOffset : INT64_MAX,
|
||||
mClusterOffset >= 0 ? mClusterOffset : INT64_MAX);
|
||||
}
|
||||
return mBlockEndOffset;
|
||||
}
|
||||
|
||||
// SyncOffsetComparator and TimeComparator are slightly confusing, in that
|
||||
// the nsTArray they're used with (mTimeMapping) is sorted by mEndOffset and
|
||||
// these comparators are used on the other fields of WebMTimeDataOffset.
|
||||
|
@ -344,6 +369,13 @@ void WebMBufferedState::NotifyDataArrived(const unsigned char* aBuffer, uint32_t
|
|||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (mRangeParsers.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
mLastBlockOffset = mRangeParsers.LastElement().mBlockEndOffset;
|
||||
}
|
||||
|
||||
void WebMBufferedState::Reset() {
|
||||
|
@ -398,6 +430,13 @@ int64_t WebMBufferedState::GetInitEndOffset()
|
|||
return mRangeParsers[0].mInitEndOffset;
|
||||
}
|
||||
|
||||
int64_t WebMBufferedState::GetLastBlockOffset()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
return mLastBlockOffset;
|
||||
}
|
||||
|
||||
bool WebMBufferedState::GetStartTime(uint64_t *aTime)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
|
|
@ -17,8 +17,14 @@ namespace mozilla {
|
|||
// that offset.
|
||||
struct WebMTimeDataOffset
|
||||
{
|
||||
WebMTimeDataOffset(int64_t aEndOffset, uint64_t aTimecode, int64_t aSyncOffset)
|
||||
: mEndOffset(aEndOffset), mSyncOffset(aSyncOffset), mTimecode(aTimecode)
|
||||
WebMTimeDataOffset(int64_t aEndOffset, uint64_t aTimecode,
|
||||
int64_t aInitOffset, int64_t aSyncOffset,
|
||||
int64_t aClusterEndOffset)
|
||||
: mEndOffset(aEndOffset)
|
||||
, mInitOffset(aInitOffset)
|
||||
, mSyncOffset(aSyncOffset)
|
||||
, mClusterEndOffset(aClusterEndOffset)
|
||||
, mTimecode(aTimecode)
|
||||
{}
|
||||
|
||||
bool operator==(int64_t aEndOffset) const {
|
||||
|
@ -34,7 +40,9 @@ struct WebMTimeDataOffset
|
|||
}
|
||||
|
||||
int64_t mEndOffset;
|
||||
int64_t mInitOffset;
|
||||
int64_t mSyncOffset;
|
||||
int64_t mClusterEndOffset;
|
||||
uint64_t mTimecode;
|
||||
};
|
||||
|
||||
|
@ -47,9 +55,17 @@ struct WebMTimeDataOffset
|
|||
struct WebMBufferedParser
|
||||
{
|
||||
explicit WebMBufferedParser(int64_t aOffset)
|
||||
: mStartOffset(aOffset), mCurrentOffset(aOffset), mInitEndOffset(-1),
|
||||
mState(READ_ELEMENT_ID), mVIntRaw(false), mClusterSyncPos(0),
|
||||
mTimecodeScale(1000000), mGotTimecodeScale(false)
|
||||
: mStartOffset(aOffset)
|
||||
, mCurrentOffset(aOffset)
|
||||
, mInitEndOffset(-1)
|
||||
, mBlockEndOffset(-1)
|
||||
, mState(READ_ELEMENT_ID)
|
||||
, mVIntRaw(false)
|
||||
, mLastInitStartOffset(-1)
|
||||
, mClusterSyncPos(0)
|
||||
, mClusterEndOffset(-1)
|
||||
, mTimecodeScale(1000000)
|
||||
, mGotTimecodeScale(false)
|
||||
{
|
||||
if (mStartOffset != 0) {
|
||||
mState = FIND_CLUSTER_SYNC;
|
||||
|
@ -83,19 +99,28 @@ struct WebMBufferedParser
|
|||
return mCurrentOffset < aOffset;
|
||||
}
|
||||
|
||||
// Returns the start offset of the init (EBML) or media segment (Cluster)
|
||||
// following the aOffset position. If none were found, returns mBlockEndOffset.
|
||||
// This allows to determine the end of the interval containg aOffset.
|
||||
int64_t EndSegmentOffset(int64_t aOffset);
|
||||
|
||||
// The offset at which this parser started parsing. Used to merge
|
||||
// adjacent parsers, in which case the later parser adopts the earlier
|
||||
// parser's mStartOffset.
|
||||
int64_t mStartOffset;
|
||||
|
||||
// Current offset with the stream. Updated in chunks as Append() consumes
|
||||
// Current offset within the stream. Updated in chunks as Append() consumes
|
||||
// data.
|
||||
int64_t mCurrentOffset;
|
||||
|
||||
// Tracks element's end offset. This indicates the end of the init segment.
|
||||
// Will only be set if a Segment Information has been found.
|
||||
// Tracks element's end offset. This indicates the end of the first init
|
||||
// segment. Will only be set if a Segment Information has been found.
|
||||
int64_t mInitEndOffset;
|
||||
|
||||
// End offset of the last block parsed.
|
||||
// Will only be set if a complete block has been parsed.
|
||||
int64_t mBlockEndOffset;
|
||||
|
||||
private:
|
||||
enum State {
|
||||
// Parser start state. Expects to begin at a valid EBML element. Move
|
||||
|
@ -175,6 +200,10 @@ private:
|
|||
|
||||
bool mVIntRaw;
|
||||
|
||||
// EBML start offset. This indicates the start of the last init segment
|
||||
// parsed. Will only be set if an EBML element has been found.
|
||||
int64_t mLastInitStartOffset;
|
||||
|
||||
// Current match position within CLUSTER_SYNC_ID. Used to find sync
|
||||
// within arbitrary data.
|
||||
uint32_t mClusterSyncPos;
|
||||
|
@ -195,6 +224,9 @@ private:
|
|||
// been parsed.
|
||||
int64_t mClusterOffset;
|
||||
|
||||
// End offset of the cluster currently being parsed. -1 if unknown.
|
||||
int64_t mClusterEndOffset;
|
||||
|
||||
// Start offset of the block currently being parsed. Used as the byte
|
||||
// offset for the offset-to-time mapping once the block timecode has been
|
||||
// parsed.
|
||||
|
@ -225,7 +257,10 @@ class WebMBufferedState final
|
|||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebMBufferedState)
|
||||
|
||||
public:
|
||||
WebMBufferedState() : mReentrantMonitor("WebMBufferedState") {
|
||||
WebMBufferedState()
|
||||
: mReentrantMonitor("WebMBufferedState")
|
||||
, mLastBlockOffset(-1)
|
||||
{
|
||||
MOZ_COUNT_CTOR(WebMBufferedState);
|
||||
}
|
||||
|
||||
|
@ -242,6 +277,8 @@ public:
|
|||
|
||||
// Returns end offset of init segment or -1 if none found.
|
||||
int64_t GetInitEndOffset();
|
||||
// Returns the end offset of the last complete block or -1 if none found.
|
||||
int64_t GetLastBlockOffset();
|
||||
|
||||
// Returns start time
|
||||
bool GetStartTime(uint64_t *aTime);
|
||||
|
@ -255,12 +292,14 @@ private:
|
|||
MOZ_COUNT_DTOR(WebMBufferedState);
|
||||
}
|
||||
|
||||
// Synchronizes access to the mTimeMapping array.
|
||||
// Synchronizes access to the mTimeMapping array and mLastBlockOffset.
|
||||
ReentrantMonitor mReentrantMonitor;
|
||||
|
||||
// Sorted (by offset) map of data offsets to timecodes. Populated
|
||||
// on the main thread as data is received and parsed by WebMBufferedParsers.
|
||||
nsTArray<WebMTimeDataOffset> mTimeMapping;
|
||||
// The last complete block parsed. -1 if not set.
|
||||
int64_t mLastBlockOffset;
|
||||
|
||||
// Sorted (by offset) live parser instances. Main thread only.
|
||||
nsTArray<WebMBufferedParser> mRangeParsers;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "MediaDataDemuxer.h"
|
||||
#include "nsAutoRef.h"
|
||||
#include "NesteggPacketHolder.h"
|
||||
#include "XiphExtradata.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdint.h>
|
||||
|
@ -35,40 +36,39 @@ extern PRLogModuleInfo* gNesteggLog;
|
|||
|
||||
// Functions for reading and seeking using WebMDemuxer required for
|
||||
// nestegg_io. The 'user data' passed to these functions is the
|
||||
// demuxer's MediaResourceIndex
|
||||
// demuxer.
|
||||
static int webmdemux_read(void* aBuffer, size_t aLength, void* aUserData)
|
||||
{
|
||||
MOZ_ASSERT(aUserData);
|
||||
MediaResourceIndex* resource =
|
||||
reinterpret_cast<MediaResourceIndex*>(aUserData);
|
||||
int64_t length = resource->GetLength();
|
||||
MOZ_ASSERT(aLength < UINT32_MAX);
|
||||
WebMDemuxer* demuxer = reinterpret_cast<WebMDemuxer*>(aUserData);
|
||||
int64_t length = demuxer->GetEndDataOffset();
|
||||
uint32_t count = aLength;
|
||||
if (length >= 0 && count + resource->Tell() > length) {
|
||||
count = uint32_t(length - resource->Tell());
|
||||
int64_t position = demuxer->GetResource()->Tell();
|
||||
if (length >= 0 && count + position > length) {
|
||||
count = length - position;
|
||||
}
|
||||
|
||||
uint32_t bytes = 0;
|
||||
nsresult rv = resource->Read(static_cast<char*>(aBuffer), count, &bytes);
|
||||
bool eof = !bytes;
|
||||
nsresult rv =
|
||||
demuxer->GetResource()->Read(static_cast<char*>(aBuffer), count, &bytes);
|
||||
bool eof = bytes < aLength;
|
||||
return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
|
||||
}
|
||||
|
||||
static int webmdemux_seek(int64_t aOffset, int aWhence, void* aUserData)
|
||||
{
|
||||
MOZ_ASSERT(aUserData);
|
||||
MediaResourceIndex* resource =
|
||||
reinterpret_cast<MediaResourceIndex*>(aUserData);
|
||||
nsresult rv = resource->Seek(aWhence, aOffset);
|
||||
WebMDemuxer* demuxer = reinterpret_cast<WebMDemuxer*>(aUserData);
|
||||
nsresult rv = demuxer->GetResource()->Seek(aWhence, aOffset);
|
||||
return NS_SUCCEEDED(rv) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int64_t webmdemux_tell(void* aUserData)
|
||||
{
|
||||
MOZ_ASSERT(aUserData);
|
||||
MediaResourceIndex* resource =
|
||||
reinterpret_cast<MediaResourceIndex*>(aUserData);
|
||||
return resource->Tell();
|
||||
WebMDemuxer* demuxer = reinterpret_cast<WebMDemuxer*>(aUserData);
|
||||
return demuxer->GetResource()->Tell();
|
||||
}
|
||||
|
||||
static void webmdemux_log(nestegg* aContext,
|
||||
|
@ -122,12 +122,15 @@ WebMDemuxer::WebMDemuxer(MediaResource* aResource)
|
|||
, mVideoTrack(0)
|
||||
, mAudioTrack(0)
|
||||
, mSeekPreroll(0)
|
||||
, mLastAudioFrameTime(0)
|
||||
, mLastVideoFrameTime(0)
|
||||
, mAudioCodec(-1)
|
||||
, mVideoCodec(-1)
|
||||
, mHasVideo(false)
|
||||
, mHasAudio(false)
|
||||
, mNeedReIndex(true)
|
||||
, mLastWebMBlockOffset(-1)
|
||||
, mIsExpectingMoreData(true)
|
||||
{
|
||||
if (!gNesteggLog) {
|
||||
gNesteggLog = PR_NewLogModule("Nestegg");
|
||||
|
@ -252,7 +255,7 @@ WebMDemuxer::ReadMetadata()
|
|||
io.read = webmdemux_read;
|
||||
io.seek = webmdemux_seek;
|
||||
io.tell = webmdemux_tell;
|
||||
io.userdata = &mResource;
|
||||
io.userdata = this;
|
||||
int64_t maxOffset = mBufferedState->GetInitEndOffset();
|
||||
if (maxOffset == -1) {
|
||||
maxOffset = mResource.GetLength();
|
||||
|
@ -378,6 +381,8 @@ WebMDemuxer::ReadMetadata()
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoTArray<const unsigned char*,4> headers;
|
||||
nsAutoTArray<size_t,4> headerLens;
|
||||
for (uint32_t header = 0; header < nheaders; ++header) {
|
||||
unsigned char* data = 0;
|
||||
size_t length = 0;
|
||||
|
@ -385,13 +390,24 @@ WebMDemuxer::ReadMetadata()
|
|||
if (r == -1) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// Vorbis has 3 headers write length + data for each header
|
||||
if (nheaders > 1) {
|
||||
uint8_t c[2];
|
||||
BigEndian::writeUint16(&c[0], length);
|
||||
mInfo.mAudio.mCodecSpecificConfig->AppendElements(&c[0], 2);
|
||||
headers.AppendElement(data);
|
||||
headerLens.AppendElement(length);
|
||||
}
|
||||
|
||||
// Vorbis has 3 headers, convert to Xiph extradata format to send them to
|
||||
// the demuxer.
|
||||
// TODO: This is already the format WebM stores them in. Would be nice
|
||||
// to avoid having libnestegg split them only for us to pack them again,
|
||||
// but libnestegg does not give us an API to access this data directly.
|
||||
if (nheaders > 1) {
|
||||
if (!XiphHeadersToExtradata(mInfo.mAudio.mCodecSpecificConfig,
|
||||
headers, headerLens)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mInfo.mAudio.mCodecSpecificConfig->AppendElements(data, length);
|
||||
}
|
||||
else {
|
||||
mInfo.mAudio.mCodecSpecificConfig->AppendElements(headers[0],
|
||||
headerLens[0]);
|
||||
}
|
||||
uint64_t duration = 0;
|
||||
r = nestegg_duration(mContext, &duration);
|
||||
|
@ -428,6 +444,8 @@ WebMDemuxer::EnsureUpToDateIndex()
|
|||
if (!mInitData && mBufferedState->GetInitEndOffset() != -1) {
|
||||
mInitData = mResource.MediaReadAt(0, mBufferedState->GetInitEndOffset());
|
||||
}
|
||||
mLastWebMBlockOffset = mBufferedState->GetLastBlockOffset();
|
||||
mIsExpectingMoreData = mResource.GetResource()->IsExpectingMoreData();
|
||||
mNeedReIndex = false;
|
||||
}
|
||||
|
||||
|
@ -454,6 +472,8 @@ WebMDemuxer::GetCrypto()
|
|||
bool
|
||||
WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSamples)
|
||||
{
|
||||
EnsureUpToDateIndex();
|
||||
|
||||
nsRefPtr<NesteggPacketHolder> holder(NextPacket(aType));
|
||||
|
||||
if (!holder) {
|
||||
|
@ -689,6 +709,10 @@ WebMDemuxer::SeekInternal(const media::TimeUnit& aTarget)
|
|||
}
|
||||
WEBM_DEBUG("got offset from buffered state: %" PRIu64 "", offset);
|
||||
}
|
||||
|
||||
mLastAudioFrameTime = 0;
|
||||
mLastVideoFrameTime = 0;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,18 @@ public:
|
|||
// Pushes a packet to the front of the video packet queue.
|
||||
virtual void PushVideoPacket(NesteggPacketHolder* aItem);
|
||||
|
||||
// Public accessor for nestegg callbacks
|
||||
MediaResourceIndex* GetResource()
|
||||
{
|
||||
return &mResource;
|
||||
}
|
||||
|
||||
int64_t GetEndDataOffset()
|
||||
{
|
||||
return mLastWebMBlockOffset < 0 || mIsExpectingMoreData
|
||||
? mResource.GetLength() : mLastWebMBlockOffset;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class WebMTrackDemuxer;
|
||||
|
||||
|
@ -152,6 +164,12 @@ private:
|
|||
bool mHasVideo;
|
||||
bool mHasAudio;
|
||||
bool mNeedReIndex;
|
||||
|
||||
// The last complete block parsed by the WebMBufferedState. -1 if not set.
|
||||
// We cache those values rather than retrieving them for performance reasons
|
||||
// as nestegg only performs 1-byte read at a time.
|
||||
int64_t mLastWebMBlockOffset;
|
||||
bool mIsExpectingMoreData;
|
||||
};
|
||||
|
||||
class WebMTrackDemuxer : public MediaTrackDemuxer
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
extern "C" {
|
||||
#include "pocketsphinx/pocketsphinx.h"
|
||||
#include "sphinxbase/logmath.h"
|
||||
#include "sphinxbase/sphinx_config.h"
|
||||
#include "sphinxbase/jsgf.h"
|
||||
}
|
||||
|
@ -33,8 +34,10 @@ class DecodeResultTask : public nsRunnable
|
|||
{
|
||||
public:
|
||||
DecodeResultTask(const nsString& hypstring,
|
||||
float64 confidence,
|
||||
WeakPtr<dom::SpeechRecognition> recognition)
|
||||
: mResult(hypstring),
|
||||
mConfidence(confidence),
|
||||
mRecognition(recognition),
|
||||
mWorkerThread(do_GetCurrentThread())
|
||||
{
|
||||
|
@ -59,7 +62,7 @@ public:
|
|||
new SpeechRecognitionAlternative(mRecognition);
|
||||
|
||||
alternative->mTranscript = mResult;
|
||||
alternative->mConfidence = 100;
|
||||
alternative->mConfidence = mConfidence;
|
||||
|
||||
result->mItems.AppendElement(alternative);
|
||||
}
|
||||
|
@ -77,6 +80,7 @@ public:
|
|||
|
||||
private:
|
||||
nsString mResult;
|
||||
float64 mConfidence;
|
||||
WeakPtr<dom::SpeechRecognition> mRecognition;
|
||||
nsCOMPtr<nsIThread> mWorkerThread;
|
||||
};
|
||||
|
@ -95,7 +99,9 @@ public:
|
|||
{
|
||||
char const* hyp;
|
||||
int rv;
|
||||
int32 score;
|
||||
int32 final;
|
||||
int32 logprob;
|
||||
float64 confidence;
|
||||
nsAutoCString hypoValue;
|
||||
|
||||
rv = ps_start_utt(mPs);
|
||||
|
@ -103,15 +109,18 @@ public:
|
|||
FALSE);
|
||||
|
||||
rv = ps_end_utt(mPs);
|
||||
confidence = 0;
|
||||
if (rv >= 0) {
|
||||
hyp = ps_get_hyp(mPs, &score);
|
||||
if (hyp) {
|
||||
hyp = ps_get_hyp_final(mPs, &final);
|
||||
if (hyp && final) {
|
||||
logprob = ps_get_prob(mPs);
|
||||
confidence = logmath_exp(ps_get_logmath(mPs), logprob);
|
||||
hypoValue.Assign(hyp);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> resultrunnable =
|
||||
new DecodeResultTask(NS_ConvertUTF8toUTF16(hypoValue), mRecognition);
|
||||
new DecodeResultTask(NS_ConvertUTF8toUTF16(hypoValue), confidence, mRecognition);
|
||||
return NS_DispatchToMainThread(resultrunnable);
|
||||
}
|
||||
|
||||
|
@ -154,7 +163,7 @@ PocketSphinxSpeechRecognitionService::PocketSphinxSpeechRecognitionService()
|
|||
|
||||
// FOR B2G PATHS HARDCODED (APPEND /DATA ON THE BEGINING, FOR DESKTOP, ONLY
|
||||
// MODELS/ RELATIVE TO ROOT
|
||||
mPSConfig = cmd_ln_init(nullptr, ps_args(), TRUE, "-hmm",
|
||||
mPSConfig = cmd_ln_init(nullptr, ps_args(), TRUE, "-bestpath", "yes", "-hmm",
|
||||
ToNewUTF8String(aStringAMPath), // acoustic model
|
||||
"-dict", ToNewUTF8String(aStringDictPath), nullptr);
|
||||
if (mPSConfig == nullptr) {
|
||||
|
@ -268,11 +277,11 @@ PocketSphinxSpeechRecognitionService::ValidateAndSetGrammarList(
|
|||
int result = ps_set_jsgf_string(mPSHandle, "name",
|
||||
NS_ConvertUTF16toUTF8(grammar).get());
|
||||
|
||||
ps_set_search(mPSHandle, "name");
|
||||
|
||||
if (result != 0) {
|
||||
ISGrammarCompiled = false;
|
||||
} else {
|
||||
ps_set_search(mPSHandle, "name");
|
||||
|
||||
ISGrammarCompiled = true;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -236,8 +236,9 @@ let PaymentManager = {
|
|||
if (systemAppId != Ci.nsIScriptSecurityManager.NO_APP_ID) {
|
||||
this.LOG("Granting firefox-accounts permission to " + provider.uri);
|
||||
let uri = Services.io.newURI(provider.uri, null, null);
|
||||
let principal = Services.scriptSecurityManager
|
||||
.getAppCodebasePrincipal(uri, systemAppId, true);
|
||||
let attrs = {appId: systemAppId, inBrowser: true};
|
||||
let principal =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(uri, attrs);
|
||||
|
||||
Services.perms.addFromPrincipal(principal, "firefox-accounts",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
|
|
|
@ -35,10 +35,14 @@ XPCOMUtils.defineLazyServiceGetter(this,
|
|||
|
||||
PermissionSettings.prototype = {
|
||||
get: function get(aPermName, aManifestURL, aOrigin, aBrowserFlag) {
|
||||
// TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.js
|
||||
debug("Get called with: " + aPermName + ", " + aManifestURL + ", " + aOrigin + ", " + aBrowserFlag);
|
||||
let uri = Services.io.newURI(aOrigin, null, null);
|
||||
let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
|
||||
let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri, appID, aBrowserFlag);
|
||||
let principal =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(uri,
|
||||
{appId: appID,
|
||||
inBrowser: aBrowserFlag});
|
||||
let result = Services.perms.testExactPermanentPermission(principal, aPermName);
|
||||
|
||||
switch (result)
|
||||
|
@ -59,11 +63,12 @@ PermissionSettings.prototype = {
|
|||
|
||||
isExplicit: function isExplicit(aPermName, aManifestURL, aOrigin,
|
||||
aBrowserFlag) {
|
||||
// TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.js
|
||||
debug("isExplicit: " + aPermName + ", " + aManifestURL + ", " + aOrigin);
|
||||
let uri = Services.io.newURI(aOrigin, null, null);
|
||||
let app = appsService.getAppByManifestURL(aManifestURL);
|
||||
let principal = Services.scriptSecurityManager
|
||||
.getAppCodebasePrincipal(uri, app.localId, aBrowserFlag);
|
||||
.createCodebasePrincipal(uri, {appId: app.localId, inBrowser: aBrowserFlag});
|
||||
|
||||
return isExplicitInPermissionsTable(aPermName,
|
||||
principal.appStatus,
|
||||
|
@ -99,9 +104,13 @@ PermissionSettings.prototype = {
|
|||
},
|
||||
|
||||
remove: function remove(aPermName, aManifestURL, aOrigin) {
|
||||
// TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.js
|
||||
let uri = Services.io.newURI(aOrigin, null, null);
|
||||
let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
|
||||
let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri, appID, true);
|
||||
let principal =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(uri,
|
||||
{appId: appID,
|
||||
inBrowser: true});
|
||||
|
||||
if (principal.appStatus !== Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED) {
|
||||
let errorMsg = "PermissionSettings.js: '" + aOrigin + "'" +
|
||||
|
|
|
@ -67,9 +67,13 @@ this.PermissionSettingsModule = {
|
|||
|
||||
|
||||
_internalAddPermission: function _internalAddPermission(aData, aAllowAllChanges, aCallbacks) {
|
||||
// TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.jsm
|
||||
let uri = Services.io.newURI(aData.origin, null, null);
|
||||
let app = appsService.getAppByManifestURL(aData.manifestURL);
|
||||
let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri, app.localId, aData.browserFlag);
|
||||
let principal =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(uri,
|
||||
{appId: app.localId,
|
||||
inBrowser: aData.browserFlag});
|
||||
|
||||
let action;
|
||||
switch (aData.value)
|
||||
|
@ -103,10 +107,14 @@ this.PermissionSettingsModule = {
|
|||
},
|
||||
|
||||
getPermission: function getPermission(aPermName, aManifestURL, aOrigin, aBrowserFlag) {
|
||||
// TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.jsm
|
||||
debug("getPermission: " + aPermName + ", " + aManifestURL + ", " + aOrigin);
|
||||
let uri = Services.io.newURI(aOrigin, null, null);
|
||||
let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
|
||||
let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri, appID, aBrowserFlag);
|
||||
let principal =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(uri,
|
||||
{appId: appID,
|
||||
inBrowser: aBrowserFlag});
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(principal, aPermName);
|
||||
|
||||
switch (result)
|
||||
|
|
|
@ -5288,10 +5288,9 @@ StorageDirectoryHelper::RunOnMainThread()
|
|||
rv = secMan->GetSimpleCodebasePrincipal(uri,
|
||||
getter_AddRefs(principal));
|
||||
} else {
|
||||
rv = secMan->GetAppCodebasePrincipal(uri,
|
||||
originProps.mAppId,
|
||||
originProps.mInMozBrowser,
|
||||
getter_AddRefs(principal));
|
||||
OriginAttributes attrs(originProps.mAppId, originProps.mInMozBrowser);
|
||||
principal = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
|
||||
rv = principal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
|
|
|
@ -27,9 +27,9 @@ function make_uri(url) {
|
|||
// Ensure "fullscreen" permissions are not present on the test URI.
|
||||
var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
|
||||
var uri = make_uri("http://mochi.test:8888");
|
||||
var principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri);
|
||||
var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
var principal = ssm.createCodebasePrincipal(uri, {});
|
||||
pm.removeFromPrincipal(principal, "fullscreen");
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
|
|
|
@ -18,11 +18,11 @@ function startTest()
|
|||
.getService(Components.interfaces.nsIDOMStorageManager);
|
||||
|
||||
var uri = ios.newURI(url, "", null);
|
||||
var principal = ssm.getNoAppCodebasePrincipal(uri);
|
||||
var principal = ssm.createCodebasePrincipal(uri, {});
|
||||
var storage = dsm.createStorage(window, principal, "");
|
||||
|
||||
|
||||
storage.setItem("chromekey", "chromevalue");
|
||||
|
||||
|
||||
var aframe = document.getElementById("aframe");
|
||||
aframe.onload = function()
|
||||
{
|
||||
|
|
|
@ -192,17 +192,54 @@ interface MozInputContext: EventTarget {
|
|||
attribute EventHandler onsurroundingtextchange;
|
||||
|
||||
/*
|
||||
* send a character with its key events.
|
||||
* @param modifiers see http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/base/nsIDOMWindowUtils.idl#206
|
||||
* @param repeat indicates whether a key would be sent repeatedly.
|
||||
* @return true if succeeds. Otherwise false if the input context becomes void.
|
||||
* Alternative: sendKey(KeyboardEvent event), but we will likely
|
||||
* waste memory for creating the KeyboardEvent object.
|
||||
* Note that, if you want to send a key n times repeatedly, make sure set
|
||||
* parameter repeat to true and invoke sendKey n-1 times, and then set
|
||||
* repeat to false in the last invoke.
|
||||
*/
|
||||
Promise<boolean> sendKey(long keyCode, long charCode, long modifiers, optional boolean repeat);
|
||||
* Send a string/character with its key events. There are two ways of invocating
|
||||
* the method for backward compability purpose.
|
||||
*
|
||||
* (1) The recommended way, allow specifying DOM level 3 properties like |code|.
|
||||
* @param dictOrKeyCode See MozInputMethodKeyboardEventDict.
|
||||
* @param charCode disregarded
|
||||
* @param modifiers disregarded
|
||||
* @param repeat disregarded
|
||||
*
|
||||
* (2) Deprecated, reserved for backward compability.
|
||||
* @param dictOrKeyCode keyCode of the key to send, should be one of the DOM_VK_ value in KeyboardEvent.
|
||||
* @param charCode charCode of the character, should be 0 for non-printable keys.
|
||||
* @param modifiers this paramater is no longer honored.
|
||||
* @param repeat indicates whether a key would be sent repeatedly.
|
||||
*
|
||||
* @return A promise. Resolve to true if succeeds.
|
||||
* Rejects to a string indicating the error.
|
||||
*
|
||||
* Note that, if you want to send a key n times repeatedly, make sure set
|
||||
* parameter repeat to true and invoke sendKey n times, and invoke keyup
|
||||
* after the end of the input.
|
||||
*/
|
||||
Promise<boolean> sendKey((MozInputMethodRequiredKeyboardEventDict or long) dictOrKeyCode,
|
||||
optional long charCode,
|
||||
optional long modifiers,
|
||||
optional boolean repeat);
|
||||
|
||||
/*
|
||||
* Send a string/character with keydown, and keypress events.
|
||||
* keyup should be called afterwards to ensure properly sequence.
|
||||
*
|
||||
* @param dict See MozInputMethodKeyboardEventDict.
|
||||
*
|
||||
* @return A promise. Resolve to true if succeeds.
|
||||
* Rejects to a string indicating the error.
|
||||
*/
|
||||
Promise<boolean> keydown(MozInputMethodRequiredKeyboardEventDict dict);
|
||||
|
||||
/*
|
||||
* Send a keyup event. keydown should be called first to ensure properly sequence.
|
||||
*
|
||||
* @param dict See MozInputMethodKeyboardEventDict.
|
||||
*
|
||||
* @return A promise. Resolve to true if succeeds.
|
||||
* Rejects to a string indicating the error.
|
||||
*
|
||||
*/
|
||||
Promise<boolean> keyup(MozInputMethodRequiredKeyboardEventDict dict);
|
||||
|
||||
/*
|
||||
* Set current composing text. This method will start composition or update
|
||||
|
@ -218,7 +255,11 @@ interface MozInputContext: EventTarget {
|
|||
* cursor will be positioned after the composition text.
|
||||
* @param clauses The array of composition clause information. If not set,
|
||||
* only one clause is supported.
|
||||
*
|
||||
* @param dict The properties of the keyboard event that cause the composition
|
||||
* to set. keydown or keyup event will be fired if it's necessary.
|
||||
* For compatibility, we recommend that you should always set this argument
|
||||
* if it's caused by a key operation.
|
||||
*
|
||||
* The composing text, which is shown with underlined style to distinguish
|
||||
* from the existing text, is used to compose non-ASCII characters from
|
||||
* keystrokes, e.g. Pinyin or Hiragana. The composing text is the
|
||||
|
@ -231,9 +272,10 @@ interface MozInputContext: EventTarget {
|
|||
* To finish composition and commit text to current input field, an IME
|
||||
* should call |endComposition|.
|
||||
*/
|
||||
// XXXbz what is this promise resolved with?
|
||||
Promise<any> setComposition(DOMString text, optional long cursor,
|
||||
optional sequence<CompositionClauseParameters> clauses);
|
||||
Promise<boolean> setComposition(DOMString text,
|
||||
optional long cursor,
|
||||
optional sequence<CompositionClauseParameters> clauses,
|
||||
optional MozInputMethodKeyboardEventDict dict);
|
||||
|
||||
/*
|
||||
* End composition, clear the composing text and commit given text to
|
||||
|
@ -241,6 +283,10 @@ interface MozInputContext: EventTarget {
|
|||
* position.
|
||||
* @param text The text to commited before cursor position. If empty string
|
||||
* is given, no text will be committed.
|
||||
* @param dict The properties of the keyboard event that cause the composition
|
||||
* to end. keydown or keyup event will be fired if it's necessary.
|
||||
* For compatibility, we recommend that you should always set this argument
|
||||
* if it's caused by a key operation.
|
||||
*
|
||||
* Note that composition always ends automatically with nothing to commit if
|
||||
* the composition does not explicitly end by calling |endComposition|, but
|
||||
|
@ -248,8 +294,8 @@ interface MozInputContext: EventTarget {
|
|||
* |replaceSurroundingText|, |deleteSurroundingText|, user moving the
|
||||
* cursor, changing the focus, etc.
|
||||
*/
|
||||
// XXXbz what is this promise resolved with?
|
||||
Promise<any> endComposition(optional DOMString text);
|
||||
Promise<boolean> endComposition(optional DOMString text,
|
||||
optional MozInputMethodKeyboardEventDict dict);
|
||||
};
|
||||
|
||||
enum CompositionClauseSelectionType {
|
||||
|
@ -263,3 +309,66 @@ dictionary CompositionClauseParameters {
|
|||
DOMString selectionType = "raw-input";
|
||||
long length;
|
||||
};
|
||||
|
||||
/*
|
||||
* A MozInputMethodKeyboardEventDictBase contains the following properties,
|
||||
* indicating the properties of the keyboard event caused.
|
||||
*
|
||||
* This is the base dictionary type for us to create two child types that could
|
||||
* be used as argument type in two types of methods, as WebIDL parser required.
|
||||
*
|
||||
*/
|
||||
dictionary MozInputMethodKeyboardEventDictBase {
|
||||
/*
|
||||
* String/character to output, or a registered name of non-printable key.
|
||||
* (To be defined in the inheriting dictionary types.)
|
||||
*/
|
||||
// DOMString key;
|
||||
/*
|
||||
* String/char indicating the virtual hardware key pressed. Optional.
|
||||
* Must be a value defined in
|
||||
* http://www.w3.org/TR/DOM-Level-3-Events-code/#keyboard-chording-virtual
|
||||
* If your keyboard app emulates physical keyboard layout, this value should
|
||||
* not be empty string. Otherwise, it should be empty string.
|
||||
*/
|
||||
DOMString code = "";
|
||||
/*
|
||||
* keyCode of the keyboard event. Optional.
|
||||
* To be disregarded if |key| is an alphanumeric character.
|
||||
* If the key causes inputting a character and if your keyboard app emulates
|
||||
* a physical keyboard layout, this value should be same as the value used
|
||||
* by Firefox for desktop. If the key causes inputting an ASCII character
|
||||
* but if your keyboard app doesn't emulate any physical keyboard layouts,
|
||||
* the value should be proper value for the key value.
|
||||
*/
|
||||
long? keyCode;
|
||||
/*
|
||||
* Indicates whether a key would be sent repeatedly. Optional.
|
||||
*/
|
||||
boolean repeat = false;
|
||||
/*
|
||||
* Optional. True if |key| property is explicitly referring to a printable key.
|
||||
* When this is set, key will be printable even if the |key| value matches any
|
||||
* of the registered name of non-printable keys.
|
||||
*/
|
||||
boolean printable = false;
|
||||
};
|
||||
|
||||
/*
|
||||
* For methods like setComposition() and endComposition(), the optional
|
||||
* dictionary type argument is really optional when all of it's property
|
||||
* are optional.
|
||||
* This dictionary type is used to denote that argument.
|
||||
*/
|
||||
dictionary MozInputMethodKeyboardEventDict : MozInputMethodKeyboardEventDictBase {
|
||||
DOMString? key;
|
||||
};
|
||||
|
||||
/*
|
||||
* For methods like keydown() and keyup(), the dictionary type argument is
|
||||
* considered required only if at least one of it's property is required.
|
||||
* This dictionary type is used to denote that argument.
|
||||
*/
|
||||
dictionary MozInputMethodRequiredKeyboardEventDict : MozInputMethodKeyboardEventDictBase {
|
||||
required DOMString key;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
:this file must be enconded in utf8
|
||||
:and its Content-Type must be equal to text/event-stream
|
||||
|
||||
retry:500
|
||||
data: 2
|
||||
unknow: unknow
|
||||
|
||||
event: other_event_name
|
||||
retry:500
|
||||
data: 2
|
||||
unknow: unknow
|
||||
|
||||
event: click
|
||||
retry:500
|
||||
|
||||
event: blur
|
||||
retry:500
|
||||
|
||||
event:keypress
|
||||
retry:500
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
Content-Type: text/event-stream
|
||||
Cache-Control: no-cache, must-revalidate
|
||||
Access-Control-Allow-Origin: *
|
|
@ -0,0 +1,75 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
|
||||
<script type="text/javascript">
|
||||
|
||||
var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/";
|
||||
|
||||
function ok(aCondition, aMessage) {
|
||||
parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
|
||||
}
|
||||
|
||||
function doUnregister() {
|
||||
navigator.serviceWorker.getRegistration().then(swr => {
|
||||
swr.unregister().then(function(result) {
|
||||
ok(result, "Unregister should return true.");
|
||||
parent.postMessage({status: "callback", data: "done"}, "*");
|
||||
}, function(e) {
|
||||
ok(false, "Unregistering the SW failed with " + e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function doEventSource() {
|
||||
var source = new EventSource(prefix + "eventsource.resource");
|
||||
source.onmessage = function(e) {
|
||||
source.onmessage = null;
|
||||
source.close();
|
||||
ok(true, "EventSource should work with cors responses");
|
||||
doUnregister();
|
||||
};
|
||||
source.onerror = function(error) {
|
||||
source.onerror = null;
|
||||
source.close();
|
||||
ok(false, "Something went wrong");
|
||||
};
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
if (!parent) {
|
||||
dump("eventsource/eventsource_cors_response.html shouldn't be launched directly!");
|
||||
}
|
||||
|
||||
window.addEventListener("message", function onMessage(e) {
|
||||
if (e.data.status === "callback") {
|
||||
switch(e.data.data) {
|
||||
case "eventsource":
|
||||
doEventSource();
|
||||
window.removeEventListener("message", onMessage);
|
||||
break;
|
||||
default:
|
||||
ok(false, "Something went wrong")
|
||||
break
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
navigator.serviceWorker.ready.then(function() {
|
||||
parent.postMessage({status: "callback", data: "ready"}, "*");
|
||||
});
|
||||
|
||||
navigator.serviceWorker.addEventListener("message", function(event) {
|
||||
parent.postMessage(event.data, "*");
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="onLoad()">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
// Cross origin request
|
||||
var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/';
|
||||
|
||||
self.importScripts('eventsource_worker_helper.js');
|
||||
|
||||
self.addEventListener('fetch', function (event) {
|
||||
var request = event.request;
|
||||
var url = new URL(request.url);
|
||||
|
||||
if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') {
|
||||
return;
|
||||
}
|
||||
|
||||
ok(request.mode === 'cors', 'EventSource should make a CORS request');
|
||||
var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'cors'});
|
||||
event.respondWith(fetch(fetchRequest).then((fetchResponse) => {
|
||||
return fetchResponse;
|
||||
}));
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
|
||||
<script type="text/javascript">
|
||||
|
||||
var prefix = "https://example.com/tests/dom/workers/test/serviceworkers/eventsource/";
|
||||
|
||||
function ok(aCondition, aMessage) {
|
||||
parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
|
||||
}
|
||||
|
||||
function doUnregister() {
|
||||
navigator.serviceWorker.getRegistration().then(swr => {
|
||||
swr.unregister().then(function(result) {
|
||||
ok(result, "Unregister should return true.");
|
||||
parent.postMessage({status: "callback", data: "done"}, "*");
|
||||
}, function(e) {
|
||||
ok(false, "Unregistering the SW failed with " + e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function doEventSource() {
|
||||
var source = new EventSource(prefix + "eventsource.resource");
|
||||
source.onmessage = function(e) {
|
||||
source.onmessage = null;
|
||||
source.close();
|
||||
ok(false, "Something went wrong");
|
||||
};
|
||||
source.onerror = function(error) {
|
||||
source.onerror = null;
|
||||
source.close();
|
||||
ok(true, "EventSource should not work with mixed content cors responses");
|
||||
doUnregister();
|
||||
};
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
if (!parent) {
|
||||
dump("eventsource/eventsource_cors_response.html shouldn't be launched directly!");
|
||||
}
|
||||
|
||||
window.addEventListener("message", function onMessage(e) {
|
||||
if (e.data.status === "callback") {
|
||||
switch(e.data.data) {
|
||||
case "eventsource":
|
||||
doEventSource();
|
||||
window.removeEventListener("message", onMessage);
|
||||
break;
|
||||
default:
|
||||
ok(false, "Something went wrong")
|
||||
break
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
navigator.serviceWorker.ready.then(function() {
|
||||
parent.postMessage({status: "callback", data: "ready"}, "*");
|
||||
});
|
||||
|
||||
navigator.serviceWorker.addEventListener("message", function(event) {
|
||||
parent.postMessage(event.data, "*");
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="onLoad()">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/';
|
||||
|
||||
self.importScripts('eventsource_worker_helper.js');
|
||||
|
||||
self.addEventListener('fetch', function (event) {
|
||||
var request = event.request;
|
||||
var url = new URL(request.url);
|
||||
|
||||
if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') {
|
||||
return;
|
||||
}
|
||||
|
||||
ok(request.mode === 'cors', 'EventSource should make a CORS request');
|
||||
var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'cors'});
|
||||
event.respondWith(fetch(fetchRequest).then((fetchResponse) => {
|
||||
return fetchResponse;
|
||||
}));
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
|
||||
<script type="text/javascript">
|
||||
|
||||
var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/";
|
||||
|
||||
function ok(aCondition, aMessage) {
|
||||
parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
|
||||
}
|
||||
|
||||
function doUnregister() {
|
||||
navigator.serviceWorker.getRegistration().then(swr => {
|
||||
swr.unregister().then(function(result) {
|
||||
ok(result, "Unregister should return true.");
|
||||
parent.postMessage({status: "callback", data: "done"}, "*");
|
||||
}, function(e) {
|
||||
ok(false, "Unregistering the SW failed with " + e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function doEventSource() {
|
||||
var source = new EventSource(prefix + "eventsource.resource");
|
||||
source.onmessage = function(e) {
|
||||
source.onmessage = null;
|
||||
source.close();
|
||||
ok(false, "Something went wrong");
|
||||
};
|
||||
source.onerror = function(error) {
|
||||
source.onerror = null;
|
||||
source.close();
|
||||
ok(true, "EventSource should not work with opaque responses");
|
||||
doUnregister();
|
||||
};
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
if (!parent) {
|
||||
dump("eventsource/eventsource_opaque_response.html shouldn't be launched directly!");
|
||||
}
|
||||
|
||||
window.addEventListener("message", function onMessage(e) {
|
||||
if (e.data.status === "callback") {
|
||||
switch(e.data.data) {
|
||||
case "eventsource":
|
||||
doEventSource();
|
||||
window.removeEventListener("message", onMessage);
|
||||
break;
|
||||
default:
|
||||
ok(false, "Something went wrong")
|
||||
break
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
navigator.serviceWorker.ready.then(function() {
|
||||
parent.postMessage({status: "callback", data: "ready"}, "*");
|
||||
});
|
||||
|
||||
navigator.serviceWorker.addEventListener("message", function(event) {
|
||||
parent.postMessage(event.data, "*");
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="onLoad()">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
// Cross origin request
|
||||
var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/';
|
||||
|
||||
self.importScripts('eventsource_worker_helper.js');
|
||||
|
||||
self.addEventListener('fetch', function (event) {
|
||||
var request = event.request;
|
||||
var url = new URL(request.url);
|
||||
|
||||
if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') {
|
||||
return;
|
||||
}
|
||||
|
||||
ok(request.mode === 'cors', 'EventSource should make a CORS request');
|
||||
var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'no-cors'});
|
||||
event.respondWith(fetch(fetchRequest).then((fetchResponse) => {
|
||||
return fetchResponse;
|
||||
}));
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
|
||||
<script type="text/javascript">
|
||||
|
||||
function getURLParam (aTarget, aValue) {
|
||||
return decodeURI(aTarget.search.replace(new RegExp("^(?:.*[&\\?]" + encodeURI(aValue).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1"));
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
navigator.serviceWorker.ready.then(function() {
|
||||
parent.postMessage({status: "callback", data: "done"}, "*");
|
||||
});
|
||||
|
||||
navigator.serviceWorker.register(getURLParam(document.location, "script"), {scope: "."});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="onLoad()">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,75 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
|
||||
<script type="text/javascript">
|
||||
|
||||
var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/";
|
||||
|
||||
function ok(aCondition, aMessage) {
|
||||
parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
|
||||
}
|
||||
|
||||
function doUnregister() {
|
||||
navigator.serviceWorker.getRegistration().then(swr => {
|
||||
swr.unregister().then(function(result) {
|
||||
ok(result, "Unregister should return true.");
|
||||
parent.postMessage({status: "callback", data: "done"}, "*");
|
||||
}, function(e) {
|
||||
ok(false, "Unregistering the SW failed with " + e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function doEventSource() {
|
||||
var source = new EventSource(prefix + "eventsource.resource");
|
||||
source.onmessage = function(e) {
|
||||
source.onmessage = null;
|
||||
source.close();
|
||||
ok(true, "EventSource should work with synthetic responses");
|
||||
doUnregister();
|
||||
};
|
||||
source.onerror = function(error) {
|
||||
source.onmessage = null;
|
||||
source.close();
|
||||
ok(false, "Something went wrong");
|
||||
};
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
if (!parent) {
|
||||
dump("eventsource/eventsource_synthetic_response.html shouldn't be launched directly!");
|
||||
}
|
||||
|
||||
window.addEventListener("message", function onMessage(e) {
|
||||
if (e.data.status === "callback") {
|
||||
switch(e.data.data) {
|
||||
case "eventsource":
|
||||
doEventSource();
|
||||
window.removeEventListener("message", onMessage);
|
||||
break;
|
||||
default:
|
||||
ok(false, "Something went wrong")
|
||||
break
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
navigator.serviceWorker.ready.then(function() {
|
||||
parent.postMessage({status: "callback", data: "ready"}, "*");
|
||||
});
|
||||
|
||||
navigator.serviceWorker.addEventListener("message", function(event) {
|
||||
parent.postMessage(event.data, "*");
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="onLoad()">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,24 @@
|
|||
self.importScripts('eventsource_worker_helper.js');
|
||||
|
||||
self.addEventListener('fetch', function (event) {
|
||||
var request = event.request;
|
||||
var url = new URL(request.url);
|
||||
|
||||
if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') {
|
||||
return;
|
||||
}
|
||||
|
||||
ok(request.mode === 'cors', 'EventSource should make a CORS request');
|
||||
var headerList = {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache, must-revalidate'
|
||||
};
|
||||
var headers = new Headers(headerList);
|
||||
var init = {
|
||||
headers: headers,
|
||||
mode: 'cors'
|
||||
};
|
||||
var body = 'data: data0\r\r';
|
||||
var response = new Response(body, init);
|
||||
event.respondWith(response);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
function ok(aCondition, aMessage) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
self.clients.matchAll().then(function(res) {
|
||||
if (!res.length) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
res[0].postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<script>
|
||||
var obj, embed;
|
||||
|
||||
function ok(v, msg) {
|
||||
window.opener.postMessage({status: "ok", result: !!v, message: msg}, "*");
|
||||
}
|
||||
|
||||
function finish() {
|
||||
document.documentElement.removeChild(obj);
|
||||
document.documentElement.removeChild(embed);
|
||||
window.opener.postMessage({status: "done"}, "*");
|
||||
}
|
||||
|
||||
function test_object() {
|
||||
obj = document.createElement("object");
|
||||
obj.setAttribute('data', "object");
|
||||
document.documentElement.appendChild(obj);
|
||||
}
|
||||
|
||||
function test_embed() {
|
||||
embed = document.createElement("embed");
|
||||
embed.setAttribute('src', "embed");
|
||||
document.documentElement.appendChild(embed);
|
||||
}
|
||||
|
||||
navigator.serviceWorker.addEventListener("message", function onMessage(e) {
|
||||
if (e.data.context === "object") {
|
||||
ok(false, "<object> should not be intercepted");
|
||||
} else if (e.data.context === "embed") {
|
||||
ok(false, "<embed> should not be intercepted");
|
||||
} else if (e.data.context === "fetch" && e.data.resource === "foo.txt") {
|
||||
navigator.serviceWorker.removeEventListener("message", onMessage);
|
||||
finish();
|
||||
}
|
||||
}, false);
|
||||
|
||||
test_object();
|
||||
test_embed();
|
||||
// SW will definitely intercept fetch API, use this to see if plugins are
|
||||
// intercepted before fetch().
|
||||
fetch("foo.txt");
|
||||
</script>
|
|
@ -0,0 +1,7 @@
|
|||
self.addEventListener("fetch", function(event) {
|
||||
var resource = event.request.url.split('/').pop();
|
||||
if (event.client) {
|
||||
event.client.postMessage({context: event.request.context,
|
||||
resource: resource});
|
||||
}
|
||||
});
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче